import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import * as os from 'node:os';
import { DailyLogWriter, DailyLogReader } from '@zyphon/logger';
import type { LogMessageRecord } from '@zyphon/shared-types';

describe('DailyLogWriter and DailyLogReader', () => {
  let tmpDir: string;

  beforeEach(async () => {
    tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'zyphon-log-test-'));
  });

  afterEach(async () => {
    await fs.rm(tmpDir, { recursive: true, force: true });
  });

  it('writes records into daily log file and reads them back', async () => {
    const fixedDate = new Date('2026-10-09T12:00:00Z');
    const writer = new DailyLogWriter({
      logsDir: tmpDir,
      clock: () => fixedDate,
    });

    const record: LogMessageRecord = {
      type: 'message',
      message_id: 'msg_001',
      request_id: 'req_001',
      ts: fixedDate.getTime(),
      application: 'app_test',
      channel: 'email',
      provider: 'smtp-primary',
      status: 'delivered',
      latency_ms: 120,
      attempts: [],
      provider_response: { ok: true },
      error: null,
      recipient_masked: 'u***@example.com',
      failover: false,
    };

    writer.write(record);

    const reader = new DailyLogReader(tmpDir);
    const result = await reader.query({ application: 'app_test' });

    expect(result.records.length).toBe(1);
    expect((result.records[0] as LogMessageRecord).message_id).toBe('msg_001');
  });

  it('prunes older log files outside the retention window', async () => {
    let currentDayIndex = 1;
    const writer = new DailyLogWriter({
      logsDir: tmpDir,
      retentionDays: 3,
      clock: () => new Date(`2026-10-0${currentDayIndex}T10:00:00Z`),
    });

    // Write logs across 5 days
    for (let day = 1; day <= 5; day++) {
      currentDayIndex = day;
      writer.write({
        type: 'message',
        message_id: `msg_day_${day}`,
        request_id: `req_${day}`,
        ts: Date.now(),
        application: 'app_test',
        channel: 'email',
        provider: 'smtp',
        status: 'delivered',
        latency_ms: 50,
        attempts: [],
        provider_response: null,
        error: null,
        recipient_masked: 'test@example.com',
        failover: false,
      });
    }

    const filesBeforePrune = await fs.readdir(tmpDir);
    expect(filesBeforePrune.filter((f) => f.endsWith('.log')).length).toBe(5);

    const deleted = writer.prune(3);
    expect(deleted.length).toBe(2);

    const filesAfterPrune = await fs.readdir(tmpDir);
    expect(filesAfterPrune.filter((f) => f.endsWith('.log')).length).toBe(3);
  });

  it('prunes 9 files down to 7 and applies setRetentionDays without restart', async () => {
    const days = [
      '2026-10-01',
      '2026-10-02',
      '2026-10-03',
      '2026-10-04',
      '2026-10-05',
      '2026-10-06',
      '2026-10-07',
      '2026-10-08',
      '2026-10-09',
    ];
    for (const day of days) {
      await fs.writeFile(path.join(tmpDir, `zyphon-${day}.log`), '{"type":"audit"}\n');
    }

    const writer = new DailyLogWriter({
      logsDir: tmpDir,
      retentionDays: 9,
      clock: () => new Date('2026-10-09T12:00:00Z'),
    });

    expect(writer.prune()).toHaveLength(0);
    writer.setRetentionDays(7);
    expect(writer.getRetentionDays()).toBe(7);
    const deleted = writer.prune();
    expect(deleted.length).toBe(2);
    const remaining = (await fs.readdir(tmpDir)).filter((f) => f.endsWith('.log')).sort();
    expect(remaining).toEqual([
      'zyphon-2026-10-03.log',
      'zyphon-2026-10-04.log',
      'zyphon-2026-10-05.log',
      'zyphon-2026-10-06.log',
      'zyphon-2026-10-07.log',
      'zyphon-2026-10-08.log',
      'zyphon-2026-10-09.log',
    ]);
  });
});

