/**
 * Mock Preload Script for E2E Tests
 *
 * This script is preloaded before the server starts via --preload flag.
 * It sets globalThis.__pipaliMockLLM which the conversation module checks
 * to return deterministic mock responses instead of calling real LLMs.
 */

import { findMatchingScenario, defaultMockScenarios, type MockScenario } from './fixtures/mock-llm';
import type { ResponseWithThought } from '../../src/server/processor/conversation/conversation';

type MockCtx = { sessionId?: string };

// Track mock state per session+scenario+query so multiple conversations can run concurrently.
const scenarioState = new Map<string, { currentIteration: number }>();

// Parse scenarios from environment if provided
function getScenarios(): MockScenario[] {
    const envScenarios = process.env.PIPALI_MOCK_SCENARIOS;
    if (envScenarios) {
        try {
            return JSON.parse(envScenarios);
        } catch {
            console.warn('[MockPreload] Failed to parse PIPALI_MOCK_SCENARIOS, using defaults');
        }
    }
    return defaultMockScenarios;
}

const scenarios = getScenarios();
console.log(`[MockPreload] Loaded ${scenarios.length} mock scenarios`);

function getStateKey(query: string, scenarioName: string, ctx?: MockCtx): string {
    const sessionId = ctx?.sessionId ?? 'no-session';
    return `${sessionId}::${scenarioName}::${query}`;
}

/**
 * Generate mock response based on query and scenario
 */
function getMockResponse(query: string, ctx?: MockCtx): ResponseWithThought | Promise<ResponseWithThought> {
    const scenario = findMatchingScenario(query, scenarios);

    if (!scenario) {
        console.log(`[MockLLM] No matching scenario for query: "${query}"`);
        return {
            message: 'Mock response: No matching scenario found.',
            raw: [],
            thought: undefined,
        };
    }

    console.log(`[MockLLM] Matched scenario: ${scenario.name} for query: "${query}"`);

    const key = getStateKey(query, scenario.name, ctx);
    const sessionId = ctx?.sessionId;

    // Get or initialize scenario state for this session/query
    let state = scenarioState.get(key);
    if (!state) {
        if (sessionId) {
            console.log(`[MockLLM] Initializing state for session: ${sessionId} scenario: ${scenario.name}`);
        }
        state = { currentIteration: 0 };
        scenarioState.set(key, state);
    }

    const iterations = scenario.iterations;

    // If we've exhausted iterations, return final response
    if (state.currentIteration >= iterations.length) {
        console.log(`[MockLLM] Scenario ${scenario.name} complete, returning final response`);
        scenarioState.delete(key);
        const finalResponse: ResponseWithThought = {
            message: scenario.finalResponse,
            raw: [],
            thought: undefined,
        };
        // Async (not sync) sleep, so a mid-flight soft interrupt lands before the run ends.
        if (scenario.finalResponseDelayMs && scenario.finalResponseDelayMs > 0) {
            return Bun.sleep(scenario.finalResponseDelayMs).then(() => finalResponse);
        }
        return finalResponse;
    }

    const iteration = iterations[state.currentIteration];
    if (!iteration) {
        return {
            message: scenario.finalResponse,
            raw: [],
            thought: undefined,
        };
    }
    state.currentIteration++;

    console.log(`[MockLLM] Scenario ${scenario.name} iteration ${state.currentIteration}/${iterations.length}`);

    // Add delay if configured (synchronous for simplicity)
    if (scenario.iterationDelayMs && scenario.iterationDelayMs > 0) {
        Bun.sleepSync(scenario.iterationDelayMs);
    }

    // Return in the format expected by director (ResponseOutputItem[] for tool calls)
    return {
        message: undefined,
        raw: iteration.toolCalls.map((tc) => ({
            type: 'function_call' as const,
            id: tc.tool_call_id,
            call_id: tc.tool_call_id,
            name: tc.function_name,
            arguments: JSON.stringify(tc.arguments),
        })),
        thought: iteration.thought,
    };
}

/**
 * Reset mock state - call this when a new WebSocket connection is established
 * to ensure each test/conversation starts fresh
 */
function resetMockState() {
    scenarioState.clear();
    console.log('[MockLLM] State reset');
}

// Declare the reset function type for global access
declare global {
    var __pipaliMockReset: typeof resetMockState | undefined;
}

globalThis.__pipaliMockLLM = getMockResponse;
globalThis.__pipaliMockReset = resetMockState;

console.log('[MockPreload] ✅ Mock LLM initialized');
