"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveEscalation = resolveEscalation;
const escalationService = __importStar(require("../../services/escalation"));
const taskService = __importStar(require("../../services/task"));
const publish_1 = require("../../lib/events/publish");
const escalation_strategy_1 = require("../../services/escalation-strategy");
const ephemeral_1 = require("../../services/iam/ephemeral");
const deployer_1 = require("../../services/yaml-workflow/deployer");
const workers_1 = require("../../workers");
const defaults_1 = require("../../modules/defaults");
// ── Orchestrator ─────────────────────────────────────────────────────────
/**
 * Resolve a pending escalation with a human-provided payload.
 *
 * Handles multiple resolution paths:
 * 1. **Condition signal** — lightweight `conditionLT` signal via metadata.signal_id
 * 2. **Signal-routed** — full signal_routing via YAML engine or Durable handle
 * 3. **Strategy triage** — escalation strategy redirects to a triage workflow
 * 4. **Notification-only** — no workflow_type; acknowledge and close
 * 5. **Re-run** — restart the original workflow with resolver data injected
 *
 * Password fields in the resolver payload are replaced with ephemeral
 * tokens (15-minute TTL) so plaintext never enters the signal store.
 */
async function resolveEscalation(input, _auth) {
    try {
        const { id, resolverPayload } = input;
        if (!resolverPayload) {
            return { status: 400, error: 'resolverPayload is required' };
        }
        const escalation = await escalationService.getEscalation(id);
        if (!escalation)
            return { status: 404, error: 'Escalation not found' };
        if (escalation.status !== 'pending')
            return { status: 409, error: 'Escalation not available for resolution' };
        // Path A: conditionLT signal
        const metadataSignalId = escalation.metadata?.signal_id;
        if (metadataSignalId && escalation.workflow_id && escalation.task_queue && escalation.workflow_type) {
            return resolveViaConditionSignal(escalation, resolverPayload);
        }
        // Path B: waitFor signal routing
        const signalRouting = escalation.metadata?.signal_routing;
        if (signalRouting?.signalId) {
            return resolveViaSignalRouting(escalation, resolverPayload);
        }
        // Path C: escalation strategy may redirect to triage
        const envelope = await reconstructEnvelope(escalation);
        const strategy = escalation_strategy_1.escalationStrategyRegistry.current;
        if (strategy) {
            const directive = await strategy.onResolution({ escalation, resolverPayload, envelope });
            if (directive.action === 'triage') {
                return resolveViaTriage(escalation, resolverPayload, directive.triageEnvelope);
            }
        }
        // Path D: notification-only — no workflow to restart
        if (!escalation.workflow_type || !escalation.task_queue) {
            await escalationService.resolveEscalation(escalation.id, resolverPayload);
            return { status: 200, data: { acknowledged: true, escalationId: escalation.id } };
        }
        // Path E: standard re-run
        return resolveViaRerun(escalation, envelope, resolverPayload);
    }
    catch (err) {
        return { status: 500, error: err.message };
    }
}
// ── Resolution paths ─────────────────────────────────────────────────────
/** Path A: lightweight conditionLT signal — inject $escalation_id and signal the running workflow. */
async function resolveViaConditionSignal(escalation, resolverPayload) {
    const signalId = escalation.metadata.signal_id;
    const client = (0, workers_1.createClient)();
    const handle = await client.workflow.getHandle(escalation.task_queue, escalation.workflow_type, escalation.workflow_id);
    await handle.signal(signalId, { ...resolverPayload, $escalation_id: escalation.id });
    publishResolvedEvent(escalation);
    return signaledResult(escalation, escalation.workflow_id);
}
/** Path B: waitFor signal escalation — signal via YAML engine or Durable handle. */
async function resolveViaSignalRouting(escalation, resolverPayload) {
    const signalRouting = escalation.metadata.signal_routing;
    const signalPayload = await redactPasswords(resolverPayload, escalation.metadata?.form_schema);
    if (signalRouting.engine === 'yaml' && signalRouting.hookTopic && signalRouting.appId) {
        const engine = await (0, deployer_1.getEngine)(signalRouting.appId);
        await engine.signal(signalRouting.hookTopic, {
            ...signalPayload,
            escalationId: escalation.id,
            job_id: signalRouting.jobId,
        });
    }
    else if (signalRouting.workflowId) {
        const client = (0, workers_1.createClient)();
        const handle = await client.workflow.getHandle(signalRouting.taskQueue, signalRouting.workflowType, signalRouting.workflowId);
        await handle.signal(signalRouting.signalId, signalPayload);
    }
    // YAML workflows resolve transactionally inside the workflow; only resolve here for Durable
    if (signalRouting.engine !== 'yaml') {
        await escalationService.resolveEscalation(escalation.id, resolverPayload);
    }
    publishResolvedEvent(escalation, {
        workflowId: escalation.workflow_id || signalRouting.workflowId,
        workflowName: escalation.workflow_type || signalRouting.workflowType,
        taskQueue: escalation.task_queue || signalRouting.taskQueue || signalRouting.appId,
        status: signalRouting.engine === 'yaml' ? 'signaled' : 'resolved',
    });
    return signaledResult(escalation, signalRouting.workflowId || signalRouting.appId);
}
/** Path C: escalation strategy directed triage — start a triage workflow. */
async function resolveViaTriage(escalation, resolverPayload, triageEnvelope) {
    const triageWorkflowId = `triage-${escalation.id}-${Date.now()}`;
    const client = (0, workers_1.createClient)();
    await taskService.createTask({
        workflow_id: triageWorkflowId,
        workflow_type: 'mcpTriageRouter',
        lt_type: 'mcpTriage',
        task_queue: 'long-tail-system',
        signal_id: `lt-triage-${triageWorkflowId}`,
        parent_workflow_id: triageWorkflowId,
        origin_id: escalation.origin_id || triageWorkflowId,
        parent_id: escalation.parent_id ?? undefined,
        envelope: JSON.stringify(triageEnvelope),
    });
    await client.workflow.start({
        workflowName: 'mcpTriageRouter',
        args: [triageEnvelope],
        taskQueue: 'long-tail-system',
        workflowId: triageWorkflowId,
        expire: defaults_1.JOB_EXPIRE_SECS,
        entity: 'mcpTriageRouter',
        signalIn: false,
    });
    await escalationService.resolveEscalation(escalation.id, {
        ...resolverPayload,
        _lt: { ...resolverPayload._lt, triaged: true, triageWorkflowId },
    });
    publishResolvedEvent(escalation);
    return {
        status: 200,
        data: { started: true, escalationId: escalation.id, workflowId: triageWorkflowId, triage: true },
    };
}
/** Path E: standard re-run — inject resolver data and restart the original workflow. */
async function resolveViaRerun(escalation, envelope, resolverPayload) {
    envelope.resolver = resolverPayload;
    envelope.lt = { ...envelope.lt, escalationId: escalation.id };
    const newWorkflowId = `rerun-${escalation.id}-${Date.now()}`;
    const client = (0, workers_1.createClient)();
    await client.workflow.start({
        workflowName: escalation.workflow_type,
        args: [envelope],
        taskQueue: escalation.task_queue,
        workflowId: newWorkflowId,
        expire: 180,
    });
    publishResolvedEvent(escalation);
    return {
        status: 200,
        data: { started: true, escalationId: escalation.id, workflowId: newWorkflowId },
    };
}
// ── Shared helpers ───────────────────────────────────────────────────────
function publishResolvedEvent(escalation, overrides) {
    (0, publish_1.publishEscalationEvent)({
        type: 'escalation.resolved',
        source: 'api',
        workflowId: overrides?.workflowId || escalation.workflow_id || '',
        workflowName: overrides?.workflowName || escalation.workflow_type || '',
        taskQueue: overrides?.taskQueue || escalation.task_queue || '',
        taskId: escalation.task_id,
        escalationId: escalation.id,
        originId: escalation.origin_id ?? undefined,
        status: overrides?.status || 'resolved',
    });
}
function signaledResult(escalation, workflowId) {
    return {
        status: 200,
        data: { signaled: true, escalationId: escalation.id, workflowId },
    };
}
/** Replace password fields with ephemeral tokens so plaintext never enters the signal store. */
async function redactPasswords(payload, formSchema) {
    if (!formSchema?.properties)
        return payload;
    const redacted = { ...payload };
    for (const [key, def] of Object.entries(formSchema.properties)) {
        if (def?.format === 'password' && typeof redacted[key] === 'string') {
            const uuid = await (0, ephemeral_1.storeEphemeral)(redacted[key], { ttlSeconds: 900, label: key });
            redacted[key] = (0, ephemeral_1.formatEphemeralToken)(uuid, key);
        }
    }
    return redacted;
}
/** Reconstruct the original envelope from the escalation record or its task. */
async function reconstructEnvelope(escalation) {
    if (escalation.envelope) {
        try {
            return JSON.parse(escalation.envelope);
        }
        catch { /* use empty */ }
    }
    else if (escalation.task_id) {
        const task = await taskService.getTask(escalation.task_id);
        if (task?.envelope) {
            try {
                return JSON.parse(task.envelope);
            }
            catch { /* use empty */ }
        }
    }
    return {};
}
