/*
 * 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 { buildDelegationPacket, parseSubAgentResponse, parseTokenUsage, } from './workflow-delegation.js';
import { PHASE_TO_PROFILE } from './workflow-instructions.js';
import { setMaxConcurrentSpawns, spawnDelegated } from './spawn-pool.js';
import { ClarificationRelay } from './clarification-relay.js';
export { getSpawnPoolStatus, setMaxConcurrentSpawns } from './spawn-pool.js';
// ─── Main orchestrator loop ─────────────────────────────────────────────────
const GATE_TRANSITIONS = new Set(['ba→architect', 'qa→release']);
export async function runDelegatedWorkflow(options) {
    const { cwd, taskId, runId, goal, phases, runtime, gates, maxRetries = 2, callbacks } = options;
    const maxPhaseMs = (options.maxPhaseMinutes ?? 60) * 60 * 1000;
    if (options.maxConcurrentSpawns !== undefined) {
        setMaxConcurrentSpawns(options.maxConcurrentSpawns);
    }
    const relay = new ClarificationRelay(callbacks.onClarification);
    const results = [];
    const priorPhases = [];
    for (let i = 0; i < phases.length; i++) {
        const phase = phases[i];
        const nextPhase = phases[i + 1];
        const profile = PHASE_TO_PROFILE[phase];
        callbacks.onPhaseStart(phase, profile);
        // eslint-disable-next-line no-await-in-loop
        const phaseSucceeded = await executePhaseWithRetries({ cwd, taskId, runId, goal, phase, profile, runtime, gates, maxPhaseMs, maxRetries }, priorPhases, results, relay, callbacks);
        if (shouldPauseAtGate(phaseSucceeded, phase, nextPhase, gates, callbacks))
            break;
        if (!phaseSucceeded && gates !== 'none')
            break;
    }
    return results;
}
async function executePhaseWithRetries(ctx, priorPhases, results, relay, callbacks) {
    const { cwd, taskId, runId, goal, phase, profile, runtime, gates, maxPhaseMs, maxRetries } = ctx;
    let attempts = 0;
    while (attempts <= maxRetries) {
        attempts++;
        // eslint-disable-next-line no-await-in-loop
        const packet = await buildDelegationPacket({ cwd, taskId, runId, phase, goal, priorPhases });
        // eslint-disable-next-line no-await-in-loop
        const { exitCode, stdout, elapsedMs } = await spawnDelegated(runtime, packet, cwd, gates, maxPhaseMs);
        const tokenUsage = parseTokenUsage(stdout) ?? undefined;
        const response = parseSubAgentResponse(stdout, exitCode);
        if (!response) {
            const result = handleNoResponse({
                phase,
                profile,
                exitCode,
                stdout,
                elapsedMs,
                tokenUsage,
                attempts,
                maxRetries,
                priorPhases,
                results,
                callbacks,
            });
            if (result === 'continue')
                continue;
            return result;
        }
        // eslint-disable-next-line no-await-in-loop
        const structured = await handleStructuredResponse({
            response,
            phase,
            profile,
            exitCode,
            elapsedMs,
            tokenUsage,
            packet,
            priorPhases,
            results,
            relay,
            callbacks,
        });
        if (structured === 'retry')
            continue;
        return structured;
    }
    return false;
}
function handleNoResponse(opts) {
    const { phase, profile, exitCode, stdout, elapsedMs, tokenUsage, attempts, maxRetries, priorPhases, results, callbacks, } = opts;
    if (exitCode === 0) {
        const fallback = { type: 'deliverable', content: stdout.slice(-2000) };
        callbacks.onDeliverable(phase, fallback.content);
        callbacks.onPhaseComplete(phase, fallback);
        results.push({ phase, response: fallback, exitCode, elapsedMs, tokenUsage });
        priorPhases.push({ phase, status: 'done', profile });
        return true;
    }
    callbacks.onLog(`Phase ${phase} exited with code ${exitCode} and no structured response (attempt ${attempts}/${maxRetries + 1})`);
    if (attempts > maxRetries) {
        const blocked = { type: 'blocked', reason: `Exit code ${exitCode} after ${attempts} attempts` };
        callbacks.onBlocked(phase, blocked.reason);
        results.push({ phase, response: blocked, exitCode, elapsedMs, tokenUsage });
        priorPhases.push({ phase, status: 'failed', profile });
        return false;
    }
    return 'continue';
}
async function handleStructuredResponse(opts) {
    const { response, phase, profile, exitCode, elapsedMs, tokenUsage, packet, priorPhases, results, relay, callbacks } = opts;
    switch (response.type) {
        case 'deliverable': {
            const enriched = tokenUsage ? { ...response, tokenUsage } : response;
            callbacks.onDeliverable(phase, enriched.content, enriched.artifacts);
            callbacks.onPhaseComplete(phase, enriched);
            results.push({ phase, response: enriched, exitCode, elapsedMs, tokenUsage });
            priorPhases.push({ phase, status: 'done', profile });
            return true;
        }
        case 'clarification': {
            const answer = await relay.relay(response.question, phase, profile, response.options);
            packet.context.clarifications.push({ question: response.question, answer, fromRole: profile, toRole: 'user' });
            callbacks.onLog(`Clarification answered, retrying phase ${phase}`);
            return 'retry';
        }
        case 'blocked':
            callbacks.onBlocked(phase, response.reason);
            results.push({ phase, response, exitCode, elapsedMs, tokenUsage });
            priorPhases.push({ phase, status: 'blocked', profile });
            return false;
    }
}
function shouldPauseAtGate(phaseSucceeded, phase, nextPhase, gates, callbacks) {
    if (!phaseSucceeded || !nextPhase || gates === 'none')
        return false;
    const transitionKey = `${phase}→${nextPhase}`;
    const shouldPause = gates === 'all' || (gates === 'phase' && GATE_TRANSITIONS.has(transitionKey));
    if (shouldPause) {
        callbacks.onGatePause(phase, nextPhase);
        return true;
    }
    return false;
}
//# sourceMappingURL=workflow-orchestrator.js.map