/**
 * SignalManager Tests — Verify atomic signal.json operations.
 */

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

describe('FileSystemSignalManager', () => {
  let tempDir: string;
  let agentWorkdir: string;
  let signalManager: FileSystemSignalManager;

  beforeEach(async () => {
    tempDir = await mkdtemp(join(tmpdir(), 'signal-manager-test-'));
    agentWorkdir = join(tempDir, 'agent-workdir');
    await mkdir(join(agentWorkdir, '.cw', 'output'), { recursive: true });
    signalManager = new FileSystemSignalManager();
  });

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

  describe('clearSignal', () => {
    it('should remove existing signal.json file', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      await writeFile(signalPath, JSON.stringify({ status: 'done' }));

      await signalManager.clearSignal(agentWorkdir);

      const exists = await signalManager.checkSignalExists(agentWorkdir);
      expect(exists).toBe(false);
    });

    it('should not throw if signal.json does not exist', async () => {
      await expect(signalManager.clearSignal(agentWorkdir)).resolves.not.toThrow();
    });
  });

  describe('checkSignalExists', () => {
    it('should return true when signal.json exists', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      await writeFile(signalPath, JSON.stringify({ status: 'done' }));

      const exists = await signalManager.checkSignalExists(agentWorkdir);
      expect(exists).toBe(true);
    });

    it('should return false when signal.json does not exist', async () => {
      const exists = await signalManager.checkSignalExists(agentWorkdir);
      expect(exists).toBe(false);
    });
  });

  describe('readSignal', () => {
    it('should read valid done signal', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      const expectedSignal = { status: 'done' };
      await writeFile(signalPath, JSON.stringify(expectedSignal));

      const signal = await signalManager.readSignal(agentWorkdir);
      expect(signal).toEqual(expectedSignal);
    });

    it('should read valid questions signal', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      const expectedSignal = {
        status: 'questions',
        questions: [{ id: '1', question: 'What to do?' }]
      };
      await writeFile(signalPath, JSON.stringify(expectedSignal));

      const signal = await signalManager.readSignal(agentWorkdir);
      expect(signal).toEqual(expectedSignal);
    });

    it('should read valid error signal', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      const expectedSignal = { status: 'error', error: 'Something went wrong' };
      await writeFile(signalPath, JSON.stringify(expectedSignal));

      const signal = await signalManager.readSignal(agentWorkdir);
      expect(signal).toEqual(expectedSignal);
    });

    it('should return null for invalid JSON', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      await writeFile(signalPath, '{ invalid json');

      const signal = await signalManager.readSignal(agentWorkdir);
      expect(signal).toBeNull();
    });

    it('should return null for invalid status', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      await writeFile(signalPath, JSON.stringify({ status: 'invalid' }));

      const signal = await signalManager.readSignal(agentWorkdir);
      expect(signal).toBeNull();
    });

    it('should return null for empty file', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      await writeFile(signalPath, '');

      const signal = await signalManager.readSignal(agentWorkdir);
      expect(signal).toBeNull();
    });

    it('should return null when file does not exist', async () => {
      const signal = await signalManager.readSignal(agentWorkdir);
      expect(signal).toBeNull();
    });
  });

  describe('waitForSignal', () => {
    it('should return signal when file already exists', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      const expectedSignal = { status: 'done' };
      await writeFile(signalPath, JSON.stringify(expectedSignal));

      const signal = await signalManager.waitForSignal(agentWorkdir, 1000);
      expect(signal).toEqual(expectedSignal);
    });

    it('should wait for signal to appear', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      const expectedSignal = { status: 'done' };

      // Write signal after a delay
      setTimeout(async () => {
        await writeFile(signalPath, JSON.stringify(expectedSignal));
      }, 100);

      const signal = await signalManager.waitForSignal(agentWorkdir, 1000);
      expect(signal).toEqual(expectedSignal);
    });

    it('should timeout if signal never appears', async () => {
      const signal = await signalManager.waitForSignal(agentWorkdir, 100);
      expect(signal).toBeNull();
    });
  });

  describe('validateSignalFile', () => {
    it('should return true for complete valid JSON file', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      await writeFile(signalPath, JSON.stringify({ status: 'done' }));

      const isValid = await signalManager.validateSignalFile(signalPath);
      expect(isValid).toBe(true);
    });

    it('should return false for incomplete JSON', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      await writeFile(signalPath, '{ "status": "don');

      const isValid = await signalManager.validateSignalFile(signalPath);
      expect(isValid).toBe(false);
    });

    it('should return false for empty file', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');
      await writeFile(signalPath, '');

      const isValid = await signalManager.validateSignalFile(signalPath);
      expect(isValid).toBe(false);
    });

    it('should return false when file does not exist', async () => {
      const signalPath = join(agentWorkdir, '.cw/output/signal.json');

      const isValid = await signalManager.validateSignalFile(signalPath);
      expect(isValid).toBe(false);
    });
  });
});