"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.publishControllerChildIssues = publishControllerChildIssues;
const types_1 = require("../../../platform/ticket-tracker/types");
const constants_1 = require("../../../worker/constants");
const plan_progress_1 = require("../../../workflows/plan-progress");
const publisher_shared_1 = require("../../../workflows/publisher-shared");
const recurring_1 = require("../../../workflows/recurring");
const template_registry_1 = require("../../../workflows/template-registry");
const workflow_structure_1 = require("../../workflow-structure");
const tracked_project_1 = require("../../../core/tracked-project");
const child_publication_1 = require("../child-publication");
const issue_branch_target_1 = require("../issue-branch-target");
const default_issue_workspace_manager_1 = require("../../../workspaces/default-issue-workspace-manager");
const shared_1 = require("../shared");
const transition_1 = require("../spawn-child/transition");
const unplanned_1 = require("../spawn-child/unplanned");
const utils_1 = require("../spawn-child/utils");
const transition_summary_1 = require("../transition-summary");
const transitions_1 = require("../transitions");
const workflow_template_root_1 = require("../workflow-template-root");
const workflow_closing_1 = require("../workflow-closing");
const match_1 = require("./match");
const reconcile_1 = require("./reconcile");
const shared_2 = require("./shared");
// biome-ignore lint/complexity/noExcessiveLinesPerFunction: Child controller publication keeps planning, dependency, and progress ordering together.
async function publishControllerChildIssues(input) {
    if (input.claimedIssue.originalIssue.kind !== types_1.TRACKER_ITEM_KINDS.issue ||
        input.workerExecution.workflowState?.workflowSurface !== "issue") {
        throw new Error("Controller child issues can only be published for issue workflow surfaces.");
    }
    const allChildIssueSteps = (0, shared_2.resolveControllerChildIssueSteps)(input.workerExecution.workflowState);
    const unresolvedChildIssueSteps = resolveUnresolvedSelectedChildIssueSteps(input.workerExecution.workflowState);
    const [liveBlockers, openIssues] = await Promise.all([
        input.context.ticketTracker.listBlockingDependencies(input.claimedIssue.originalIssue.id),
        input.context.ticketTracker.listIssues({
            state: "open",
        }),
    ]);
    const assignee = await (0, utils_1.resolveSingleServiceAssignee)(input.context.currentUserLogin, input.context.ticketTracker);
    const catalog = allChildIssueSteps.some((childIssueStep) => childIssueStep.result === null &&
        childIssueStep.suggestedTemplate !== undefined)
        ? await (0, template_registry_1.loadWorkflowTemplates)((0, template_registry_1.getWorkflowTemplatesDirectoryPath)((0, workflow_template_root_1.getWorkflowTemplateRepoRoot)(input.context)), input.workerExecution.responseLanguage)
        : null;
    const existingOpenChildren = await (0, match_1.resolveOpenControllerChildIssues)({
        ticketTracker: input.context.ticketTracker,
        openIssues,
        liveBlockers,
        parentIssue: input.workerExecution.issue,
        childIssueSteps: unresolvedChildIssueSteps,
    });
    const preReconciledState = await buildStartedConflictWorkerExecution({
        ticketTracker: input.context.ticketTracker,
        workerExecution: input.workerExecution,
        existingOpenChildren,
        childIssueSteps: unresolvedChildIssueSteps,
    });
    const unresolvedPreReconciledChildIssueSteps = resolveUnresolvedSelectedChildIssueSteps(preReconciledState.workerExecution.workflowState);
    const pendingChildIssueDecisionKeys = new Set((preReconciledState.workerExecution.workflowState
        ?.pendingChildIssueDecisions ?? []).map((decision) => decision.childKey));
    const childBaseBranch = (0, issue_branch_target_1.resolveIssueCreationBaseBranch)({
        branchTarget: "parent_issue_branch",
        config: input.context.config,
        parentIssue: input.workerExecution.issue,
        effectiveWorkflowState: preReconciledState.workerExecution.workflowState,
    });
    const startedChildConflict = await (0, match_1.resolveStartedControllerChildConflict)({
        ticketTracker: input.context.ticketTracker,
        responseLanguage: input.workerExecution.responseLanguage,
        parentIssue: input.workerExecution.issue,
        existingOpenChildren,
        childIssueSteps: unresolvedPreReconciledChildIssueSteps,
        allChildIssueSteps,
        skipChildKeys: pendingChildIssueDecisionKeys,
        catalog,
        parentWorkflowState: preReconciledState.workerExecution.workflowState,
        baseBranch: childBaseBranch,
        workflowLabels: input.workflowLabels,
        assignee,
    });
    if (startedChildConflict !== null) {
        return (0, transition_1.publishControllerChildNeedsHumanTransition)({
            ...input,
            workerExecution: preReconciledState.workerExecution,
            summary: (0, transition_1.buildStartedControllerChildConflictSummary)(input.workerExecution.responseLanguage, startedChildConflict.displayId),
        });
    }
    let reconciledChildren;
    try {
        reconciledChildren = await (0, reconcile_1.reconcileControllerChildIssues)({
            context: input.context,
            workflowLabels: input.workflowLabels,
            childIssueSteps: unresolvedPreReconciledChildIssueSteps,
            pendingChildIssueDecisions: preReconciledState.workerExecution.workflowState
                ?.pendingChildIssueDecisions ?? [],
            workerExecution: preReconciledState.workerExecution,
            existingOpenChildren,
            catalog,
            assignee,
        });
    }
    catch (error) {
        if (error instanceof reconcile_1.ControllerChildDecisionReviewError) {
            return (0, transition_1.publishControllerChildNeedsHumanTransition)({
                ...input,
                workerExecution: preReconciledState.workerExecution,
                summary: error.message,
            });
        }
        throw error;
    }
    const parentDependencyChanges = await (0, reconcile_1.buildControllerChildParentDependencyChanges)({
        parentIssue: input.workerExecution.issue,
        existingBlockers: liveBlockers,
        childIssues: [...reconciledChildren.activeChildIssueByKey.values()],
    });
    const workerExecutionWithChildRefs = {
        ...preReconciledState.workerExecution,
        workflowState: reconciledChildren.workflowState,
    };
    const childDependencyChanges = await (0, reconcile_1.buildControllerChildDependencyChanges)({
        ticketTracker: input.context.ticketTracker,
        childIssueSteps: allChildIssueSteps,
        allWorkflowSteps: workerExecutionWithChildRefs.workflowState?.plan?.steps ?? [],
        childIssueByKey: reconciledChildren.childIssueByKey,
        activeChildIssueByKey: reconciledChildren.activeChildIssueByKey,
    });
    const explicitChildDecisionDependencyChanges = buildPendingChildDecisionParentDependencyChanges({
        parentIssueId: input.workerExecution.issue.id,
        existingBlockers: liveBlockers,
        childIssueDecisions: preReconciledState.workerExecution.workflowState
            ?.pendingChildIssueDecisions ?? [],
    });
    const dependencyChanges = [
        ...parentDependencyChanges,
        ...explicitChildDecisionDependencyChanges,
        ...childDependencyChanges,
    ].filter(dedupeWorkflowDependencyChanges());
    const transitionDetails = buildControllerChildTransitionDetails({
        responseLanguage: input.workerExecution.responseLanguage,
        reconciledChildren: {
            ...reconciledChildren,
            reflectedCompletedChildren: [
                ...preReconciledState.reflectedCompletedChildren,
                ...reconciledChildren.reflectedCompletedChildren,
            ],
        },
    });
    const controllerTerminalCompletion = isControllerParentTerminalCompletion(workerExecutionWithChildRefs.workflowState);
    const closingResolution = await resolveControllerTerminalClosing({
        context: input.context,
        claimedIssue: input.claimedIssue,
        logger: input.logger,
        workflowLabels: input.workflowLabels,
        workerExecution: workerExecutionWithChildRefs,
        controllerTerminalCompletion,
    });
    if (closingResolution.kind === "handled") {
        return closingResolution.summary;
    }
    return (0, transition_1.publishSpawnChildTransition)({
        ...input,
        workerExecution: closingResolution.workerExecution,
        ...transitionDetails,
        controllerTerminalCompletion,
        dependencyChanges,
        includeWorkerOutcome: false,
        commentCursorPolicy: resolveControllerChildCommentCursorPolicy(input.workerExecution.scannedInputCursor),
    });
}
async function resolveControllerTerminalClosing(input) {
    if (!input.controllerTerminalCompletion ||
        !(0, workflow_closing_1.shouldRunWorkflowClosingForTerminalIssue)({
            ...input.workerExecution,
            recordsRunOutcome: true,
        })) {
        return { kind: "continue", workerExecution: input.workerExecution };
    }
    const workspace = {
        path: (0, default_issue_workspace_manager_1.getIssueWorkspacePath)((0, tracked_project_1.toTrackedProjectRef)(input.context.config), input.claimedIssue.originalIssue.id, input.context.logHomeDirectory),
        branchName: input.workerExecution.branchName,
    };
    const closingExecution = await (0, workflow_closing_1.runWorkflowClosingWorker)({
        context: input.context,
        logger: input.logger,
        workspace,
        previousConsecutiveAiRuns: input.claimedIssue.previousConsecutiveAiRuns,
        latestIssue: input.claimedIssue.originalIssue,
        comments: await (0, shared_1.loadSortedIssueComments)(input.context.ticketTracker, input.claimedIssue.originalIssue.id, { timeline: "issue-only" }),
        workerExecution: {
            ...input.workerExecution,
            recordsRunOutcome: true,
        },
    });
    if (closingExecution.continuation?.strategy === "spawn_child_issue") {
        return {
            kind: "handled",
            summary: await (0, unplanned_1.publishSpawnChildContinuation)({
                context: input.context,
                claimedIssue: input.claimedIssue,
                logger: input.logger,
                workflowLabels: input.workflowLabels,
                workspace,
                workerExecution: closingExecution,
            }),
        };
    }
    if (closingExecution.workerOutcome !== constants_1.WORKER_OUTCOMES.proceed ||
        closingExecution.continuation !== null) {
        const transitionState = (0, transitions_1.buildSuccessfulTransitionState)(input.context.config, input.claimedIssue, closingExecution);
        return {
            kind: "handled",
            summary: await (0, transition_summary_1.publishTransitionSummary)({
                repoRoot: (0, workflow_template_root_1.getWorkflowTemplateRepoRoot)(input.context),
                config: input.context.config,
                currentUserLogin: input.context.currentUserLogin,
                repositoryHost: input.context.repositoryHost,
                ticketTracker: input.context.ticketTracker,
                issueId: input.claimedIssue.originalIssue.id,
                transitionState,
                logger: input.logger,
                workflowLabels: input.workflowLabels,
                fromStatus: input.claimedIssue.aiRunningSnapshot.status,
                resolvedFacts: closingExecution.facts,
                latestIssue: input.claimedIssue.originalIssue,
                worker: input.context.worker,
                ...(input.context.logHomeDirectory !== undefined
                    ? { homeDirectory: input.context.logHomeDirectory }
                    : {}),
            }),
        };
    }
    return { kind: "continue", workerExecution: closingExecution };
}
function buildPendingChildDecisionParentDependencyChanges(input) {
    const liveBlockerIds = new Set(input.existingBlockers
        .filter((blocker) => blocker.isOpen)
        .map((blocker) => blocker.id));
    return input.childIssueDecisions.flatMap((decision) => {
        if (decision.action !== "detach_child_issue" &&
            decision.action !== "cancel_child_issue") {
            return [];
        }
        const childIssueId = (0, shared_2.normalizeControllerChildIssueRef)(decision.childIssueRef);
        if (childIssueId === null || !liveBlockerIds.has(childIssueId)) {
            return [];
        }
        return [
            {
                kind: "remove_blocking_dependency",
                issue: { kind: "issue", issueId: input.parentIssueId },
                blockedBy: { kind: "issue", issueId: childIssueId },
            },
        ];
    });
}
function dedupeWorkflowDependencyChanges() {
    const seen = new Set();
    return (change) => {
        const key = JSON.stringify(change);
        if (seen.has(key)) {
            return false;
        }
        seen.add(key);
        return true;
    };
}
function resolveUnresolvedSelectedChildIssueSteps(workflowState) {
    return (0, shared_2.selectReadyControllerChildIssueSteps)(workflowState).filter((step) => step.result === null);
}
function buildControllerChildTransitionDetails(input) {
    if (input.reconciledChildren.createdChildren.length > 0 &&
        input.reconciledChildren.reflectedCompletedChildren.length > 0) {
        return {
            summary: (0, transition_1.buildCreatedAndProgressedControllerChildrenSummary)(input.responseLanguage, input.reconciledChildren.createdChildren.map((child) => child.displayId), input.reconciledChildren.reflectedCompletedChildren.map((child) => child.displayId)),
            visibleUpdateKind: child_publication_1.CHILD_ISSUE_VISIBLE_UPDATE_KINDS.controllerChildrenCreated,
            updateTrackerTitle: true,
        };
    }
    if (input.reconciledChildren.createdChildren.length === 0 &&
        input.reconciledChildren.updatedChildren.length > 0 &&
        input.reconciledChildren.reflectedCompletedChildren.length > 0) {
        return {
            summary: (0, transition_1.buildUpdatedAndProgressedControllerChildrenSummary)(input.responseLanguage, input.reconciledChildren.updatedChildren.map((child) => child.displayId), input.reconciledChildren.reflectedCompletedChildren.map((child) => child.displayId)),
            visibleUpdateKind: child_publication_1.CHILD_ISSUE_VISIBLE_UPDATE_KINDS.controllerChildrenUpdated,
            updateTrackerTitle: true,
        };
    }
    if (input.reconciledChildren.createdChildren.length === 0 &&
        input.reconciledChildren.reflectedCompletedChildren.length > 0) {
        return {
            summary: (0, transition_1.buildProgressedControllerChildrenSummary)(input.responseLanguage, input.reconciledChildren.reflectedCompletedChildren.map((child) => child.displayId)),
            visibleUpdateKind: child_publication_1.CHILD_ISSUE_VISIBLE_UPDATE_KINDS.controllerChildrenProgressed,
            updateTrackerTitle: true,
        };
    }
    if (input.reconciledChildren.createdChildren.length === 0 &&
        input.reconciledChildren.updatedChildren.length > 0) {
        return {
            summary: (0, transition_1.buildUpdatedControllerChildrenSummary)(input.responseLanguage, input.reconciledChildren.updatedChildren.map((child) => child.displayId)),
            visibleUpdateKind: child_publication_1.CHILD_ISSUE_VISIBLE_UPDATE_KINDS.controllerChildrenUpdated,
            updateTrackerTitle: false,
        };
    }
    if (input.reconciledChildren.createdChildren.length === 0 &&
        input.reconciledChildren.childIssueByKey.size > 0) {
        return {
            summary: (0, transition_1.buildExistingControllerChildrenSummary)(input.responseLanguage, [...input.reconciledChildren.childIssueByKey.values()].map((child) => child.displayId)),
            visibleUpdateKind: child_publication_1.CHILD_ISSUE_VISIBLE_UPDATE_KINDS.controllerChildrenExisting,
            updateTrackerTitle: false,
        };
    }
    return {
        summary: (0, transition_1.buildCreatedControllerChildrenSummary)(input.responseLanguage, input.reconciledChildren.createdChildren.map((child) => child.displayId)),
        visibleUpdateKind: child_publication_1.CHILD_ISSUE_VISIBLE_UPDATE_KINDS.controllerChildrenCreated,
        updateTrackerTitle: false,
    };
}
function resolveControllerChildCommentCursorPolicy(scannedInputCursor) {
    if (scannedInputCursor === null) {
        return { mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve };
    }
    return {
        mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.advanceToScannedInput,
        scannedInputCursor,
    };
}
function isControllerParentTerminalCompletion(workflowState) {
    if (workflowState === null) {
        return false;
    }
    if (!(0, workflow_structure_1.isWorkflowParentState)(workflowState) ||
        workflowState.plan === null ||
        (0, recurring_1.isRecurringParentWorkflowState)(workflowState)) {
        return false;
    }
    return workflowState.plan.steps.every((step) => step.result?.outcome === constants_1.WORKER_OUTCOMES.proceed);
}
async function buildStartedConflictWorkerExecution(input) {
    let workflowState = input.workerExecution.workflowState;
    const openChildLookup = await (0, shared_2.loadControllerChildIssueLookup)({
        ticketTracker: input.ticketTracker,
        issues: input.existingOpenChildren,
    });
    const childIssueByKey = new Map();
    const reflectedCompletedChildren = [];
    for (const childIssueStep of input.childIssueSteps) {
        const existingChild = await resolveControllerChildIssueForStartedConflict({
            ticketTracker: input.ticketTracker,
            parentIssue: input.workerExecution.issue,
            childIssueStep,
            openChildIssueByKey: openChildLookup.issueByChildKey,
            openChildWorkflowStateByKey: openChildLookup.workflowStateByChildKey,
        });
        if (existingChild === undefined) {
            continue;
        }
        childIssueByKey.set(childIssueStep.childKey, existingChild.issue);
        if (!existingChild.issue.isOpen &&
            workflowState !== null &&
            workflowState.plan !== null &&
            !(0, shared_2.hasControllerChildParentStepContractSnapshotDrift)({
                childIssueStep,
                childWorkflowState: existingChild.workflowState,
            })) {
            reflectedCompletedChildren.push(existingChild.issue);
            workflowState = {
                ...workflowState,
                plan: (0, plan_progress_1.withWorkflowPlanStepResultById)(workflowState.plan, childIssueStep.id, (0, plan_progress_1.buildCompletedControllerChildIssueResult)({
                    issue: existingChild.issue,
                    workflowState: existingChild.workflowState,
                })),
            };
        }
    }
    return {
        workerExecution: {
            ...input.workerExecution,
            workflowState: (0, shared_2.attachControllerChildIssueRefsToWorkflowState)({
                workflowState,
                childIssueByKey,
            }),
        },
        reflectedCompletedChildren,
    };
}
async function resolveControllerChildIssueForStartedConflict(input) {
    const openChild = input.openChildIssueByKey.get(input.childIssueStep.childKey);
    if (openChild !== undefined) {
        const workflowState = input.openChildWorkflowStateByKey.get(input.childIssueStep.childKey) ??
            null;
        if (workflowState !== null &&
            (0, shared_2.isControllerChildForParent)({
                workflowState,
                parentDisplayId: input.parentIssue.displayId,
                childKey: input.childIssueStep.childKey,
            })) {
            return {
                issue: openChild,
                workflowState,
            };
        }
    }
    const completedChild = await (0, match_1.resolveCompletedControllerChildIssueByRef)({
        ticketTracker: input.ticketTracker,
        parentIssue: input.parentIssue,
        childIssueStep: input.childIssueStep,
    });
    if (completedChild !== null) {
        return {
            issue: completedChild.issue,
            workflowState: completedChild.workflowState,
        };
    }
    return undefined;
}
