/**
 * AgentQuestionSupervisorRouter
 *
 * Routes a freshly persisted {@link AgentQuestion} through the
 * supervisor pipeline (spec 093, task 31).
 *
 * Why this exists: the unified question pipeline (task 17) treats both
 * gate-emitted questions (task 20) and SDK V2 `AskUserQuestion`
 * questions identically. For non-gate questions we still want a
 * configured supervisor to evaluate the ask and — in autonomous mode —
 * answer it on the user's behalf. The router encapsulates the routing
 * rules so {@link AskAgentQuestionUseCase} stays small and so the
 * behaviour is unit-testable in isolation.
 *
 * Routing rules:
 *  - Skip when the question is gate-linked (`gateQuestionPublisher`
 *    will have raised a corresponding gate event already; routing the
 *    question too would double-evaluate).
 *  - Skip when `answerer === 'user'` — the user explicitly retains
 *    answer authority for these.
 *  - Resolve the effective {@link SupervisorPolicy} for the
 *    (appId, featureId) scope; if none is configured the call is a
 *    no-op (preserves NFR-14 default behaviour).
 *  - Evaluate the supervisor — the decision is always recorded so the
 *    audit / "Why?" drawer can show context even in advisory mode.
 *  - In **autonomous** mode AND when the verdict is `approve` /
 *    `reject`, settle the question via
 *    {@link AnswerAgentQuestionUseCase} with `actor =
 *    supervisor:<supervisorRunId>`. In **advisory** / **co-sign**
 *    modes the question stays pending — the user retains the final
 *    say (user always wins).
 *
 * Failures are swallowed: a supervisor outage MUST NOT prevent the
 * question from being persisted or answered later by the user.
 */
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { inject, injectable } from 'tsyringe';
import { randomUUID } from 'node:crypto';
import { AnswerAgentQuestionUseCase } from './answer-agent-question.use-case.js';
import { EvaluateSupervisorDecisionUseCase } from './evaluate-supervisor-decision.use-case.js';
import { GetSupervisorPolicyUseCase } from './get-supervisor-policy.use-case.js';
import { AgentQuestionAnswerer, SupervisorAutonomy, SupervisorVerdict, } from '../../../domain/generated/output.js';
const SUPERVISOR_QUESTION_PROMPT_PREFIX = 'gate.';
const APPROVE_ANSWER = 'approve';
const REJECT_ANSWER = 'reject';
let AgentQuestionSupervisorRouter = class AgentQuestionSupervisorRouter {
    getPolicy;
    evaluateDecision;
    answerAgentQuestion;
    constructor(getPolicy, evaluateDecision, answerAgentQuestion) {
        this.getPolicy = getPolicy;
        this.evaluateDecision = evaluateDecision;
        this.answerAgentQuestion = answerAgentQuestion;
    }
    /**
     * Route a freshly-persisted question through the supervisor.
     * Returns `{ evaluated: false, answered: false }` when the question is
     * skipped (user-only / no policy / failure).
     */
    async routeIfApplicable(question) {
        if (question.answerer === AgentQuestionAnswerer.user) {
            // User retains exclusive answer authority — never route to the
            // supervisor (acceptance criterion #3 for task 31).
            return { evaluated: false, answered: false };
        }
        if (isGateLinkedPrompt(question.prompt)) {
            // Gate-emitted question: the worker already routes the
            // corresponding gate event through the supervisor, so routing
            // here would double-evaluate.
            return { evaluated: false, answered: false };
        }
        const scopeType = question.appId ? 'app' : question.repositoryId ? 'repo' : 'global';
        const scopeId = question.appId ?? question.repositoryId;
        try {
            const policy = await this.getPolicy.execute({
                scopeType,
                scopeId,
                featureId: question.featureId,
            });
            if (!policy) {
                return { evaluated: false, answered: false };
            }
            const supervisorRunId = randomUUID();
            const result = await this.evaluateDecision.execute({
                event: {
                    kind: 'question',
                    scopeType,
                    scopeId,
                    featureId: question.featureId,
                    agentRunId: question.agentRunId,
                    questionId: question.id,
                    questionKind: question.kind,
                    prompt: question.prompt,
                    options: parseOptions(question.optionsJson),
                    sourceEventId: question.id,
                },
                supervisorRunId,
            });
            if (!result.evaluated || !result.decision) {
                return { evaluated: false, answered: false };
            }
            const verdict = result.decision.verdict;
            const answered = await this.maybeAnswer(question, policy.autonomyLevel, verdict, supervisorRunId);
            return {
                evaluated: true,
                effectiveAutonomy: policy.autonomyLevel,
                verdict,
                answered,
            };
        }
        catch {
            // Audit-log / supervisor outages must never prevent the
            // user-driven path from continuing.
            return { evaluated: false, answered: false };
        }
    }
    async maybeAnswer(question, autonomy, verdict, supervisorRunId) {
        if (autonomy !== SupervisorAutonomy.autonomous)
            return false;
        const answer = mapVerdictToAnswer(verdict);
        if (!answer)
            return false;
        // Respect the question's option set when present — refuse to write
        // an answer that would fail validation in AnswerAgentQuestion.
        const options = parseOptions(question.optionsJson);
        if (options && !options.includes(answer))
            return false;
        try {
            await this.answerAgentQuestion.execute({
                appId: question.appId ?? '',
                questionId: question.id,
                answer,
                answeredBy: `supervisor:${supervisorRunId}`,
            });
            return true;
        }
        catch {
            return false;
        }
    }
};
AgentQuestionSupervisorRouter = __decorate([
    injectable(),
    __param(2, inject(AnswerAgentQuestionUseCase)),
    __metadata("design:paramtypes", [GetSupervisorPolicyUseCase,
        EvaluateSupervisorDecisionUseCase,
        AnswerAgentQuestionUseCase])
], AgentQuestionSupervisorRouter);
export { AgentQuestionSupervisorRouter };
function parseOptions(optionsJson) {
    if (!optionsJson)
        return undefined;
    try {
        const parsed = JSON.parse(optionsJson);
        if (Array.isArray(parsed) && parsed.every((v) => typeof v === 'string')) {
            return parsed;
        }
    }
    catch {
        // Malformed JSON — treat as no options.
    }
    return undefined;
}
function mapVerdictToAnswer(verdict) {
    if (verdict === SupervisorVerdict.approve)
        return APPROVE_ANSWER;
    if (verdict === SupervisorVerdict.reject)
        return REJECT_ANSWER;
    return null;
}
function isGateLinkedPrompt(prompt) {
    // The gate-question publisher (task 20) writes its prompt as a JSON
    // object containing event=waiting_approval. We use that as the
    // discriminator rather than introducing a column on the question
    // schema purely for routing.
    if (typeof prompt !== 'string')
        return false;
    const trimmed = prompt.trim();
    if (trimmed.startsWith(SUPERVISOR_QUESTION_PROMPT_PREFIX))
        return true;
    if (!trimmed.startsWith('{'))
        return false;
    try {
        const parsed = JSON.parse(trimmed);
        return parsed?.event === 'waiting_approval';
    }
    catch {
        return false;
    }
}
