/**
 * Agent Executor Factory Service
 *
 * Infrastructure implementation of the IAgentExecutorFactory port.
 * Creates and caches agent executor instances based on agent type.
 *
 * Uses constructor dependency injection for the spawn function
 * to enable testability without mocking node:child_process directly.
 */
import { OpenRouterModelCatalogService } from './model-catalogs/openrouter-model-catalog.service.js';
import { TogetherAiModelCatalogService } from './model-catalogs/together-ai-model-catalog.service.js';
import { ClaudeCodeExecutorService } from './executors/claude-code-executor.service.js';
import { ClaudeCodeInteractiveExecutor } from './executors/claude-code-interactive-executor.service.js';
import { CursorExecutorService } from './executors/cursor-executor.service.js';
import { DevAgentExecutorService } from './executors/dev-executor.service.js';
import { GeminiCliExecutorService } from './executors/gemini-cli-executor.service.js';
import { CodexCliExecutorService } from './executors/codex-cli-executor.service.js';
import { CopilotCliExecutorService } from './executors/copilot-cli-executor.service.js';
import { OpenRouterExecutorService } from './executors/openrouter-executor.service.js';
import { TogetherAiExecutorService } from './executors/together-ai-executor.service.js';
import { OllamaExecutorService } from './executors/ollama-executor.service.js';
import { ClineExecutorService } from './executors/cline-executor.service.js';
import { CLAUDE_CODE_MODELS, GEMINI_CLI_MODELS, CURSOR_MODELS, CODEX_CLI_MODELS, COPILOT_CLI_MODELS, CLINE_MODELS, OPENROUTER_MODELS, TOGETHER_AI_MODELS, OLLAMA_MODELS, } from './agent-model-catalog.js';
/**
 * Factory that creates and caches agent executor instances.
 *
 * Executor instances are cached per agent type (singleton per type)
 * to avoid unnecessary re-creation of stateless executors.
 */
