"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeControllerChildIssueRef = void 0;
exports.resolveControllerChildCanonicalWorkflowState = resolveControllerChildCanonicalWorkflowState;
exports.loadControllerChildCanonicalStateMap = loadControllerChildCanonicalStateMap;
exports.loadControllerChildIssueLookup = loadControllerChildIssueLookup;
exports.buildControllerChildIssueLookup = buildControllerChildIssueLookup;
exports.resolveControllerChildIdentity = resolveControllerChildIdentity;
exports.isControllerChildForParent = isControllerChildForParent;
exports.buildControllerChildDesiredSpec = buildControllerChildDesiredSpec;
exports.buildControllerChildDesiredSpecForStep = buildControllerChildDesiredSpecForStep;
exports.buildControllerChildWorkflowState = buildControllerChildWorkflowState;
exports.resolveControllerChildFollowupState = resolveControllerChildFollowupState;
exports.resolveStoredControllerChildFollowupState = resolveStoredControllerChildFollowupState;
exports.buildDesiredControllerChildDriftFingerprint = buildDesiredControllerChildDriftFingerprint;
exports.buildExistingControllerChildDriftFingerprint = buildExistingControllerChildDriftFingerprint;
exports.resolveControllerChildTemplate = resolveControllerChildTemplate;
exports.resolveControllerChildMilestoneRef = resolveControllerChildMilestoneRef;
exports.resolveControllerChildProjectRef = resolveControllerChildProjectRef;
exports.isStartedControllerChildIssue = isStartedControllerChildIssue;
exports.buildControllerChildContractState = buildControllerChildContractState;
exports.hasControllerChildParentStepContractSnapshotDrift = hasControllerChildParentStepContractSnapshotDrift;
exports.createControllerChildDependencyStepResolver = createControllerChildDependencyStepResolver;
exports.resolveControllerChildIssueSteps = resolveControllerChildIssueSteps;
exports.selectNextReadyControllerChildIssueStep = selectNextReadyControllerChildIssueStep;
exports.selectReadyControllerChildIssueSteps = selectReadyControllerChildIssueSteps;
exports.attachControllerChildIssueRefsToWorkflowState = attachControllerChildIssueRefsToWorkflowState;
const catalog_1 = require("../../../i18n/catalog");
const workflow_labels_1 = require("../../../platform/ticket-tracker/workflow-labels");
const types_1 = require("../../../platform/workflow-state/types");
const issue_ref_1 = require("../../../workflows/issue-ref");
const plan_progress_1 = require("../../../workflows/plan-progress");
const state_1 = require("../../../workflows/state");
const template_bindings_1 = require("../../../workflows/template-bindings");
const types_2 = require("../../../workflows/types");
const child_issue_title_1 = require("../child-issue-title");
const child_workflow_state_1 = require("../child-workflow-state");
async function resolveControllerChildCanonicalWorkflowState(input) {
    return (0, child_workflow_state_1.resolveChildCanonicalWorkflowState)(input);
}
async function loadControllerChildCanonicalStateMap(input) {
    return (0, child_workflow_state_1.loadChildCanonicalStateMap)(input);
}
async function loadControllerChildIssueLookup(input) {
    return buildControllerChildIssueLookup({
        issues: input.issues,
        workflowStateByIssueId: await loadControllerChildCanonicalStateMap(input),
    });
}
function buildControllerChildIssueLookup(input) {
    const issueByChildKey = new Map();
    const workflowStateByChildKey = new Map();
    for (const issue of input.issues) {
        const workflowState = input.workflowStateByIssueId.get(issue.id) ?? null;
        const { childKey } = resolveControllerChildIdentity(workflowState);
        if (childKey === null) {
            continue;
        }
        issueByChildKey.set(childKey, issue);
        if (workflowState !== null) {
            workflowStateByChildKey.set(childKey, workflowState);
        }
    }
    return {
        issueByChildKey,
        workflowStateByChildKey,
        workflowStateByIssueId: input.workflowStateByIssueId,
    };
}
function resolveControllerChildIdentity(workflowState) {
    if (workflowState?.workflowSurface !== "issue") {
        return { childKey: null, parentRef: null };
    }
    return (0, child_workflow_state_1.resolveChildIssueIdentity)(workflowState);
}
function isControllerChildForParent(input) {
    if (input.workflowState?.detachedFromParentReason != null) {
        return false;
    }
    const { childKey, parentRef } = resolveControllerChildIdentity(input.workflowState);
    return (childKey !== null &&
        (input.childKey === undefined || childKey === input.childKey) &&
        (0, issue_ref_1.workflowIssueRefsMatch)(parentRef, input.parentDisplayId));
}
exports.normalizeControllerChildIssueRef = issue_ref_1.normalizeWorkflowIssueDisplayRef;
function buildControllerChildDesiredSpec(input) {
    const template = resolveControllerChildTemplate(input.childIssueStep, input.catalog);
    const milestoneRef = resolveControllerChildMilestoneRef(input.parentIssue, input.childIssueStep);
    const projectRef = resolveControllerChildProjectRef(input.parentIssue, input.childIssueStep);
    return {
        childKey: input.childIssueStep.childKey,
        title: (0, child_issue_title_1.appendParentIssueTitleSuffix)(input.childIssueStep.title, input.parentIssue.displayId),
        issueBody: buildControllerChildIssueBody({
            responseLanguage: input.responseLanguage,
            parentIssue: input.parentIssue,
            childIssueStep: input.childIssueStep,
        }),
        assignees: [input.assignee],
        labelNames: [input.workflowLabels.aiReady],
        milestoneRef,
        projectRef,
        priorityRank: input.parentWorkflowState?.priorityRank,
        workflowTemplates: buildControllerChildWorkflowTemplates({
            catalog: input.catalog,
            parentWorkflowState: input.parentWorkflowState,
            template,
        }),
        baseBranch: input.baseBranch,
        configOverrides: cloneConfigOverrides(input.parentWorkflowState?.configOverrides),
        consecutiveAiRuns: 0,
        totalAiRunHours: 0,
        lastScannedCommentCursor: null,
        parentRef: input.parentIssue.displayId,
        contract: buildControllerChildContractState(input.childIssueStep),
        parentContractRevision: input.parentWorkflowState?.planRevision ?? 0,
        parentStepContractSnapshot: buildControllerChildContractState(input.childIssueStep),
        dependsOn: resolveControllerChildDependencyKeys(input.childIssueStep, input.childIssueSteps, input.allWorkflowSteps ??
            input.allChildIssueSteps ??
            input.childIssueSteps),
        followup: input.followup,
    };
}
function buildControllerChildDesiredSpecForStep(input) {
    const allWorkflowSteps = input.parentWorkflowState?.plan?.steps;
    const desiredSpecInput = {
        responseLanguage: input.responseLanguage,
        parentIssue: input.parentIssue,
        parentWorkflowState: input.parentWorkflowState,
        followup: input.followup,
        baseBranch: input.baseBranch,
        childIssueStep: input.childIssueStep,
        childIssueSteps: input.childIssueSteps,
        allChildIssueSteps: input.allChildIssueSteps ??
            resolveControllerChildIssueSteps(input.parentWorkflowState),
        ...(allWorkflowSteps !== undefined ? { allWorkflowSteps } : {}),
        catalog: input.catalog,
        workflowLabels: input.workflowLabels,
        assignee: input.assignee,
    };
    return buildControllerChildDesiredSpec({
        ...desiredSpecInput,
    });
}
function buildControllerChildWorkflowState(input) {
    return (0, state_1.buildPersistedWorkflowState)({
        workflowSurface: "issue",
        workflowTemplates: input.desiredSpec.workflowTemplates,
        baseBranch: input.desiredSpec.baseBranch,
        parentLink: {
            parentRef: input.desiredSpec.parentRef,
            childKey: input.desiredSpec.childKey,
        },
        executionContract: cloneControllerChildContract(input.desiredSpec.contract),
        parentContractRevision: input.desiredSpec.parentContractRevision,
        parentStepContractSnapshot: cloneControllerChildContract(input.desiredSpec.parentStepContractSnapshot),
        priorityRank: input.desiredSpec.priorityRank ?? null,
        consecutiveAiRuns: input.desiredSpec.consecutiveAiRuns,
        totalAiRunHours: input.desiredSpec.totalAiRunHours,
        lastScannedCommentCursor: input.desiredSpec.lastScannedCommentCursor,
        ...(input.desiredSpec.configOverrides !== undefined
            ? {
                configOverrides: input.desiredSpec.configOverrides,
            }
            : {}),
        followup: input.desiredSpec.followup,
        plan: null,
    });
}
function resolveControllerChildFollowupState(input) {
    if (input.childIssuePolicy === "off") {
        return (0, types_2.createOffWorkflowFollowupState)();
    }
    const parentFollowup = resolveStoredControllerChildFollowupState(input.parentWorkflowState.followup);
    return {
        proposalPolicy: parentFollowup.proposalPolicy,
        proposalApproval: parentFollowup.proposalApproval,
    };
}
function resolveStoredControllerChildFollowupState(followup) {
    return followup ?? (0, types_2.createOffWorkflowFollowupState)();
}
function buildDesiredControllerChildDriftFingerprint(desiredSpec, workflowLabels) {
    return buildControllerChildDriftFingerprint({
        title: desiredSpec.title,
        assignees: desiredSpec.assignees,
        labelNames: normalizeControllerChildLabelNames(desiredSpec.labelNames, workflowLabels),
        parentRef: desiredSpec.parentRef,
        workflowTemplates: desiredSpec.workflowTemplates,
        milestoneRef: desiredSpec.milestoneRef ?? null,
        projectRef: desiredSpec.projectRef ?? null,
        priorityRank: desiredSpec.priorityRank ?? null,
        configOverrides: desiredSpec.configOverrides,
        dependsOn: desiredSpec.dependsOn,
    });
}
function buildExistingControllerChildDriftFingerprint(input) {
    return buildControllerChildDriftFingerprint({
        title: input.existingIssue.baseTitle,
        assignees: input.existingIssue.assignees,
        labelNames: normalizeControllerChildLabelNames(input.existingIssue.labelNames ?? [], input.workflowLabels),
        parentRef: input.existingWorkflowState.parentLink?.parentRef ?? null,
        workflowTemplates: input.existingWorkflowState.workflowTemplates,
        milestoneRef: input.existingIssue.milestoneRef ?? null,
        projectRef: input.existingIssue.projectRef ?? null,
        priorityRank: input.existingWorkflowState.priorityRank ?? null,
        configOverrides: input.existingWorkflowState.configOverrides,
        dependsOn: input.currentDependencyKeys,
    });
}
function resolveControllerChildTemplate(childIssueStep, catalog) {
    const templatePath = childIssueStep.suggestedTemplate?.trim() ?? "";
    if (templatePath.length === 0) {
        return null;
    }
    const template = catalog?.templateByPath.get(templatePath) ?? null;
    if (template === null) {
        throw new Error(`Missing child workflow template: ${templatePath}`);
    }
    return template;
}
function buildControllerChildWorkflowTemplates(input) {
    const issueSelector = input.template === null
        ? { mode: "auto_select" }
        : {
            mode: "template",
            templatePath: input.template.templatePath,
        };
    const workflowTemplates = input.parentWorkflowState?.workflowTemplates ??
        (0, template_bindings_1.createUnresolvedWorkflowTemplateBindings)();
    return {
        ...workflowTemplates,
        issue: input.catalog === null
            ? (0, template_bindings_1.createWorkflowTemplateBinding)({
                selector: issueSelector,
                ...(input.template !== null
                    ? { resolution: (0, template_bindings_1.buildWorkflowTemplateResolution)(input.template) }
                    : {}),
            })
            : (0, template_bindings_1.resolveWorkflowTemplateBinding)({
                catalog: input.catalog,
                surface: "issue",
                selector: issueSelector,
            }),
    };
}
function resolveControllerChildMilestoneRef(parentIssue, childIssueStep) {
    return childIssueStep.bindingHints?.milestoneRef ?? parentIssue.milestoneRef;
}
function resolveControllerChildProjectRef(parentIssue, childIssueStep) {
    return childIssueStep.bindingHints?.projectRef ?? parentIssue.projectRef;
}
function isStartedControllerChildIssue(issue, workflowState) {
    if (workflowState !== undefined) {
        return !(0, state_1.hasPendingWorkflowStartApproval)(workflowState);
    }
    return issue.workflowState.status !== types_1.WORKFLOW_STATUSES.aiReady;
}
function buildControllerChildIssueBody(input) {
    const parentIssueLine = `${input.parentIssue.displayId} ${input.parentIssue.baseTitle}`.trim();
    const lines = [
        `## ${(0, catalog_1.translate)(input.responseLanguage, "run.controllerChildren.body.parentIssueLabel")}`,
        parentIssueLine,
        "",
        `## ${(0, catalog_1.translate)(input.responseLanguage, "run.controllerChildren.body.objectiveHeading")}`,
        input.childIssueStep.objective,
        "",
        ...(input.childIssueStep.instructions.length > 0
            ? [
                `## ${(0, catalog_1.translate)(input.responseLanguage, "run.controllerChildren.body.instructionsHeading")}`,
                ...renderControllerChildIssueList(input.childIssueStep.instructions),
                "",
            ]
            : []),
        `## ${(0, catalog_1.translate)(input.responseLanguage, "run.controllerChildren.body.doneWhenHeading")}`,
        ...renderControllerChildIssueList(input.childIssueStep.doneWhen),
        "",
        `## ${(0, catalog_1.translate)(input.responseLanguage, "run.controllerChildren.body.inputsHeading")}`,
        ...renderControllerChildIssueList(input.childIssueStep.inputs),
        "",
        `## ${(0, catalog_1.translate)(input.responseLanguage, "run.controllerChildren.body.outputsHeading")}`,
        ...renderControllerChildIssueList(input.childIssueStep.outputs),
    ];
    return lines.join("\n");
}
function renderControllerChildIssueList(values) {
    return values.length === 0 ? ["-"] : values.map((value) => `- ${value}`);
}
function buildControllerChildContractState(childIssueStep) {
    const contract = {
        objective: childIssueStep.objective,
        doneWhen: [...childIssueStep.doneWhen],
        inputs: [...childIssueStep.inputs],
        outputs: [...childIssueStep.outputs],
    };
    if (childIssueStep.instructions.length > 0) {
        contract.instructions = [...childIssueStep.instructions];
    }
    return contract;
}
function hasControllerChildParentStepContractSnapshotDrift(input) {
    const snapshot = input.childWorkflowState.parentStepContractSnapshot;
    if (snapshot === undefined) {
        return true;
    }
    return !areControllerChildContractsEqual(snapshot, buildControllerChildContractState(input.childIssueStep));
}
function areControllerChildContractsEqual(left, right) {
    return (left.objective === right.objective &&
        areStringListsEqual(left.doneWhen, right.doneWhen) &&
        areStringListsEqual(left.inputs, right.inputs) &&
        areStringListsEqual(left.outputs, right.outputs) &&
        (left.instructions === undefined ||
            areStringListsEqual(left.instructions, right.instructions ?? [])));
}
function areStringListsEqual(left, right) {
    return (left.length === right.length &&
        left.every((value, index) => value === right[index]));
}
function cloneControllerChildContract(contract) {
    const clonedContract = {
        objective: contract.objective,
        doneWhen: [...contract.doneWhen],
        inputs: [...contract.inputs],
        outputs: [...contract.outputs],
    };
    if (contract.instructions !== undefined && contract.instructions.length > 0) {
        clonedContract.instructions = [...contract.instructions];
    }
    return clonedContract;
}
function cloneConfigOverrides(configOverrides) {
    if (configOverrides === undefined) {
        return undefined;
    }
    return {
        entries: configOverrides.entries.map((entry) => ({ ...entry })),
    };
}
function normalizeControllerChildLabelNames(labelNames, workflowLabels) {
    const workflowLabelNames = new Set((0, workflow_labels_1.getNonFollowupManagedLabelNames)(workflowLabels));
    return labelNames.filter((labelName) => !workflowLabelNames.has(labelName));
}
function resolveControllerChildDependencyKeys(childIssueStep, childIssueSteps, allWorkflowSteps) {
    const resolveDependencySteps = createControllerChildDependencyStepResolver({
        childIssueSteps,
        allWorkflowSteps,
    });
    return resolveDependencySteps(childIssueStep).map((dependencyStep) => dependencyStep.childKey);
}
function createControllerChildDependencyStepResolver(input) {
    const childStepById = new Map(input.childIssueSteps.map((step) => [step.id, step]));
    const workflowStepById = new Map(input.allWorkflowSteps.map((step) => [step.id, step]));
    return (childIssueStep) => childIssueStep.dependsOnStepIds.flatMap((dependencyStepId) => {
        const workflowStep = workflowStepById.get(dependencyStepId);
        if (workflowStep === undefined) {
            throw new Error(`Missing controller child dependency step: ${dependencyStepId}`);
        }
        if (workflowStep.kind !== "child_issue") {
            return [];
        }
        const dependencyStep = childStepById.get(dependencyStepId);
        if (dependencyStep === undefined) {
            return [];
        }
        return [dependencyStep];
    });
}
function resolveControllerChildIssueSteps(workflowState) {
    if (workflowState?.plan === null || workflowState === null) {
        return [];
    }
    return workflowState.plan.steps.filter(types_2.isWorkflowChildIssueStep);
}
function selectNextReadyControllerChildIssueStep(workflowState) {
    if (workflowState?.plan === null || workflowState === null) {
        return null;
    }
    return (0, plan_progress_1.selectNextControllerChildIssueStep)(workflowState.plan);
}
function selectReadyControllerChildIssueSteps(workflowState) {
    if (workflowState?.plan === null || workflowState === null) {
        return [];
    }
    return (0, plan_progress_1.selectReadyControllerChildIssueSteps)(workflowState.plan);
}
function attachControllerChildIssueRefsToWorkflowState(input) {
    if (input.workflowState?.plan === null || input.workflowState === null) {
        return input.workflowState;
    }
    return {
        ...input.workflowState,
        plan: {
            ...input.workflowState.plan,
            steps: input.workflowState.plan.steps.map((step) => {
                if (step.kind !== "child_issue") {
                    return step;
                }
                const childIssueStep = step;
                const childIssue = input.childIssueByKey.get(childIssueStep.childKey);
                const startedChildWorkflowState = input.startedChildWorkflowStateByKey?.get(childIssueStep.childKey);
                const startedChildContract = startedChildWorkflowState?.executionContract;
                const nextStep = childIssue === undefined
                    ? step
                    : {
                        ...step,
                        childIssueRef: childIssue.displayId,
                    };
                if (startedChildContract === undefined) {
                    return nextStep;
                }
                return {
                    ...nextStep,
                    objective: startedChildContract.objective,
                    instructions: startedChildContract.instructions !== undefined
                        ? [...startedChildContract.instructions]
                        : [...nextStep.instructions],
                    doneWhen: [...startedChildContract.doneWhen],
                    inputs: [...startedChildContract.inputs],
                    outputs: [...startedChildContract.outputs],
                };
            }),
        },
    };
}
function buildControllerChildDriftFingerprint(input) {
    return JSON.stringify({
        title: input.title,
        assignees: [...input.assignees].sort(),
        labelNames: [...input.labelNames].sort(),
        parentRef: input.parentRef,
        workflowTemplates: input.workflowTemplates,
        milestoneRef: input.milestoneRef,
        projectRef: input.projectRef,
        priorityRank: input.priorityRank,
        configOverrides: input.configOverrides ?? null,
        dependsOn: [...input.dependsOn].sort(),
    });
}
