/**
 * LogManager Tests
 *
 * Tests for the log directory and file path management.
 * Uses temporary directories to avoid polluting the real log directory.
 */

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdir, rm, writeFile, utimes } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { LogManager } from './manager.js';

describe('LogManager', () => {
  let testDir: string;
  let manager: LogManager;

  beforeEach(async () => {
    // Create a unique temp directory for each test
    testDir = join(tmpdir(), `cw-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
    manager = new LogManager({ baseDir: testDir });
  });

  afterEach(async () => {
    // Clean up temp directory after each test
    try {
      await rm(testDir, { recursive: true, force: true });
    } catch {
      // Ignore cleanup errors
    }
  });

  describe('getBaseDir', () => {
    it('should return the configured base directory', () => {
      expect(manager.getBaseDir()).toBe(testDir);
    });

    it('should use default directory when not configured', () => {
      const defaultManager = new LogManager();
      expect(defaultManager.getBaseDir()).toContain('.cw');
      expect(defaultManager.getBaseDir()).toContain('logs');
    });
  });

  describe('ensureLogDir', () => {
    it('should create the base log directory', async () => {
      expect(existsSync(testDir)).toBe(false);

      await manager.ensureLogDir();

      expect(existsSync(testDir)).toBe(true);
    });

    it('should not error if directory already exists', async () => {
      await mkdir(testDir, { recursive: true });
      expect(existsSync(testDir)).toBe(true);

      // Should not throw
      await manager.ensureLogDir();

      expect(existsSync(testDir)).toBe(true);
    });
  });

  describe('ensureProcessDir', () => {
    it('should create the process-specific log directory', async () => {
      const processId = 'test-process-123';
      const expectedDir = join(testDir, processId);

      expect(existsSync(expectedDir)).toBe(false);

      await manager.ensureProcessDir(processId);

      expect(existsSync(expectedDir)).toBe(true);
    });

    it('should create nested directories if base does not exist', async () => {
      const processId = 'nested-process';
      const expectedDir = join(testDir, processId);

      expect(existsSync(testDir)).toBe(false);

      await manager.ensureProcessDir(processId);

      expect(existsSync(testDir)).toBe(true);
      expect(existsSync(expectedDir)).toBe(true);
    });
  });

  describe('getProcessDir', () => {
    it('should return the correct path for a process', () => {
      const processId = 'my-process';
      const expected = join(testDir, processId);

      expect(manager.getProcessDir(processId)).toBe(expected);
    });
  });

  describe('getLogPath', () => {
    it('should return correct path for stdout log', () => {
      const processId = 'proc-1';
      const expected = join(testDir, processId, 'stdout.log');

      expect(manager.getLogPath(processId, 'stdout')).toBe(expected);
    });

    it('should return correct path for stderr log', () => {
      const processId = 'proc-2';
      const expected = join(testDir, processId, 'stderr.log');

      expect(manager.getLogPath(processId, 'stderr')).toBe(expected);
    });
  });

  describe('listLogs', () => {
    it('should return empty array if base directory does not exist', async () => {
      expect(existsSync(testDir)).toBe(false);

      const logs = await manager.listLogs();

      expect(logs).toEqual([]);
    });

    it('should return empty array if no log directories exist', async () => {
      await mkdir(testDir, { recursive: true });

      const logs = await manager.listLogs();

      expect(logs).toEqual([]);
    });

    it('should return process IDs for existing log directories', async () => {
      // Create some process directories
      await mkdir(join(testDir, 'process-a'), { recursive: true });
      await mkdir(join(testDir, 'process-b'), { recursive: true });
      await mkdir(join(testDir, 'process-c'), { recursive: true });

      const logs = await manager.listLogs();

      expect(logs).toHaveLength(3);
      expect(logs).toContain('process-a');
      expect(logs).toContain('process-b');
      expect(logs).toContain('process-c');
    });

    it('should only return directories, not files', async () => {
      await mkdir(testDir, { recursive: true });
      await mkdir(join(testDir, 'valid-process'), { recursive: true });
      await writeFile(join(testDir, 'some-file.txt'), 'not a directory');

      const logs = await manager.listLogs();

      expect(logs).toEqual(['valid-process']);
    });
  });

  describe('cleanOldLogs', () => {
    it('should return 0 when retainDays is not configured', async () => {
      const managerNoRetain = new LogManager({ baseDir: testDir });

      const removed = await managerNoRetain.cleanOldLogs();

      expect(removed).toBe(0);
    });

    it('should return 0 when no directories exist', async () => {
      const managerWithRetain = new LogManager({ baseDir: testDir, retainDays: 7 });

      const removed = await managerWithRetain.cleanOldLogs();

      expect(removed).toBe(0);
    });

    it('should remove directories older than retainDays', async () => {
      const managerWithRetain = new LogManager({ baseDir: testDir, retainDays: 7 });

      // Create an "old" directory
      const oldDir = join(testDir, 'old-process');
      await mkdir(oldDir, { recursive: true });

      // Set mtime to 10 days ago
      const tenDaysAgo = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000);
      await utimes(oldDir, tenDaysAgo, tenDaysAgo);

      // Create a "new" directory
      const newDir = join(testDir, 'new-process');
      await mkdir(newDir, { recursive: true });

      const removed = await managerWithRetain.cleanOldLogs();

      expect(removed).toBe(1);
      expect(existsSync(oldDir)).toBe(false);
      expect(existsSync(newDir)).toBe(true);
    });

    it('should use provided retainDays over config value', async () => {
      const managerWithRetain = new LogManager({ baseDir: testDir, retainDays: 30 });

      // Create directory that is 10 days old
      const oldDir = join(testDir, 'process');
      await mkdir(oldDir, { recursive: true });
      const tenDaysAgo = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000);
      await utimes(oldDir, tenDaysAgo, tenDaysAgo);

      // With config (30 days), should NOT remove
      expect(await managerWithRetain.cleanOldLogs()).toBe(0);
      expect(existsSync(oldDir)).toBe(true);

      // With explicit 5 days, SHOULD remove
      expect(await managerWithRetain.cleanOldLogs(5)).toBe(1);
      expect(existsSync(oldDir)).toBe(false);
    });
  });
});