export class AgentExecutorFactory {
    spawn;
    cache = new Map();
    openRouterCatalog;
    togetherAiCatalog;
    /**
     * @param spawn - Spawn function for creating subprocesses (injectable for testing).
     * @param openRouterCatalog - Optional OpenRouter catalog (defaults to new instance).
     * @param togetherAiCatalog - Optional Together AI catalog (defaults to new instance).
     */
    constructor(spawn, openRouterCatalog, togetherAiCatalog) {
        this.spawn = spawn;
        this.openRouterCatalog = openRouterCatalog ?? new OpenRouterModelCatalogService();
        this.togetherAiCatalog = togetherAiCatalog ?? new TogetherAiModelCatalogService();
    }
    /**
     * Create (or return cached) executor for the specified agent type.
     *
     * @param agentType - The type of agent to create an executor for
     * @param _authConfig - Agent authentication and configuration
     * @returns A configured agent executor
     * @throws Error if the agent type is not supported
     */
    createExecutor(agentType, _authConfig) {
        const key = agentType;
        const cached = this.cache.get(key);
        if (cached)
            return cached;
        let executor;
        switch (key) {
            case 'claude-code':
                executor = new ClaudeCodeExecutorService(this.spawn);
                break;
            case 'cursor':
                executor = new CursorExecutorService(this.spawn);
                break;
            case 'dev':
                executor = new DevAgentExecutorService();
                break;
            case 'gemini-cli':
                executor = new GeminiCliExecutorService(this.spawn, _authConfig);
                break;
            case 'codex-cli':
                executor = new CodexCliExecutorService(this.spawn, _authConfig);
                break;
            case 'copilot-cli':
                executor = new CopilotCliExecutorService(this.spawn, _authConfig);
                break;
            case 'cline':
                executor = new ClineExecutorService(this.spawn);
                break;
            case 'openrouter':
                executor = new OpenRouterExecutorService(_authConfig.token ?? '');
                break;
            case 'together-ai':
                executor = new TogetherAiExecutorService(_authConfig.token ?? '');
                break;
            case 'ollama':
                executor = new OllamaExecutorService(_authConfig.token ?? undefined);
                break;
            default:
                throw new Error(`Unsupported agent type: ${agentType}. Supported: ${this.getSupportedAgents().join(', ')}`);
        }
        this.cache.set(key, executor);
        return executor;
    }
    /**
     * Get the list of agent types this factory can create executors for.
     *
     * @returns Array of supported agent types
     */
    getSupportedAgents() {
        return [
            'claude-code',
            'cursor',
            'dev',
            'gemini-cli',
            'codex-cli',
            'copilot-cli',
            'cline',
            'openrouter',
            'together-ai',
            'ollama',
        ];
    }
    getCliInfo() {
        return [
            { agentType: 'claude-code', cmd: 'claude', versionArgs: ['--version'] },
            { agentType: 'gemini-cli', cmd: 'gemini', versionArgs: ['--version'] },
            { agentType: 'cursor', cmd: 'cursor', versionArgs: ['--version'] },
            { agentType: 'codex-cli', cmd: 'codex', versionArgs: ['--version'] },
            { agentType: 'copilot-cli', cmd: 'copilot', versionArgs: ['--version'] },
            { agentType: 'cline', cmd: 'cline', versionArgs: ['version'] },
        ];
    }
    /**
     * Get the model identifiers supported by the given agent executor.
     * Returns synchronously with no I/O — lists are static data embedded here.
     *
     * @param agentType - The agent type to query
     * @returns Array of model identifier strings, or empty array for unknown/dev agents
     */
    getSupportedModels(agentType) {
        switch (agentType) {
            case 'claude-code':
                return CLAUDE_CODE_MODELS;
            case 'gemini-cli':
                return GEMINI_CLI_MODELS;
            case 'cursor':
                return CURSOR_MODELS;
            case 'codex-cli':
                return CODEX_CLI_MODELS;
            case 'copilot-cli':
                return COPILOT_CLI_MODELS;
            case 'cline':
                return CLINE_MODELS;
            case 'openrouter':
                return OPENROUTER_MODELS;
            case 'together-ai':
                return TOGETHER_AI_MODELS;
            case 'ollama':
                return OLLAMA_MODELS;
            default:
                return [];
        }
    }
    /**
     * List models available for the given agent type. For OpenRouter and
     * Together AI this hits the provider's catalog API (cached). For all other
     * agents it wraps the static list returned by {@link getSupportedModels}.
     */
    async listAvailableModels(agentType, authConfig) {
        const key = agentType;
        const trimmed = authConfig?.token?.trim();
        const token = trimmed && trimmed.length > 0 ? trimmed : undefined;
        if (key === 'openrouter') {
            const dynamic = await this.openRouterCatalog.listModels(token);
            if (dynamic.length > 0)
                return dynamic;
            return OPENROUTER_MODELS.map((id) => ({ id }));
        }
        if (key === 'together-ai') {
            const dynamic = await this.togetherAiCatalog.listModels(token);
            if (dynamic.length > 0)
                return dynamic;
            return TOGETHER_AI_MODELS.map((id) => ({ id }));
        }
        return this.getSupportedModels(agentType).map((id) => ({ id }));
    }
    /**
     * Create an interactive executor for multi-turn agent sessions.
     * Currently only Claude Code supports interactive sessions via the SDK.
     *
     * @param agentType - The type of agent to create an interactive executor for
     * @param _authConfig - Agent authentication and configuration
     * @returns A configured interactive agent executor
     * @throws Error if the agent type does not support interactive sessions
     */
    createInteractiveExecutor(agentType, _authConfig) {
        const key = agentType;
        if (key === 'claude-code') {
            return new ClaudeCodeInteractiveExecutor();
        }
        throw new Error(`Agent type '${agentType}' does not support interactive sessions. ` +
            `Only 'claude-code' supports interactive mode.`);
    }
    /**
     * Check whether the given agent type supports interactive sessions.
     *
     * @param agentType - The agent type to query
     * @returns true if createInteractiveExecutor can be called for this type
     */
    supportsInteractive(agentType) {
        return agentType === 'claude-code';
    }
}
// Model lists are defined in agent-model-catalog.ts — imported above.
