import { describe, it, expect, beforeEach } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
import { SequentialOrchestrator } from '../services/SequentialOrchestrator.js';
import { getRecentPhaseProgress, type PhaseProgressLogEntry } from '../services/PhaseProgressLogger.js';

const tempCheckpoint = path.join(process.cwd(), 'data/test-orch-checkpoint-progress.json');
const tempLog = path.join(process.cwd(), 'status/phase-progress.jsonl');

async function cleanFiles() {
  try { await fs.unlink(tempCheckpoint); } catch {}
  try { await fs.unlink(tempLog); } catch {}
}

describe('Phase Progress Logging', () => {
  beforeEach(async () => {
    await cleanFiles();
  });

  it('logs success and failure with next action and reason', async () => {
    let tries = 0;
    const orchestrator = new SequentialOrchestrator({
      phases: [
        { name: 'a', run: async () => {} },
        { name: 'fail1', run: async () => { tries++; if (tries < 2) throw new Error('boom'); }, retry: 2 },
        { name: 'b', run: async () => {} },
      ],
      checkpointPath: tempCheckpoint,
      input: undefined,
    });
    await orchestrator.run();
    const entries = await getRecentPhaseProgress(10);
    // There should be at least fail1 failure, then success, and at least one other phase
    expect(entries.some((e: PhaseProgressLogEntry) => e.phase==='fail1' && e.status==='failure')).toBe(true);
    expect(entries.some((e: PhaseProgressLogEntry) => e.phase==='fail1' && e.status==='success')).toBe(true);
    const failure = entries.find((e: PhaseProgressLogEntry) => e.phase==='fail1' && e.status==='failure');
    expect(failure).toBeDefined();
    expect(failure!.failureReason).toBe('boom');
    expect(['retry','manual intervention']).toContain(failure!.nextAction);
    const success = entries.find((e: PhaseProgressLogEntry) => e.phase==='fail1' && e.status==='success');
    expect(success).toBeDefined();
    expect(success!.nextAction).toBe('proceed');
  });

  it('logs nextAction as manual intervention after final failure', async () => {
    const orchestrator = new SequentialOrchestrator({
      phases: [
        { name: 'a', run: async () => {} },
        { name: 'fail-all', run: async () => { throw new Error('nope'); }, retry: 2 },
        { name: 'b', run: async () => {} },
      ],
      checkpointPath: tempCheckpoint,
      input: undefined,
      maxAttemptsPerPhasePerRun: 2,
    });
    await orchestrator.run();
    const entries = await getRecentPhaseProgress(10);
    const fails = entries.filter((e: PhaseProgressLogEntry) => e.phase==='fail-all');
    expect(fails.length).toBe(2);
    expect(fails.every((e: PhaseProgressLogEntry) => e.status==='failure')).toBe(true);
    expect(fails[1].nextAction).toBe('manual intervention');
  });

  it('helper returns [] if log does not exist', async () => {
    await fs.unlink(tempLog).catch(() => {});
    const entries = await getRecentPhaseProgress(5);
    expect(entries).toEqual([]);
  });
});
