/**
 * Real Claude CLI Manager 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/claude-manager.test.ts --test-timeout=300000
 * ```
 *
 * Tests covered:
 * - Output stream parsing (text_delta events)
 * - Session ID extraction from init event
 * - Result parsing and validation
 * - Session resume with user answers
 *
 * Estimated cost: ~$0.10 per full run
 */

import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import {
  createRealProviderHarness,
  describeRealClaude,
  REAL_TEST_TIMEOUT,
  sleep,
  type RealProviderHarness,
} from './harness.js';
import { MINIMAL_PROMPTS } from './prompts.js';
import type { AgentSpawnedEvent, AgentStoppedEvent, AgentOutputEvent } from '../../../events/types.js';

describeRealClaude('Real Claude Manager Integration', () => {
  let harness: RealProviderHarness;

  beforeAll(async () => {
    console.log('\n=== Running Real Claude Manager 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('Output Parsing', () => {
    it(
      'parses text_delta events from stream',
      async () => {
        // Spawn agent with streaming prompt
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.streaming,
          mode: 'execute',
          provider: 'claude',
        });

        expect(agent.id).toBeTruthy();
        expect(agent.status).toBe('running');

        // Wait for completion
        const result = await harness.waitForAgentCompletion(agent.id, REAL_TEST_TIMEOUT);

        // Verify we got output events
        const outputEvents = harness.getEventsByType<AgentOutputEvent>('agent:output');
        console.log('  Output events:', outputEvents.length);

        // Verify completion
        expect(result).toBeTruthy();
        console.log('  Result:', result?.message);
      },
      REAL_TEST_TIMEOUT
    );

    it(
      'parses init event and extracts session ID',
      async () => {
        // Spawn agent with simple done prompt
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.done,
          mode: 'execute',
          provider: 'claude',
        });

        // Wait for completion
        await harness.waitForAgentCompletion(agent.id, REAL_TEST_TIMEOUT);

        // Verify session ID was extracted and persisted
        const dbAgent = await harness.agentRepository.findById(agent.id);
        expect(dbAgent?.sessionId).toBeTruthy();
        expect(dbAgent?.sessionId).toMatch(/^[a-f0-9-]+$/);

        console.log('  Session ID:', dbAgent?.sessionId);
      },
      REAL_TEST_TIMEOUT
    );

    it(
      'parses result event with completion',
      async () => {
        // Spawn agent with simple done prompt
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.done,
          mode: 'execute',
          provider: 'claude',
        });

        // Wait for completion
        const result = await harness.waitForAgentCompletion(agent.id, REAL_TEST_TIMEOUT);

        // Verify result was parsed
        expect(result).toBeTruthy();
        expect(result?.success).toBe(true);
        expect(result?.message).toBeTruthy();

        // Verify events
        const spawnedEvents = harness.getEventsByType<AgentSpawnedEvent>('agent:spawned');
        expect(spawnedEvents.length).toBe(1);
        expect(spawnedEvents[0].payload.agentId).toBe(agent.id);
        expect(spawnedEvents[0].payload.provider).toBe('claude');

        const stoppedEvents = harness.getEventsByType<AgentStoppedEvent>('agent:stopped');
        expect(stoppedEvents.length).toBe(1);
        expect(stoppedEvents[0].payload.agentId).toBe(agent.id);
        expect(stoppedEvents[0].payload.reason).toBe('task_complete');

        console.log('  Result message:', result?.message);
      },
      REAL_TEST_TIMEOUT
    );
  });

  describe('Questions Flow', () => {
    it(
      'parses questions status and enters waiting_for_input',
      async () => {
        // Spawn agent with questions prompt
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.questions,
          mode: 'execute',
          provider: 'claude',
        });

        // Wait for waiting_for_input status
        const questions = await harness.waitForAgentWaiting(agent.id, REAL_TEST_TIMEOUT);

        // Verify questions were parsed
        expect(questions).toBeTruthy();
        expect(questions?.questions).toBeTruthy();
        expect(questions?.questions.length).toBeGreaterThan(0);
        expect(questions?.questions[0].id).toBeTruthy();
        expect(questions?.questions[0].question).toBeTruthy();

        // Verify agent status
        const dbAgent = await harness.agentRepository.findById(agent.id);
        expect(dbAgent?.status).toBe('waiting_for_input');
        expect(dbAgent?.sessionId).toBeTruthy();

        console.log('  Questions:', questions?.questions.length);
        console.log('  First question:', questions?.questions[0].question);
      },
      REAL_TEST_TIMEOUT
    );
  });

  describe('Session Resume', () => {
    it(
      'resumes session with user answers',
      async () => {
        // 1. Spawn agent that asks questions
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.questions,
          mode: 'execute',
          provider: 'claude',
        });

        // 2. Wait for waiting_for_input
        const questions = await harness.waitForAgentWaiting(agent.id, REAL_TEST_TIMEOUT);
        expect(questions?.questions.length).toBeGreaterThan(0);

        const sessionIdBeforeResume = (await harness.agentRepository.findById(agent.id))?.sessionId;
        console.log('  Session ID before resume:', sessionIdBeforeResume);
        console.log('  Questions received:', questions?.questions.map((q) => q.id).join(', '));

        harness.clearEvents();

        // 3. Resume with answer
        const answers: Record<string, string> = {};
        for (const q of questions?.questions ?? []) {
          answers[q.id] = `Answer to ${q.id}`;
        }

        await harness.agentManager.resume(agent.id, answers);

        // 4. Wait for completion or another waiting state
        let attempts = 0;
        let finalStatus = 'running';
        while (attempts < 60) {
          const agent2 = await harness.agentRepository.findById(agent.id);
          if (agent2?.status !== 'running') {
            finalStatus = agent2?.status ?? 'unknown';
            break;
          }
          await sleep(1000);
          attempts++;
        }

        // Verify the agent processed the resume (either completed or asked more questions)
        const dbAgent = await harness.agentRepository.findById(agent.id);
        console.log('  Final status:', dbAgent?.status);

        // Agent should not still be running
        expect(['idle', 'waiting_for_input', 'crashed']).toContain(dbAgent?.status);

        // If idle, verify result
        if (dbAgent?.status === 'idle') {
          const result = await harness.agentManager.getResult(agent.id);
          console.log('  Result:', result?.message);
          expect(result).toBeTruthy();
        }
      },
      REAL_TEST_TIMEOUT * 2 // Double timeout for two-step process
    );

    it(
      'maintains session continuity across resume',
      async () => {
        // 1. Spawn agent that asks questions
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.questions,
          mode: 'execute',
          provider: 'claude',
        });

        // 2. Wait for waiting_for_input
        const questions = await harness.waitForAgentWaiting(agent.id, REAL_TEST_TIMEOUT);
        expect(questions?.questions.length).toBeGreaterThan(0);

        const sessionIdBefore = (await harness.agentRepository.findById(agent.id))?.sessionId;
        expect(sessionIdBefore).toBeTruthy();

        // 3. Resume with answer
        const answers: Record<string, string> = {};
        for (const q of questions?.questions ?? []) {
          answers[q.id] = `Answer to ${q.id}`;
        }

        await harness.agentManager.resume(agent.id, answers);

        // 4. Wait for completion
        await harness.waitForAgentCompletion(agent.id, REAL_TEST_TIMEOUT);

        // Verify session ID exists (may be same or new depending on CLI behavior)
        const sessionIdAfter = (await harness.agentRepository.findById(agent.id))?.sessionId;
        expect(sessionIdAfter).toBeTruthy();

        console.log('  Session ID before:', sessionIdBefore);
        console.log('  Session ID after:', sessionIdAfter);
      },
      REAL_TEST_TIMEOUT * 2
    );
  });

  describe('Error Handling', () => {
    it(
      'handles error status',
      async () => {
        // Spawn agent with error prompt
        const agent = await harness.agentManager.spawn({
          taskId: null,
          prompt: MINIMAL_PROMPTS.error,
          mode: 'execute',
          provider: 'claude',
        });

        // Wait for completion (will be crashed)
        const result = await harness.waitForAgentCompletion(agent.id, REAL_TEST_TIMEOUT);

        // Verify error was handled
        const dbAgent = await harness.agentRepository.findById(agent.id);
        expect(dbAgent?.status).toBe('crashed');
        expect(result?.success).toBe(false);
        expect(result?.message).toContain('Test error');

        console.log('  Error message:', result?.message);
      },
      REAL_TEST_TIMEOUT
    );
  });
});
