/*
 * 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 { Hono } from 'hono';
import { streamSSE } from 'hono/streaming';
import { BUILTIN_ADAPTERS } from '../../services/spawn-adapters.js';
import { buildSpawnRequest, executeSpawn, getAdapter, listAdapters, registerAdapter, validateGuardrails, } from '../../services/spawn-bridge.js';
import { getSession, listSessions, onSessionEvent, spawnDetached } from '../../services/spawn-session.js';
import { buildDelegationPacket } from '../../services/workflow-delegation.js';
import { PHASE_TO_PROFILE } from '../../services/workflow-instructions.js';
function validateSpawnBody(body) {
    if (!body.taskId || !body.runId || !body.phase || !body.goal) {
        return { ok: false, error: 'Missing required fields: taskId, runId, phase, goal' };
    }
    const phase = body.phase;
    if (!PHASE_TO_PROFILE[phase]) {
        return { ok: false, error: `Invalid phase: ${body.phase}` };
    }
    const adapterId = body.adapterId ?? 'claude';
    if (!getAdapter(adapterId)) {
        return { ok: false, error: `Unknown adapter: ${adapterId}` };
    }
    return {
        ok: true,
        validated: {
            taskId: body.taskId,
            runId: body.runId,
            phase,
            goal: body.goal,
            adapterId,
            mode: body.mode ?? 'auto',
            delegationDepth: body.delegationDepth,
            guardrails: body.guardrails,
        },
    };
}
async function buildPhaseRequest(cwd, validated) {
    const packet = await buildDelegationPacket({
        cwd,
        taskId: validated.taskId,
        runId: validated.runId,
        phase: validated.phase,
        goal: validated.goal,
        priorPhases: [],
    });
    const role = PHASE_TO_PROFILE[validated.phase];
    return buildSpawnRequest({
        taskId: validated.taskId,
        runId: validated.runId,
        phase: validated.phase,
        role,
        adapterId: validated.adapterId,
        packet,
        delegationDepth: validated.delegationDepth,
        guardrails: validated.guardrails,
    });
}
// ─── Route factory ───────────────────────────────────────────────────────────
export function spawnRoute(cwd) {
    const app = new Hono();
    for (const adapter of BUILTIN_ADAPTERS) {
        registerAdapter(adapter);
    }
    app.get('/adapters', (c) => {
        const adapters = listAdapters().map((a) => ({
            id: a.id,
            runtime: a.runtime,
            capability: a.capability,
        }));
        return c.json({ adapters });
    });
    app.post('/request', async (c) => {
        const body = await c.req.json();
        const result = validateSpawnBody(body);
        if (!result.ok) {
            return c.json({ error: result.error }, 400);
        }
        const request = await buildPhaseRequest(cwd, result.validated);
        const adapter = getAdapter(result.validated.adapterId);
        return c.json({ request, adapter: { id: adapter.id, capability: adapter.capability } });
    });
    app.post('/execute', async (c) => {
        const body = await c.req.json();
        const validation = validateSpawnBody(body);
        if (!validation.ok) {
            return c.json({ error: validation.error }, 400);
        }
        const request = await buildPhaseRequest(cwd, validation.validated);
        const result = await executeSpawn(request, cwd, validation.validated.mode);
        return c.json(result);
    });
    app.post('/validate', async (c) => {
        const body = await c.req.json();
        if (!body.adapterId || !body.taskId) {
            return c.json({ error: 'Missing required fields: adapterId, taskId' }, 400);
        }
        const adapter = getAdapter(body.adapterId);
        const mockRequest = {
            id: 'validate-check',
            taskId: body.taskId,
            runId: '',
            phase: 'developer',
            role: 'developer',
            adapterId: body.adapterId,
            contextBundle: '{}',
            renderedPrompt: '',
            ownershipBoundaries: { files: [], readOnly: [], forbidden: [] },
            outputContract: { expectedType: 'deliverable' },
            evidenceContract: { requiredTypes: [], autoRecord: true },
            queueMetadata: {
                priority: 5,
                maxConcurrent: 2,
                maxPerTask: body.maxPerTask ?? 8,
                delegationDepth: body.delegationDepth ?? 0,
                maxDelegationDepth: body.maxDelegationDepth ?? 3,
                contextBudgetTokens: 100_000,
            },
            createdAt: new Date().toISOString(),
        };
        const violation = validateGuardrails(mockRequest, adapter);
        return c.json({ valid: !violation, violation });
    });
    // ─── Detached spawn (fire-and-forget, parent continues) ─────────────────
    app.post('/detach', async (c) => {
        const body = await c.req.json();
        const validation = validateSpawnBody(body);
        if (!validation.ok) {
            return c.json({ error: validation.error }, 400);
        }
        const request = await buildPhaseRequest(cwd, validation.validated);
        const session = spawnDetached(request, cwd, validation.validated.mode);
        return c.json({ session: { id: session.id, status: session.status, startedAt: session.startedAt } }, 202);
    });
    // ─── Session polling ────────────────────────────────────────────────────
    app.get('/sessions', (c) => {
        const taskId = c.req.query('taskId');
        const status = c.req.query('status');
        const results = listSessions({ taskId: taskId ?? undefined, status: status ?? undefined });
        return c.json({
            sessions: results.map((s) => ({
                id: s.id,
                taskId: s.request.taskId,
                phase: s.request.phase,
                role: s.request.role,
                adapterId: s.request.adapterId,
                status: s.status,
                startedAt: s.startedAt,
                completedAt: s.completedAt,
                fallback: s.fallback,
            })),
        });
    });
    app.get('/sessions/:id', (c) => {
        const session = getSession(c.req.param('id'));
        if (!session) {
            return c.json({ error: 'Session not found' }, 404);
        }
        return c.json({
            id: session.id,
            taskId: session.request.taskId,
            phase: session.request.phase,
            role: session.request.role,
            adapterId: session.request.adapterId,
            status: session.status,
            startedAt: session.startedAt,
            completedAt: session.completedAt,
            result: session.result,
            violation: session.violation,
            fallback: session.fallback,
        });
    });
    // ─── SSE stream for spawn events ───────────────────────────────────────
    app.get('/events', (c) => streamSSE(c, async (stream) => {
        const unsubscribe = onSessionEvent((event) => {
            void stream.writeSSE({ data: JSON.stringify(event), event: event.type, id: event.sessionId });
        });
        stream.onAbort(unsubscribe);
        await new Promise((resolve) => {
            stream.onAbort(resolve);
        });
    }));
    return app;
}
//# sourceMappingURL=spawn.js.map