/**
 * Crash Recovery Integration Tests
 *
 * IMPORTANT: These tests call the REAL Claude CLI and incur API costs!
 * They are SKIPPED by default and should only be run manually for validation.
 *
 * To run these tests:
 * ```bash
 * REAL_CLAUDE_TESTS=1 npm test -- src/test/integration/real-providers/crash-recovery.test.ts --test-timeout=300000
 * ```
 *
 * Tests covered:
 * - Server restart while agent is running
 * - Resuming streaming after restart
 * - Marking dead agents as crashed
 * - Output file processing after restart
 *
 * Estimated cost: ~$0.08 per full run
 */

import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import {
  createRealProviderHarness,
  describeRealClaude,
  REAL_TEST_TIMEOUT,
  EXTENDED_TEST_TIMEOUT,
  sleep,
  type RealProviderHarness,
} from './harness.js';
import { MINIMAL_PROMPTS } from './prompts.js';
import { MultiProviderAgentManager } from '../../../agent/manager.js';

describeRealClaude('Crash Recovery', () => {
  let harness: RealProviderHarness;

  beforeAll(async () => {
    console.log('\n=== Running Crash Recovery Tests ===');
    console.log('These tests call the real Claude API and incur costs.\n');
    harness = await createRealProviderHarness({ provider: 'claude' });
  });

  afterAll(async () => {
    await harness.cleanup();
  });

  beforeEach(() => {
    harness.clearEvents();
  });

  describe('Server Restart Simulation', () => {
    it(
      'resumes streaming for still-running agent after restart',
      async () => {
        // 1. Spawn agent with slow task
        console.log('  1. Spawning agent with slow task...');
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.slow,
          mode: 'execute',
          provider: 'claude',
        });

        // 2. Wait for agent to be running
        await harness.waitForAgentStatus(agent.id, 'running', 10000);
        const dbAgent = await harness.agentRepository.findById(agent.id);
        expect(dbAgent?.pid).toBeTruthy();
        expect(dbAgent?.outputFilePath).toBeTruthy();
        console.log('  2. Agent running with PID:', dbAgent?.pid);

        // 3. Give the agent a moment to start writing output
        await sleep(2000);

        // 4. Simulate server crash - create NEW manager (old state lost)
        console.log('  3. Simulating server restart with new manager...');
        harness.clearEvents(); // Clear events from old manager

        const newManager = new MultiProviderAgentManager(
          harness.agentRepository,
          harness.workspaceRoot,
          harness.projectRepository,
          harness.accountRepository,
          harness.eventBus
        );

        // 5. Reconcile - should pick up running agent
        console.log('  4. Reconciling agent state...');
        await newManager.reconcileAfterRestart();

        // 6. Wait for completion via new manager
        console.log('  5. Waiting for completion via new manager...');
        let attempts = 0;
        let finalStatus = 'running';
        while (attempts < 60) {
          const refreshed = await harness.agentRepository.findById(agent.id);
          if (refreshed?.status !== 'running') {
            finalStatus = refreshed?.status ?? 'unknown';
            break;
          }
          await sleep(2000);
          attempts++;
        }

        const finalAgent = await harness.agentRepository.findById(agent.id);
        console.log('  6. Final status:', finalAgent?.status);

        // Either completed successfully or crashed (both are valid outcomes)
        expect(['idle', 'crashed', 'stopped']).toContain(finalAgent?.status);

        if (finalAgent?.status === 'idle') {
          const result = await newManager.getResult(agent.id);
          console.log('  Result:', result?.message);
        }
      },
      EXTENDED_TEST_TIMEOUT
    );

    it(
      'marks dead agent as crashed during reconcile',
      async () => {
        // 1. Create a fake agent record with a dead PID
        console.log('  1. Creating fake agent with dead PID...');
        const fakeAgent = await harness.agentRepository.create({
          name: 'dead-agent-test',
          taskId: null,
          initiativeId: null,
          sessionId: null,
          worktreeId: 'dead-worktree',
          status: 'running',
          mode: 'execute',
          provider: 'claude',
          accountId: null,
        });

        // Set a PID that's definitely dead (high number that won't exist)
        await harness.agentRepository.update(fakeAgent.id, { pid: 999999, outputFilePath: '/nonexistent/path' });

        // Verify it's marked as running
        let agent = await harness.agentRepository.findById(fakeAgent.id);
        expect(agent?.status).toBe('running');
        expect(agent?.pid).toBe(999999);

        // 2. Create new manager and reconcile
        console.log('  2. Creating new manager and reconciling...');
        const newManager = new MultiProviderAgentManager(
          harness.agentRepository,
          harness.workspaceRoot,
          harness.projectRepository,
          harness.accountRepository,
          harness.eventBus
        );

        await newManager.reconcileAfterRestart();

        // 3. Verify agent is now crashed
        agent = await harness.agentRepository.findById(fakeAgent.id);
        expect(agent?.status).toBe('crashed');
        console.log('  3. Agent marked as crashed (dead PID detected)');
      },
      REAL_TEST_TIMEOUT
    );

    it(
      'processes output file for dead agent during reconcile',
      async () => {
        // 1. Spawn agent and wait for completion
        console.log('  1. Spawning agent to completion...');
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.done,
          mode: 'execute',
          provider: 'claude',
        });

        await harness.waitForAgentCompletion(agent.id, REAL_TEST_TIMEOUT);

        const dbAgent = await harness.agentRepository.findById(agent.id);
        const outputFilePath = dbAgent?.outputFilePath;
        expect(outputFilePath).toBeTruthy();
        console.log('  2. Output file:', outputFilePath);

        // 2. Reset agent to "running" to simulate mid-crash state
        await harness.agentRepository.update(agent.id, { status: 'running' });
        // Clear result so reconcile has to re-process
        await harness.agentRepository.update(agent.id, { result: null });

        // Verify reset
        let resetAgent = await harness.agentRepository.findById(agent.id);
        expect(resetAgent?.status).toBe('running');

        // 3. Create new manager and reconcile
        console.log('  3. Creating new manager and reconciling...');
        harness.clearEvents();

        const newManager = new MultiProviderAgentManager(
          harness.agentRepository,
          harness.workspaceRoot,
          harness.projectRepository,
          harness.accountRepository,
          harness.eventBus
        );

        await newManager.reconcileAfterRestart();

        // Give it a moment to process the file
        await sleep(1000);

        // 4. Verify agent was processed from output file
        const finalAgent = await harness.agentRepository.findById(agent.id);
        console.log('  4. Final status:', finalAgent?.status);

        // Should either be idle (processed successfully) or crashed (couldn't process)
        expect(['idle', 'crashed']).toContain(finalAgent?.status);
      },
      REAL_TEST_TIMEOUT
    );
  });

  describe('Event Consistency', () => {
    it(
      'does not duplicate events on restart',
      async () => {
        // 1. Spawn agent with slow task
        console.log('  1. Spawning agent...');
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.streaming,
          mode: 'execute',
          provider: 'claude',
        });

        // 2. Wait for some output events
        await sleep(3000);
        const initialOutputCount = harness.getEventsByType('agent:output').length;
        console.log('  2. Initial output events:', initialOutputCount);

        // 3. Wait for completion
        await harness.waitForAgentCompletion(agent.id, REAL_TEST_TIMEOUT);
        const finalOutputCount = harness.getEventsByType('agent:output').length;
        console.log('  3. Final output events:', finalOutputCount);

        // 4. Create new manager and reconcile (agent already complete)
        harness.clearEvents();

        const newManager = new MultiProviderAgentManager(
          harness.agentRepository,
          harness.workspaceRoot,
          harness.projectRepository,
          harness.accountRepository,
          harness.eventBus
        );

        await newManager.reconcileAfterRestart();
        await sleep(1000);

        // 5. Verify no new output events (agent was already complete)
        const postReconcileOutputCount = harness.getEventsByType('agent:output').length;
        console.log('  4. Post-reconcile output events:', postReconcileOutputCount);

        // Should not have re-emitted all the old output events
        expect(postReconcileOutputCount).toBe(0);
      },
      REAL_TEST_TIMEOUT
    );
  });
});
