/**
 * AgentTaskAdapter — bridges agent sessions (AgentOrchestrator / WRFC agents)
 * into the unified RuntimeTask registry.
 *
 * Each running agent gets a corresponding RuntimeTask of kind 'agent'. The
 * adapter maps agent lifecycle state strings to task lifecycle transitions.
 */
import { randomUUID } from 'node:crypto';
import { createDomainDispatch } from '../../store/index.js';
/** Terminal agent lifecycle states that map to terminal task states. */
const TERMINAL_STATES = new Set([
    'completed',
    'failed',
    'cancelled',
]);
/** Active (non-terminal, non-queued) agent states that map to 'running'. */
const RUNNING_STATES = new Set([
    'running',
    'awaiting_message',
    'awaiting_tool',
    'finalizing',
]);
/**
 * Maps an agent lifecycle state string to a RuntimeTask lifecycle state.
 *
 * @param state - Agent lifecycle state from AgentLifecycleState.
 * @returns Corresponding task lifecycle state.
 */
function mapAgentStateToTask(state) {
    if (state === 'spawning')
        return 'queued';
    if (RUNNING_STATES.has(state))
        return 'running';
    if (state === 'completed')
        return 'completed';
    if (state === 'failed')
        return 'failed';
    if (state === 'cancelled')
        return 'cancelled';
    return 'running'; // fallback for unknown states
}
/**
 * Bridges agent sessions into the RuntimeTask registry.
 *
 * @example
 * ```ts
 * const adapter = new AgentTaskAdapter(store);
 * const taskId = adapter.wrapAgent('agent_1', 'Fix the linting errors', { sessionId: 'sess_1' });
 * adapter.handleAgentStateChange('agent_1', 'running');
 * adapter.handleAgentStateChange('agent_1', 'completed');
 * ```
 */
export class AgentTaskAdapter {
    _store;
    /** Maps agent ID → task ID. */
    _agentToTask = new Map();
    /** Maps task ID → agent ID. */
    _taskToAgent = new Map();
    _dispatch;
    constructor(_store) {
        this._store = _store;
        this._dispatch = createDomainDispatch(_store);
    }
    // ── Core API ────────────────────────────────────────────────────────────────
    /**
     * Wrap an agent session as a RuntimeTask.
     *
     * @param agentId - Unique agent ID from the agent system.
     * @param task - Human-readable description of what the agent is doing.
     * @param owner - Session that spawned this agent.
     * @returns The new task ID.
     */
    wrapAgent(agentId, task, owner) {
        // Idempotent: return existing task ID if already wrapped
        const existing = this._agentToTask.get(agentId);
        if (existing !== undefined)
            return existing;
        const taskId = randomUUID();
        const now = Date.now();
        const runtimeTask = {
            id: taskId,
            kind: 'agent',
            title: task.length > 80 ? `${task.slice(0, 77)}...` : task,
            description: task,
            status: 'queued',
            owner: agentId,
            cancellable: true,
            childTaskIds: [],
            queuedAt: now,
            correlationId: owner.sessionId,
        };
        this._agentToTask.set(agentId, taskId);
        this._taskToAgent.set(taskId, agentId);
        this._upsertTask(runtimeTask);
        return taskId;
    }
    /**
     * Handle an agent lifecycle state change and transition the task accordingly.
     *
     * @param agentId - The agent whose state changed.
     * @param state - New agent lifecycle state (AgentLifecycleState string).
     */
    handleAgentStateChange(agentId, state) {
        const taskId = this._agentToTask.get(agentId);
        if (taskId === undefined)
            return;
        const agentState = state;
        const taskStatus = mapAgentStateToTask(agentState);
        this._transitionTask(taskId, taskStatus, {
            isTerminal: TERMINAL_STATES.has(agentState),
            error: agentState === 'failed' ? `Agent ${agentId} failed` : undefined,
        });
        // Clean up mappings once terminal
        if (TERMINAL_STATES.has(agentState)) {
            this._agentToTask.delete(agentId);
            this._taskToAgent.delete(taskId);
        }
    }
    /**
     * Cancel an agent task by task ID.
     * Marks the task as cancelled in the store. The caller is responsible for
     * actually stopping the agent session.
     *
     * @param taskId - The RuntimeTask ID to cancel.
     */
    cancelAgent(taskId) {
        const agentId = this._taskToAgent.get(taskId);
        if (agentId === undefined)
            return;
        this._transitionTask(taskId, 'cancelled', { isTerminal: true });
        this._agentToTask.delete(agentId);
        this._taskToAgent.delete(taskId);
    }
    /**
     * Reconcile adapter state with an external agent registry snapshot.
     *
     * @param activeAgents - Current snapshot of active agents: agentId → task description.
     * @param owner - Default owner context for auto-wrapped agents.
     */
    sync(activeAgents, owner = { sessionId: 'system' }) {
        const liveIds = new Set(activeAgents.keys());
        // Wrap newly discovered agents
        for (const [agentId, task] of activeAgents.entries()) {
            if (!this._agentToTask.has(agentId)) {
                this.wrapAgent(agentId, task, owner);
            }
        }
        // Mark stale tracked agents as cancelled
        const staleAgentIds = [];
        for (const [agentId] of this._agentToTask.entries()) {
            if (!liveIds.has(agentId))
                staleAgentIds.push(agentId);
        }
        for (const agentId of staleAgentIds) {
            const taskId = this._agentToTask.get(agentId);
            this._transitionTask(taskId, 'cancelled', { isTerminal: true });
            this._agentToTask.delete(agentId);
            this._taskToAgent.delete(taskId);
        }
    }
    // ── Private helpers ─────────────────────────────────────────────────────────
    _upsertTask(task) {
        this._dispatch.syncRuntimeTask(task, 'agent-adapter');
    }
    _transitionTask(taskId, status, opts) {
        const current = this._store.getState().tasks.tasks.get(taskId);
        const timestamp = Date.now();
        this._dispatch.transitionRuntimeTask(taskId, status, {
            startedAt: status === 'running' && current?.startedAt === undefined ? timestamp : current?.startedAt,
            endedAt: opts.isTerminal ? timestamp : current?.endedAt,
            error: opts.error,
        }, 'agent-adapter');
    }
}
