import { describe, expect, it } from 'vitest';
import { LogFileBackend, LogFileInfo, LogLevel, createLogger } from '../src/utils/logger';

class MemoryLogBackend implements LogFileBackend {
  public files: Map<string, string> = new Map();

  async appendLog(dateStr: string, logLine: string): Promise<void> {
    const existing = this.files.get(dateStr) ?? '';
    this.files.set(dateStr, existing + logLine);
  }

  async readLogFile(dateStr: string): Promise<string> {
    return this.files.get(dateStr) ?? '';
  }

  async listLogFiles(): Promise<LogFileInfo[]> {
    const res: LogFileInfo[] = [];
    for (const [date, content] of this.files.entries()) {
      res.push({
        name: `app-${date}.log`,
        path: `/mock/logs/app-${date}.log`,
        size: Buffer.byteLength(content, 'utf8'),
        date,
      });
    }
    return res.sort((a, b) => b.date.localeCompare(a.date));
  }

  async deleteLogFile(dateStr: string): Promise<void> {
    this.files.delete(dateStr);
  }

  async clearAllLogFiles(): Promise<void> {
    this.files.clear();
  }
}

describe('LoggerImpl 持久化与高级功能测试', () => {
  it('应当能正确初始化 LogFileBackend 并将日志追加写入内存文件', async () => {
    const logger = createLogger();
    logger.consoleOutput = false;
    const backend = new MemoryLogBackend();

    await logger.initFileBackend(backend);
    logger.info('testTag', '测试消息1', { foo: 'bar' });
    logger.error('testTag', '崩溃报错消息');

    await logger.flushQueue();

    const todayStr = new Date().toISOString().slice(0, 10);
    const fileContent = await backend.readLogFile(todayStr);
    expect(fileContent).toContain('测试消息1');
    expect(fileContent).toContain('崩溃报错消息');
    expect(fileContent).toContain('"foo":"bar"');
  });

  it('应当能正确按日期读取文件中的历史日志条目', async () => {
    const logger = createLogger();
    logger.consoleOutput = false;
    const backend = new MemoryLogBackend();

    const historicalDate = '2026-07-20';
    const logLine1 = JSON.stringify({
      timestamp: '2026-07-20T10:00:00.000Z',
      level: LogLevel.WARN,
      tag: 'network',
      message: '网络超时',
    });
    const logLine2 = JSON.stringify({
      timestamp: '2026-07-20T10:05:00.000Z',
      level: LogLevel.ERROR,
      tag: 'db',
      message: '数据库锁死',
    });
    await backend.appendLog(historicalDate, `${logLine1}\n${logLine2}\n`);

    await logger.initFileBackend(backend);
    const loadedLogs = await logger.loadLogsFromFile(historicalDate);

    expect(loadedLogs).toHaveLength(2);
    expect(loadedLogs[0].tag).toBe('network');
    expect(loadedLogs[0].level).toBe(LogLevel.WARN);
    expect(loadedLogs[1].message).toBe('数据库锁死');
  });

  it('应当能自动清理超过指定保留天数（如 7 天）的历史日志文件', async () => {
    const logger = createLogger();
    logger.consoleOutput = false;
    const backend = new MemoryLogBackend();

    // 假设今天 2026-07-22，注入 10 天前与 2 天前的日志文件
    await backend.appendLog('2026-07-01', 'old logs\n'); // 超过 7 天
    await backend.appendLog('2026-07-21', 'recent logs\n'); // 保留

    await logger.initFileBackend(backend, { maxDays: 7 });

    const files = await logger.getLogFiles();
    const dates = files.map(f => f.date);

    expect(dates).not.toContain('2026-07-01');
    expect(dates).toContain('2026-07-21');
  });

  it('应当能格式化导出人类可读文本字符串', () => {
    const logger = createLogger();
    logger.consoleOutput = false;
    logger.info('billPipeline', '接收到微信账单', { amount: 15.5 });
    logger.warn('dedup', '发现重复交易', { id: 'tx-123' });

    const exportText = logger.exportAsFormattedText();
    expect(exportText).toContain('[INFO] [billPipeline] 接收到微信账单');
    expect(exportText).toContain('[WARN] [dedup] 发现重复交易');
    expect(exportText).toContain('"amount":15.5');
  });

  it('query 方法应当支持按关键词过滤日志', () => {
    const logger = createLogger();
    logger.consoleOutput = false;
    logger.info('tagA', '普通记账事件');
    logger.error('tagB', '支付接口网络超时', { status: 504 });

    const result = logger.query({ keyword: '超时' });
    expect(result).toHaveLength(1);
    expect(result[0].tag).toBe('tagB');
  });

  it('clearAllLogs 应当同时清空内存与磁盘中的全部日志', async () => {
    const logger = createLogger();
    logger.consoleOutput = false;
    const backend = new MemoryLogBackend();
    await logger.initFileBackend(backend);

    logger.info('tag', '一条日志');
    await logger.flushQueue();

    expect(logger.getLogs()).toHaveLength(1);
    expect((await backend.listLogFiles()).length).toBeGreaterThan(0);

    await logger.clearAllLogs();

    expect(logger.getLogs()).toHaveLength(0);
    expect((await backend.listLogFiles()).length).toBe(0);
  });

  it('getTodayLogs 应当能正确合并磁盘上的历史 session 日志与当前内存中的新日志（自动去重）', async () => {
    const logger = createLogger();
    logger.consoleOutput = false;
    const backend = new MemoryLogBackend();
    await logger.initFileBackend(backend);

    const todayStr = new Date().toISOString().slice(0, 10);
    const earlierSessionLog = JSON.stringify({
      timestamp: `${todayStr}T08:00:00.000Z`,
      level: LogLevel.INFO,
      tag: 'layout',
      message: '早前 Session 启动日志',
    });
    await backend.appendLog(todayStr, `${earlierSessionLog}\n`);

    // 当前 Session 新记录的内存日志
    logger.info('billPipeline', '新 Session 产生的日志');

    const todayLogs = await logger.getTodayLogs();
    expect(todayLogs.some(e => e.message === '早前 Session 启动日志')).toBe(true);
    expect(todayLogs.some(e => e.message === '新 Session 产生的日志')).toBe(true);
  });
});
