"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveWorkerExecutionWorkflow = resolveWorkerExecutionWorkflow;
exports.withWorkflowPlanStepResultsById = withWorkflowPlanStepResultsById;
const catalog_1 = require("../../i18n/catalog");
const types_1 = require("../../worker/types");
const plan_progress_1 = require("../plan-progress");
const errors_1 = require("./errors");
const plan_patch_1 = require("./plan-patch");
const plan_support_1 = require("./plan-support");
function resolveWorkerExecutionWorkflow(input, decision, locale = catalog_1.DEFAULT_APP_LOCALE) {
    const contextWorkflow = input.workflow;
    if (contextWorkflow === null) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.requiresDecision", {
            expectedMode: types_1.WORKER_RUN_MODES.workflowExecute,
        }));
    }
    const expectedMode = contextWorkflow.mode;
    const decisionWorkflow = decision.workflow;
    if (!decisionWorkflow) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.requiresDecision", { expectedMode }));
    }
    if (decisionWorkflow.mode !== expectedMode) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.modeMismatch", {
            expectedMode,
            receivedMode: decisionWorkflow.mode,
        }));
    }
    if (decisionWorkflow.templateName !== input.workflowTemplateName) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.templateMismatch", {
            expectedTemplate: input.workflowTemplateName ??
                (0, catalog_1.translate)(locale, "workflow.runContext.none"),
            receivedTemplate: decisionWorkflow.templateName ??
                (0, catalog_1.translate)(locale, "workflow.runContext.none"),
        }));
    }
    const resolvedWorkflowState = (() => {
        if (expectedMode === types_1.WORKER_RUN_MODES.workflowPlan) {
            return resolveWorkerPlanningWorkflowState(input, decision, locale);
        }
        if (expectedMode === types_1.WORKER_RUN_MODES.workflowReconcileStepBatch) {
            return resolveWorkerStepBatchReconciliationWorkflowState(input, decision, locale);
        }
        if (expectedMode === types_1.WORKER_RUN_MODES.workflowClosing) {
            return resolveWorkerClosingWorkflowState(input, locale);
        }
        return resolveWorkerExecuteWorkflowState(input, decision, locale);
    })();
    return {
        workflowTemplateName: decisionWorkflow.templateName,
        workflowState: resolvedWorkflowState,
    };
}
function resolveWorkerPlanningWorkflowState(input, decision, locale) {
    if (decision.outcome !== types_1.WORKER_OUTCOMES.proceed) {
        const workflowState = input.workflowState;
        if (workflowState === null) {
            throw new Error((0, catalog_1.translate)(locale, "workflow.state.invalidDecisionState"));
        }
        return workflowState;
    }
    const previousState = input.workflowState;
    const planningIntent = input.workflow?.planningIntent ?? "new_plan";
    if (planningIntent === "replan" || planningIntent === "revise_plan") {
        return resolveWorkerReplanningWorkflowState({
            previousState,
            decision,
            planningIntent,
            locale,
        });
    }
    const metadata = resolvePlanningProceedMetadata(previousState, decision, locale);
    return {
        ...metadata.previousState,
        executionContract: metadata.contract,
        priorityRank: decision.priorityRank ?? metadata.previousState.priorityRank ?? null,
        plan: normalizePlanningDecisionPlan(metadata.plan),
        planRevision: metadata.previousState.planRevision,
        finalResult: null,
        executionGuidance: [...metadata.executionGuidance],
        predictedTouchedPaths: metadata.predictedTouchedPaths,
    };
}
function resolveWorkerReplanningWorkflowState(input) {
    if (input.previousState === null) {
        throw new Error((0, catalog_1.translate)(input.locale, "workflow.state.previousPlanRequired"));
    }
    if (input.decision.planPatch === undefined ||
        input.decision.planPatch === null) {
        throw new errors_1.WorkflowDecisionValidationError((0, catalog_1.translate)(input.locale, "workflow.state.requiredMetadataBeforeQueue"), {
            code: errors_1.WORKFLOW_DECISION_VALIDATION_ERROR_CODES.planRequiredBeforeQueue,
            path: ["planPatch"],
            repairEligible: true,
        });
    }
    const patchedState = (0, plan_patch_1.applyWorkflowPlanPatch)({
        workflowState: input.previousState,
        patch: input.decision.planPatch,
        locale: input.locale,
    });
    const nextState = {
        ...patchedState,
        executionContract: input.decision.contract === undefined
            ? patchedState.executionContract
            : buildExecutionContractFromDecision({
                contract: input.decision.contract,
            }),
        priorityRank: input.decision.priorityRank ?? patchedState.priorityRank ?? null,
        predictedTouchedPaths: input.decision.predictedTouchedPaths === undefined
            ? patchedState.predictedTouchedPaths
            : input.decision.predictedTouchedPaths,
    };
    if (input.decision.executionGuidance !== undefined) {
        nextState.executionGuidance = [...input.decision.executionGuidance];
    }
    else if (patchedState.executionGuidance !== undefined) {
        nextState.executionGuidance = patchedState.executionGuidance;
    }
    return nextState;
}
function resolvePlanningProceedMetadata(previousState, decision, locale) {
    const missingMetadataField = resolveMissingPlanningMetadataField(previousState, decision);
    if (missingMetadataField !== null) {
        throw new errors_1.WorkflowDecisionValidationError((0, catalog_1.translate)(locale, "workflow.state.requiredMetadataBeforeQueue"), {
            code: errors_1.WORKFLOW_DECISION_VALIDATION_ERROR_CODES.planRequiredBeforeQueue,
            path: [missingMetadataField],
            repairEligible: true,
        });
    }
    const contract = decision.contract;
    return {
        previousState: previousState,
        contract: buildExecutionContractFromDecision({
            contract,
        }),
        plan: decision.plan,
        executionGuidance: decision.executionGuidance,
        predictedTouchedPaths: decision.predictedTouchedPaths,
    };
}
function buildExecutionContractFromDecision(input) {
    const contract = {
        objective: input.contract.objective,
        doneWhen: [...input.contract.doneWhen],
        inputs: [...input.contract.inputs],
        outputs: [...input.contract.outputs],
    };
    if (input.contract.instructions !== undefined &&
        input.contract.instructions.length > 0) {
        contract.instructions = [...input.contract.instructions];
    }
    return contract;
}
function resolveMissingPlanningMetadataField(previousState, decision) {
    if (previousState === null) {
        return "workflow";
    }
    if (decision.issueTitle === undefined) {
        return "issueTitle";
    }
    if (decision.contract === undefined) {
        return "contract";
    }
    if (decision.plan === undefined || decision.plan === null) {
        return "plan";
    }
    if (decision.executionGuidance === undefined) {
        return "executionGuidance";
    }
    if (decision.predictedTouchedPaths === undefined) {
        return "predictedTouchedPaths";
    }
    return null;
}
function resolveWorkerExecuteWorkflowState(input, decision, locale) {
    const previousState = input.workflowState;
    if (previousState?.plan === null || previousState === null) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.previousPlanRequired"));
    }
    if (decision.outcome === types_1.WORKER_OUTCOMES.proceed &&
        (decision.continuation?.strategy === "replan_same_issue" ||
            decision.continuation?.strategy === "spawn_child_issue")) {
        return previousState;
    }
    const currentStepId = input.workflow?.currentStepId ?? null;
    if (currentStepId === null) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.currentStepRequired"));
    }
    const nextPlan = resolveExecuteDecisionPlan(previousState.plan, decision, currentStepId, locale);
    return {
        ...previousState,
        priorityRank: previousState.priorityRank ?? null,
        plan: nextPlan,
    };
}
function resolveWorkerClosingWorkflowState(input, locale) {
    if (input.workflowState === null) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.previousPlanRequired"));
    }
    return input.workflowState;
}
function normalizePlanningDecisionPlan(plan) {
    const steps = plan.steps.map((step) => (0, plan_support_1.normalizeWorkflowStep)({
        ...step,
        ...(step.kind === "child_issue" && step.childIssueRef === undefined
            ? { childIssueRef: null }
            : {}),
        result: null,
    }));
    return {
        steps,
    };
}
function resolveExecuteDecisionPlan(previousPlan, decision, currentStepId, locale) {
    const selectedStep = (0, plan_progress_1.findWorkflowStepById)(previousPlan, currentStepId);
    if (selectedStep === null) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.previousPlanRequired"));
    }
    if (selectedStep.kind === "child_issue") {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.previousPlanRequired"));
    }
    if (decision.outcome !== types_1.WORKER_OUTCOMES.proceed &&
        decision.outcome !== types_1.WORKER_OUTCOMES.fail &&
        decision.outcome !== types_1.WORKER_OUTCOMES.waitHuman) {
        return previousPlan;
    }
    return (0, plan_progress_1.withWorkflowPlanStepResultById)(previousPlan, selectedStep.id, (0, plan_support_1.buildWorkflowResultFromDecision)(decision));
}
function resolveWorkerStepBatchReconciliationWorkflowState(input, decision, locale) {
    const previousState = input.workflowState;
    if (previousState?.plan === null || previousState === null) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.previousPlanRequired"));
    }
    if (decision.stepResults === undefined || decision.stepResults.length === 0) {
        throw new Error((0, catalog_1.translate)(locale, "workflow.state.invalidDecisionState"));
    }
    return {
        ...previousState,
        priorityRank: previousState.priorityRank ?? null,
        plan: withWorkflowPlanStepResultsById(previousState.plan, decision.stepResults.filter((stepResult) => stepResult.outcome === types_1.WORKER_OUTCOMES.proceed)),
    };
}
function withWorkflowPlanStepResultsById(plan, stepResults) {
    const resultByStepId = new Map(stepResults.map((stepResult) => [
        stepResult.stepId,
        {
            outcome: stepResult.outcome,
            ...stepResult.result,
        },
    ]));
    return {
        steps: plan.steps.map((step) => {
            const result = resultByStepId.get(step.id);
            return result === undefined ? step : { ...step, result };
        }),
    };
}
