/**
 * Agent Runner Service
 *
 * Infrastructure implementation of the IAgentRunner port.
 * Orchestrates agent execution by resolving definitions from the registry,
 * creating executors, compiling LangGraph workflows, and tracking runs
 * in the repository.
 */
import * as crypto from 'node:crypto';
import { AgentRunStatus } from '../../../../domain/generated/output.js';
import { getSettings } from '../../../../infrastructure/services/settings.service.js';
import { EventChannel } from '../streaming/event-channel.js';
import { StreamingExecutorProxy } from '../streaming/streaming-executor-proxy.js';
export class AgentRunnerService {
    registry;
    executorFactory;
    checkpointer;
    runRepository;
    constructor(registry, executorFactory, checkpointer, runRepository) {
        this.registry = registry;
        this.executorFactory = executorFactory;
        this.checkpointer = checkpointer;
        this.runRepository = runRepository;
    }
    async runAgent(agentName, prompt, options) {
        const { definition, executor, runId, threadId } = await this.setupRun(agentName, prompt);
        try {
            const compiledGraph = definition.graphFactory(executor, this.checkpointer);
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const result = await compiledGraph.invoke({ repositoryPath: options?.repositoryPath ?? process.cwd() }, { configurable: { thread_id: threadId } });
            await this.markCompleted(runId, result);
            return (await this.runRepository.findById(runId));
        }
        catch (error) {
            await this.markFailed(runId, error);
            return (await this.runRepository.findById(runId));
        }
    }
    async *runAgentStream(agentName, prompt, options) {
        const { definition, executor, runId, threadId } = await this.setupRun(agentName, prompt);
        // Create streaming channel and proxy
        const channel = new EventChannel();
        const proxy = new StreamingExecutorProxy(executor, channel);
        // Compile graph with proxy (graph doesn't know about streaming)
        const compiledGraph = definition.graphFactory(proxy, this.checkpointer);
        // Start graph invocation in background
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const graphPromise = compiledGraph
            .invoke({ repositoryPath: options?.repositoryPath ?? process.cwd() }, { configurable: { thread_id: threadId } })
            .then(async (result) => {
            channel.close();
            await this.markCompleted(runId, result);
        })
            .catch(async (error) => {
            channel.close();
            await this.markFailed(runId, error);
        });
        // Yield events as they arrive, mapping infra → domain type
        for await (const event of channel) {
            yield {
                type: event.type,
                content: event.content,
                timestamp: event.timestamp instanceof Date ? event.timestamp.toISOString() : event.timestamp,
            };
        }
        // Ensure graph promise cleanup completes
        await graphPromise;
    }
    async setupRun(agentName, prompt) {
        const definition = this.registry.get(agentName);
        if (!definition) {
            throw new Error(`Agent '${agentName}' not found. Available: ${this.registry
                .list()
                .map((a) => a.name)
                .join(', ')}`);
        }
        const settings = getSettings();
        const agentType = settings.agent.type;
        const executor = this.executorFactory.createExecutor(agentType, settings.agent);
        const runId = crypto.randomUUID();
        const threadId = crypto.randomUUID();
        const now = new Date().toISOString();
        const agentRun = {
            id: runId,
            agentType,
            agentName,
            status: AgentRunStatus.pending,
            prompt,
            threadId,
            createdAt: now,
            updatedAt: now,
        };
        await this.runRepository.create(agentRun);
        await this.runRepository.updateStatus(runId, AgentRunStatus.running, {
            pid: process.pid,
            startedAt: now,
            updatedAt: now,
        });
        return { definition, executor, runId, threadId };
    }
    async markCompleted(runId, result) {
        const completedAt = new Date().toISOString();
        await this.runRepository.updateStatus(runId, AgentRunStatus.completed, {
            result: result.analysisMarkdown ?? result.result ?? JSON.stringify(result),
            completedAt,
            updatedAt: completedAt,
        });
    }
    async markFailed(runId, error) {
        const failedAt = new Date().toISOString();
        await this.runRepository.updateStatus(runId, AgentRunStatus.failed, {
            error: error instanceof Error ? error.message : String(error),
            completedAt: failedAt,
            updatedAt: failedAt,
        });
    }
}
