/**
 * Integration Test: Full Observation Lifecycle
 *
 * Tests the complete flow from tool usage to observation storage
 * and retrieval through search. This validates that all components
 * work together correctly.
 */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
  bashCommandScenario,
  sessionScenario,
  sampleObservation
} from '../helpers/scenarios.js';
import { getWorkerPort } from '../../src/shared/worker-utils.js';

describe('Full Observation Lifecycle', () => {
  const WORKER_PORT = getWorkerPort();
  let sessionId: string;

  beforeEach(() => {
    vi.clearAllMocks();
    sessionId = sessionScenario.claudeSessionId;
  });

  it('observation flows from hook to database to search', async () => {
    /**
     * This integration test simulates the complete happy path:
     *
     * 1. Session starts → Context injected
     * 2. User types prompt → First tool runs
     * 3. Tool result captured → Observation queued
     * 4. SDK processes → Observation saved
     * 5. Search finds observation
     * 6. Session ends → Cleanup
     */

    // === Step 1: Context Injection (SessionStart) ===
    global.fetch = vi.fn().mockResolvedValueOnce({
      ok: true,
      status: 200,
      text: async () => '# [claude-mem] recent context\n\nNo observations yet.'
    });

    const contextResponse = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/context/inject?project=claude-mem`
    );
    expect(contextResponse.ok).toBe(true);
    const contextText = await contextResponse.text();
    expect(contextText).toContain('recent context');

    // === Step 2 & 3: Tool runs, Observation captured (PostToolUse) ===
    global.fetch = vi.fn().mockResolvedValueOnce({
      ok: true,
      status: 200,
      json: async () => ({ status: 'queued', observationId: 1 })
    });

    const observationResponse = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          claudeSessionId: sessionId,
          tool_name: bashCommandScenario.tool_name,
          tool_input: bashCommandScenario.tool_input,
          tool_response: bashCommandScenario.tool_response,
          cwd: '/project/claude-mem'
        })
      }
    );
    expect(observationResponse.ok).toBe(true);
    const obsResult = await observationResponse.json();
    expect(obsResult.status).toBe('queued');

    // === Step 4: Simulate SDK processing and saving observation ===
    // In a real flow, the SDK would process the tool data and generate an observation
    // For this test, we simulate the observation being saved to the database

    // === Step 5: Search finds the observation ===
    global.fetch = vi.fn().mockResolvedValueOnce({
      ok: true,
      status: 200,
      json: async () => ({
        results: [
          {
            id: 1,
            title: 'Git status check',
            content: 'Checked repository status, working tree clean',
            type: 'discovery',
            files: [],
            created_at: new Date().toISOString()
          }
        ],
        total: 1
      })
    });

    const searchResponse = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/search?query=git+status&project=claude-mem`
    );
    expect(searchResponse.ok).toBe(true);
    const searchResults = await searchResponse.json();
    expect(searchResults.results).toHaveLength(1);
    expect(searchResults.results[0].title).toContain('Git');

    // === Step 6: Session summary (Stop) ===
    global.fetch = vi.fn().mockResolvedValueOnce({
      ok: true,
      status: 200,
      json: async () => ({ status: 'queued' })
    });

    const summaryResponse = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/sessions/summarize`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          claudeSessionId: sessionId,
          last_user_message: 'Thanks!',
          last_assistant_message: 'Checked git status successfully.',
          cwd: '/project/claude-mem'
        })
      }
    );
    expect(summaryResponse.ok).toBe(true);

    // === Step 7: Session cleanup (SessionEnd) ===
    global.fetch = vi.fn().mockResolvedValueOnce({
      ok: true,
      status: 200,
      json: async () => ({ status: 'completed' })
    });

    const cleanupResponse = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/sessions/complete`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          claudeSessionId: sessionId,
          reason: 'user_exit'
        })
      }
    );
    expect(cleanupResponse.ok).toBe(true);

    // Verify: All steps completed successfully
    expect(global.fetch).toHaveBeenCalled();
  });

  it('handles multiple observations in a single session', async () => {
    /**
     * Tests a more realistic session with multiple tool uses
     * and observations being generated.
     */

    // Track all observations in this session
    const observations: any[] = [];

    // Mock worker to accept multiple observations
    let obsCount = 0;
    global.fetch = vi.fn().mockImplementation(async (url: string, options?: any) => {
      if (url.includes('/api/sessions/observations') && options?.method === 'POST') {
        obsCount++;
        const body = JSON.parse(options.body);
        observations.push(body);
        return {
          ok: true,
          status: 200,
          json: async () => ({ status: 'queued', observationId: obsCount })
        };
      }
      if (url.includes('/api/search')) {
        return {
          ok: true,
          status: 200,
          json: async () => ({
            results: observations.map((obs, i) => ({
              id: i + 1,
              title: `Observation ${i + 1}`,
              content: `Tool: ${obs.tool_name}`,
              type: 'discovery',
              created_at: new Date().toISOString()
            })),
            total: observations.length
          })
        };
      }
      return { ok: true, status: 200, json: async () => ({}) };
    });

    // Simulate 5 different tool uses
    const tools = [
      { name: 'Bash', input: { command: 'npm test' } },
      { name: 'Read', input: { file_path: '/src/index.ts' } },
      { name: 'Edit', input: { file_path: '/src/index.ts', old_string: 'old', new_string: 'new' } },
      { name: 'Grep', input: { pattern: 'function', path: '/src' } },
      { name: 'Write', input: { file_path: '/src/new.ts', content: 'code' } }
    ];

    // Send observations for each tool
    for (const tool of tools) {
      const response = await fetch(
        `http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
        {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            claudeSessionId: sessionId,
            tool_name: tool.name,
            tool_input: tool.input,
            tool_response: { success: true },
            cwd: '/project'
          })
        }
      );
      expect(response.ok).toBe(true);
    }

    // Verify: All observations were queued
    expect(observations).toHaveLength(5);
    expect(observations.map(o => o.tool_name)).toEqual(['Bash', 'Read', 'Edit', 'Grep', 'Write']);

    // Search finds all observations
    const searchResponse = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/search?query=observation&project=test-project`
    );
    const searchResults = await searchResponse.json();
    expect(searchResults.results).toHaveLength(5);
  });

  it('preserves context across session lifecycle', async () => {
    /**
     * Tests that observations from one session can be found
     * when starting a new session in the same project.
     */

    // Session 1: Create some observations
    global.fetch = vi.fn().mockImplementation(async (url: string, options?: any) => {
      if (url.includes('/api/sessions/observations')) {
        return {
          ok: true,
          status: 200,
          json: async () => ({ status: 'queued', observationId: 1 })
        };
      }
      if (url.includes('/api/context/inject')) {
        return {
          ok: true,
          status: 200,
          text: async () => `# [test-project] recent context

## Recent Work (1 observation)

### [bugfix] Fixed parser bug
The XML parser now handles self-closing tags correctly.
Files: /src/parser.ts`
        };
      }
      return { ok: true, status: 200, json: async () => ({}) };
    });

    // Session 1: Add observation
    await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          claudeSessionId: 'session-1',
          tool_name: 'Edit',
          tool_input: { file_path: '/src/parser.ts' },
          tool_response: { success: true },
          cwd: '/project/test-project'
        })
      }
    );

    // Session 2: Start new session, should see context from session 1
    const contextResponse = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/context/inject?project=test-project`
    );
    const context = await contextResponse.text();

    // Verify: Context includes previous session's work
    expect(context).toContain('Fixed parser bug');
    expect(context).toContain('parser.ts');
  });

  it('handles error recovery gracefully', async () => {
    /**
     * Tests that the system continues to work even if some
     * operations fail along the way.
     */

    let callCount = 0;
    global.fetch = vi.fn().mockImplementation(async () => {
      callCount++;

      // First call fails (simulating transient error)
      if (callCount === 1) {
        return {
          ok: false,
          status: 500,
          json: async () => ({ error: 'Temporary error' })
        };
      }

      // Subsequent calls succeed
      return {
        ok: true,
        status: 200,
        json: async () => ({ status: 'queued' })
      };
    });

    // First attempt fails
    const firstAttempt = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          claudeSessionId: sessionId,
          tool_name: 'Bash',
          tool_input: { command: 'test' },
          tool_response: {},
          cwd: '/project'
        })
      }
    );
    expect(firstAttempt.ok).toBe(false);

    // Retry succeeds
    const secondAttempt = await fetch(
      `http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          claudeSessionId: sessionId,
          tool_name: 'Bash',
          tool_input: { command: 'test' },
          tool_response: {},
          cwd: '/project'
        })
      }
    );
    expect(secondAttempt.ok).toBe(true);
  });
});
