/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { executeSpawn } from './spawn-bridge.js';
// ─── In-memory session registry ─────────────────────────────────────────────
const sessions = new Map();
const eventListeners = [];
export function getSession(sessionId) {
    return sessions.get(sessionId);
}
export function listSessions(filter) {
    let results = [...sessions.values()];
    if (filter?.taskId) {
        results = results.filter((s) => s.request.taskId === filter.taskId);
    }
    if (filter?.status) {
        results = results.filter((s) => s.status === filter.status);
    }
    return results;
}
export function onSessionEvent(listener) {
    eventListeners.push(listener);
    return () => {
        const idx = eventListeners.indexOf(listener);
        if (idx >= 0)
            eventListeners.splice(idx, 1);
    };
}
function emitEvent(event) {
    for (const listener of eventListeners) {
        listener(event);
    }
}
// ─── Detached spawn execution ───────────────────────────────────────────────
export function spawnDetached(request, cwd, mode = 'auto') {
    const session = {
        id: request.id,
        request,
        status: 'running',
        startedAt: new Date().toISOString(),
    };
    sessions.set(session.id, session);
    emitEvent({
        type: 'spawn-started',
        sessionId: session.id,
        taskId: request.taskId,
        phase: request.phase,
        role: request.role,
        timestamp: session.startedAt,
    });
    executeSpawn(request, cwd, mode).then((bridgeResult) => {
        completeSession(session.id, bridgeResult, cwd);
    }, (error) => {
        const s = sessions.get(session.id);
        if (s) {
            s.status = 'failed';
            s.completedAt = new Date().toISOString();
            s.violation = error instanceof Error ? error.message : String(error);
            emitEvent({
                type: 'spawn-failed',
                sessionId: s.id,
                taskId: request.taskId,
                phase: request.phase,
                role: request.role,
                timestamp: s.completedAt,
            });
        }
    });
    return session;
}
function completeSession(sessionId, bridgeResult, cwd) {
    const session = sessions.get(sessionId);
    if (!session)
        return;
    if (bridgeResult.violation && !bridgeResult.fallback) {
        session.status = 'rejected';
        session.violation = bridgeResult.violation.message;
    }
    else if (bridgeResult.fallback) {
        session.status = 'queued';
        session.fallback = true;
        session.completedAt = new Date().toISOString();
        emitEvent({
            type: 'spawn-queued',
            sessionId: session.id,
            taskId: session.request.taskId,
            phase: session.request.phase,
            role: session.request.role,
            timestamp: session.completedAt,
            reason: bridgeResult.fallback.reason,
        });
        persistSession(session, cwd);
        return;
    }
    else if (bridgeResult.result) {
        session.status = bridgeResult.result.status === 'completed' ? 'completed' : 'failed';
        session.result = bridgeResult.result;
    }
    else {
        session.status = 'failed';
    }
    session.completedAt = new Date().toISOString();
    const eventType = session.status === 'completed' ? 'spawn-completed' : 'spawn-failed';
    emitEvent({
        type: eventType,
        sessionId: session.id,
        taskId: session.request.taskId,
        phase: session.request.phase,
        role: session.request.role,
        timestamp: session.completedAt,
        result: session.result,
    });
    persistSession(session, cwd);
}
// ─── Persistence ────────────────────────────────────────────────────────────
function persistSession(session, cwd) {
    const stateDir = join(cwd, '.setup-agents', 'state');
    if (!existsSync(stateDir)) {
        mkdirSync(stateDir, { recursive: true });
    }
    const filePath = join(stateDir, 'spawn-sessions.jsonl');
    const record = {
        id: session.id,
        taskId: session.request.taskId,
        runId: session.request.runId,
        phase: session.request.phase,
        role: session.request.role,
        adapterId: session.request.adapterId,
        status: session.status,
        startedAt: session.startedAt,
        completedAt: session.completedAt,
        fallback: session.fallback ?? false,
        elapsedMs: session.result?.elapsedMs,
        closeStatus: session.result?.closeStatus,
    };
    appendFileSync(filePath, JSON.stringify(record) + '\n');
}
export function loadPersistedSessions(cwd) {
    const filePath = join(cwd, '.setup-agents', 'state', 'spawn-sessions.jsonl');
    if (!existsSync(filePath))
        return [];
    const content = readFileSync(filePath, 'utf-8').trim();
    if (!content)
        return [];
    return content.split('\n').map((line) => {
        const record = JSON.parse(line);
        return {
            id: record.id,
            request: {
                id: record.id,
                taskId: record.taskId,
                runId: record.runId,
                phase: record.phase,
                role: record.role,
                adapterId: record.adapterId,
                contextBundle: '{}',
                renderedPrompt: '',
                ownershipBoundaries: { files: [], readOnly: [], forbidden: [] },
                outputContract: { expectedType: 'deliverable' },
                evidenceContract: { requiredTypes: [], autoRecord: true },
                queueMetadata: {
                    priority: 5,
                    maxConcurrent: 2,
                    maxPerTask: 8,
                    delegationDepth: 0,
                    maxDelegationDepth: 3,
                    contextBudgetTokens: 100_000,
                },
                createdAt: record.startedAt,
            },
            status: record.status,
            startedAt: record.startedAt,
            completedAt: record.completedAt,
            fallback: record.fallback,
        };
    });
}
//# sourceMappingURL=spawn-session.js.map