"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.registerLT = registerLT;
exports.createLTInterceptor = createLTInterceptor;
const hotmesh_1 = require("@hotmeshio/hotmesh");
const interceptorActivities = __importStar(require("./activities"));
const activity_interceptor_1 = require("./activity-interceptor");
const context_1 = require("./context");
const state_1 = require("./state");
const escalation_1 = require("./escalation");
const completion_1 = require("./completion");
const lifecycle_1 = require("./lifecycle");
const context_2 = require("../iam/context");
const envelope_1 = require("../iam/envelope");
const DEFAULT_ACTIVITY_QUEUE = 'lt-interceptor';
/**
 * Register the Long Tail interceptors in a single call.
 *
 * This registers:
 * 1. The shared activity worker for interceptor DB operations
 * 2. The workflow interceptor (escalation, routing, re-runs, composition)
 * 3. The activity interceptor (milestone event publishing)
 */
async function registerLT(connection, options) {
    const taskQueue = options?.taskQueue ?? DEFAULT_ACTIVITY_QUEUE;
    await hotmesh_1.Durable.registerActivityWorker({ connection, taskQueue, guid: `interceptor::${taskQueue}-${hotmesh_1.Durable.guid()}` }, interceptorActivities, taskQueue);
    hotmesh_1.Durable.registerInboundInterceptor(createLTInterceptor({
        activityTaskQueue: taskQueue,
        defaultRole: options?.defaultRole,
    }));
    hotmesh_1.Durable.registerOutboundInterceptor((0, activity_interceptor_1.createLTActivityInterceptor)());
}
/**
 * The Long Tail interceptor wraps every registered workflow.
 *
 * Every workflow in lt_config_workflows gets the same unified treatment:
 *
 * 1. **Orchestrator context** — `next()` is always wrapped in
 *    `runWithOrchestratorContext` so that ANY workflow can compose
 *    children via `executeLT`. If it doesn't, the context is unused.
 *
 * 2. **Task tracking** — Every execution is tied to a task record
 *    for lifecycle tracking and origin lineage.
 *
 * 3. **Escalation routing** — On escalation or error, the workflow
 *    ENDS after creating an escalation record. When a human resolves,
 *    a NEW workflow is started with resolver data. The interceptor
 *    detects re-runs via `envelope.resolver` + `envelope.lt.escalationId`.
 *
 * 4. **Parent signaling** — On completion, signals back to the parent
 *    orchestrator (if any) so the composition chain continues.
 *
 * Routing info is read from the task record's metadata (set by executeLT)
 * rather than from workflow arguments, since the interceptor ctx does not
 * expose workflow function arguments directly.
 */
function createLTInterceptor(options) {
    const { activityTaskQueue, defaultRole = 'reviewer', } = options;
    const interceptor = {
        async execute(ctx, next) {
            // 1. Extract workflow identity and proxy activities
            const wf = (0, lifecycle_1.extractWorkflowIdentity)(ctx);
            const activities = hotmesh_1.Durable.workflow.proxyActivities({
                activities: interceptorActivities,
                taskQueue: activityTaskQueue,
                retry: { maximumAttempts: 3 },
            });
            const envelope = (0, state_1.extractEnvelope)(ctx);
            // 2. Fast path: certification is opt-in. Only workflows with
            //    metadata.certified === true pay for the config lookup,
            //    task creation, and escalation wiring.
            if (envelope?.metadata?.certified !== true) {
                const toolCtx = (0, envelope_1.buildToolContextFromEnvelope)(envelope, wf.workflowId, wf.workflowTrace, wf.workflowSpan);
                return toolCtx ? (0, context_2.runWithToolContext)(toolCtx, next) : next();
            }
            // 3. Load config — unregistered/uncertified workflows get ToolContext only
            const wfConfig = await activities.ltGetWorkflowConfig(wf.workflowName);
            if (!wfConfig) {
                const toolCtx = (0, envelope_1.buildToolContextFromEnvelope)(envelope, wf.workflowId, wf.workflowTrace, wf.workflowSpan);
                return toolCtx ? (0, context_2.runWithToolContext)(toolCtx, next) : next();
            }
            const taskQueue = (0, lifecycle_1.deriveTaskQueue)(wf);
            // 3. Find existing task and handle re-run escalation resolution
            const existingTask = await activities.ltGetTaskByWorkflowId(wf.workflowId);
            const reRun = await (0, lifecycle_1.resolveReRun)(activities, envelope, existingTask, wf, taskQueue);
            // 4. Ensure task exists with routing and origin lineage
            const { taskId, routing, originId } = await (0, lifecycle_1.ensureTaskWithRouting)(activities, wf, envelope, existingTask, taskQueue, reRun);
            // 5. Publish lifecycle events
            (0, lifecycle_1.publishStartedEvents)(wf, taskQueue, taskId, originId);
            // 6. Build state and execute workflow with interception
            const state = {
                workflowId: wf.workflowId,
                workflowName: wf.workflowName,
                taskQueue,
                wfConfig,
                defaultRole,
                taskId,
                routing,
                envelope,
                isReRun: reRun.isReRun,
                activities,
                traceId: wf.workflowTrace,
                spanId: wf.workflowSpan,
            };
            try {
                const orchCtx = { workflowId: wf.workflowId, taskQueue, workflowType: wf.workflowName, userId: envelope?.lt?.userId };
                // Build ToolContext from envelope principal (no DB call — resolved at front door)
                const toolCtx = (0, envelope_1.buildToolContextFromEnvelope)(envelope, wf.workflowId, wf.workflowTrace, wf.workflowSpan);
                // Nest both contexts: OrchestratorContext (for executeLT routing)
                // and ToolContext (for universal identity access in activities)
                const wrappedNext = toolCtx
                    ? () => (0, context_2.runWithToolContext)(toolCtx, next)
                    : next;
                const result = await (0, context_1.runWithOrchestratorContext)(orchCtx, wrappedNext);
                if (result?.type === 'escalation') {
                    return (0, escalation_1.handleEscalation)(state, result);
                }
                if (result?.type === 'return') {
                    return (0, completion_1.handleCompletion)(state, result);
                }
                // Plain return (e.g., orchestrators returning raw results)
                await (0, lifecycle_1.completePlainResult)(activities, wf, taskQueue, taskId, routing, result);
                return result;
            }
            catch (err) {
                if (hotmesh_1.Durable.workflow.didInterrupt(err))
                    throw err;
                return (0, escalation_1.handleErrorEscalation)(state, err);
            }
        },
    };
    return interceptor;
}
