"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkflowClosingFreshnessMismatchError = void 0;
exports.shouldRunWorkflowClosingForTerminalIssue = shouldRunWorkflowClosingForTerminalIssue;
exports.runWorkflowClosingWorker = runWorkflowClosingWorker;
const control_plane_config_1 = require("../../config/control-plane-config");
const types_1 = require("../../platform/ticket-tracker/types");
const types_2 = require("../../worker/types");
const tree_1 = require("../../comments/canonical-workflow-state/tree");
const run_context_1 = require("../../workflows/run-context");
const state_1 = require("../../workflows/state");
const log_text_1 = require("../log-text");
const results_1 = require("./results");
const worker_support_1 = require("./worker-support");
class WorkflowClosingFreshnessMismatchError extends Error {
    workspacePath;
    workspaceHeadCommitOid;
    expectedCommitOid;
    expectedCommitKind;
    constructor(input) {
        super(`workflow_closing workspace freshness mismatch: expected ${input.expectedCommitKind} ${input.expectedCommitOid}, got ${input.workspaceHeadCommitOid}`);
        this.name = "WorkflowClosingFreshnessMismatchError";
        this.workspacePath = input.workspacePath;
        this.workspaceHeadCommitOid = input.workspaceHeadCommitOid;
        this.expectedCommitOid = input.expectedCommitOid;
        this.expectedCommitKind = input.expectedCommitKind;
    }
}
exports.WorkflowClosingFreshnessMismatchError = WorkflowClosingFreshnessMismatchError;
function shouldRunWorkflowClosingForTerminalIssue(workerExecution) {
    return (workerExecution.runMode !== types_2.WORKER_RUN_MODES.workflowClosing &&
        workerExecution.issue.kind === types_1.TRACKER_ITEM_KINDS.issue &&
        !workerExecution.facts.branchAheadOfBase &&
        !workerExecution.facts.openPullRequestExists &&
        (0, results_1.isTerminalWorkflowSuccessCandidate)({
            runMode: workerExecution.runMode,
            finalOutcome: workerExecution.workerOutcome,
            recordsRunOutcome: workerExecution.recordsRunOutcome,
            continuation: workerExecution.continuation,
            workflowState: workerExecution.workflowState,
        }));
}
async function runWorkflowClosingWorker(input) {
    const workflowState = input.workerExecution.workflowState;
    if (workflowState === null) {
        return input.workerExecution;
    }
    const workflow = buildWorkflowClosingContext({
        context: input.context,
        workerExecution: input.workerExecution,
    });
    await appendWorkflowClosingWorkerStartLog(input);
    let wroteStdoutHeader = false;
    let wroteStderrHeader = false;
    try {
        const workspaceHeadCommitOidBeforeRun = await input.context.gitRepository.getHeadCommitOid(input.workspace.path);
        await assertWorkflowClosingWorkspaceFreshness({
            context: input.context,
            logger: input.logger,
            workspacePath: input.workspace.path,
            branchName: input.workspace.branchName,
            workspaceHeadCommitOid: workspaceHeadCommitOidBeforeRun,
            expectation: input.freshnessExpectation ?? null,
        });
        const workerResult = await input.context.worker.run({
            issue: input.latestIssue,
            comments: [...input.comments],
            responseLanguage: input.workerExecution.responseLanguage,
            workspacePath: input.workspace.path,
            repositoryRoot: input.workspace.path,
            workflow,
            onStdoutChunk(chunk) {
                if (!wroteStdoutHeader) {
                    wroteStdoutHeader = true;
                    void input.logger.appendText(`\n--- ${(0, log_text_1.issueLogSection)("workerStdout")} ---\n`);
                }
                void input.logger.appendText(chunk);
            },
            onStderrChunk(chunk) {
                if (!wroteStderrHeader) {
                    wroteStderrHeader = true;
                    void input.logger.appendText(`\n--- ${(0, log_text_1.issueLogSection)("workerStderr")} ---\n`);
                }
                void input.logger.appendText(chunk);
            },
            validateDecision(decision) {
                (0, state_1.resolveWorkerExecutionWorkflow)({
                    workflow,
                    workflowTemplateName: input.workerExecution.workflowTemplateName,
                    workflowState,
                }, decision, input.workerExecution.responseLanguage);
            },
        });
        if (workerResult.kind === "deferred") {
            throw new Error(`workflow_closing worker deferred: ${workerResult.reason}`);
        }
        await (0, worker_support_1.appendWorkerDecisionSections)(input.logger, workerResult);
        return buildWorkflowClosingWorkerExecutionState({
            input,
            workflow,
            workflowState,
            workerResult,
            workspaceHeadCommitOidBeforeRun,
        });
    }
    finally {
        await appendMissingWorkflowClosingWorkerOutputLogs({
            logger: input.logger,
            wroteStdoutHeader,
            wroteStderrHeader,
        });
        await input.logger.flush();
    }
}
async function assertWorkflowClosingWorkspaceFreshness(input) {
    if (input.expectation === null) {
        return;
    }
    const expectedCommitOid = input.expectation.expectedCommitOid.trim();
    const freshnessVerified = expectedCommitOid !== "" &&
        (input.workspaceHeadCommitOid === expectedCommitOid ||
            (await input.context.gitRepository.isCommitAncestorOfHead(input.workspacePath, expectedCommitOid)));
    await input.logger.appendSection((0, log_text_1.issueLogSection)("freshnessRefresh"), [
        (0, log_text_1.issueLogField)("mode", types_2.WORKER_RUN_MODES.workflowClosing),
        (0, log_text_1.issueLogField)("workspace", input.workspacePath),
        (0, log_text_1.issueLogField)("branch", input.branchName),
        (0, log_text_1.issueLogField)("workspaceHead", input.workspaceHeadCommitOid),
        (0, log_text_1.issueLogField)("expectedCommit", expectedCommitOid || "-"),
        (0, log_text_1.issueLogField)("expectedCommitKind", input.expectation.expectedCommitKind),
        (0, log_text_1.issueLogField)("freshnessVerified", freshnessVerified ? "yes" : "no"),
        ...(input.expectation.pullRequestNumber === undefined
            ? []
            : [(0, log_text_1.issueLogField)("pr", input.expectation.pullRequestNumber)]),
    ].join("\n"));
    if (freshnessVerified) {
        return;
    }
    throw new WorkflowClosingFreshnessMismatchError({
        workspacePath: input.workspacePath,
        workspaceHeadCommitOid: input.workspaceHeadCommitOid,
        expectedCommitOid,
        expectedCommitKind: input.expectation.expectedCommitKind,
    });
}
async function appendWorkflowClosingWorkerStartLog(input) {
    await input.logger.appendSection((0, log_text_1.issueLogSection)("workerStart"), [
        (0, log_text_1.issueLogField)("mode", types_2.WORKER_RUN_MODES.workflowClosing),
        (0, log_text_1.issueLogField)("template", input.workerExecution.workflowTemplateName ?? "-"),
        (0, log_text_1.issueLogField)("workspace", input.workspace.path),
        (0, log_text_1.issueLogField)("branch", input.workspace.branchName),
        ...renderWorkflowClosingFreshnessExpectationFields(input.freshnessExpectation ?? null),
        (0, log_text_1.issueLogField)("commentCount", input.comments.length),
        (0, log_text_1.issueLogField)("consecutiveAiRunsBeforeWorker", input.previousConsecutiveAiRuns),
    ].join("\n"));
}
function renderWorkflowClosingFreshnessExpectationFields(expectation) {
    if (expectation === null) {
        return [];
    }
    return [
        (0, log_text_1.issueLogField)("expectedCommit", expectation.expectedCommitOid),
        (0, log_text_1.issueLogField)("expectedCommitKind", expectation.expectedCommitKind),
        ...(expectation.pullRequestNumber === undefined
            ? []
            : [(0, log_text_1.issueLogField)("pr", expectation.pullRequestNumber)]),
    ];
}
function buildWorkflowClosingWorkerExecutionState(input) {
    const resolvedWorkflow = (0, state_1.resolveWorkerExecutionWorkflow)({
        workflow: input.workflow,
        workflowTemplateName: input.input.workerExecution.workflowTemplateName,
        workflowState: input.workflowState,
    }, input.workerResult.launchResult.decision, input.input.workerExecution.responseLanguage);
    return (0, worker_support_1.buildWorkerExecutionState)(input.input.workerExecution.issue, {
        workerId: input.workerResult.workerId,
        workerName: input.workerResult.workerName,
        ...(input.workerResult.authProfileId === undefined
            ? {}
            : { authProfileId: input.workerResult.authProfileId }),
        ...(input.workerResult.authProfileKind === undefined
            ? {}
            : { authProfileKind: input.workerResult.authProfileKind }),
        workerDurationMs: (input.input.workerExecution.workerDurationMs ?? 0) +
            (input.workerResult.workerDurationMs ?? 0),
        workspaceHeadCommitOidBeforeRun: input.workspaceHeadCommitOidBeforeRun,
        decision: input.workerResult.launchResult.decision,
    }, input.input.workerExecution.consecutiveAiRuns, input.input.workerExecution.responseLanguage, {
        facts: input.input.workerExecution.facts,
        pullRequestUrl: input.input.workerExecution.pullRequestUrl,
        pullRequestThreadId: input.input.workerExecution.facts.pullRequestThreadId,
    }, input.input.workerExecution.scannedInputCursor, input.input.workerExecution.branchName, types_2.WORKER_RUN_MODES.workflowClosing, resolvedWorkflow.workflowTemplateName, resolvedWorkflow.workflowState, input.input.workerExecution.planningIntent, {
        mergeContext: input.input.workerExecution.mergeContext,
        plannedIssueTitle: input.input.workerExecution.plannedIssueTitle,
        recordsRunOutcome: true,
        startedWithPlanningIntent: input.input.workerExecution.startedWithPlanningIntent,
        startedWithWorkflowStartApprovalIntent: input.input.workerExecution.startedWithWorkflowStartApprovalIntent,
    });
}
async function appendMissingWorkflowClosingWorkerOutputLogs(input) {
    if (!input.wroteStdoutHeader) {
        await input.logger.appendText(`\n--- ${(0, log_text_1.issueLogSection)("workerStdout")} ---\n`);
    }
    if (!input.wroteStderrHeader) {
        await input.logger.appendText(`\n--- ${(0, log_text_1.issueLogSection)("workerStderr")} ---\n`);
    }
}
function buildWorkflowClosingContext(input) {
    const effectiveConfig = (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.context.config, input.workerExecution.workflowState);
    const workflowState = input.workerExecution.workflowState;
    const progress = (0, tree_1.selectWorkflowPlanProgress)(workflowState?.plan ?? null);
    return {
        mode: types_2.WORKER_RUN_MODES.workflowClosing,
        templateName: input.workerExecution.workflowTemplateName,
        workflowSurface: workflowState?.workflowSurface ?? "issue",
        state: (0, run_context_1.sanitizeWorkerVisibleWorkflowState)(workflowState),
        issueCompletion: effectiveConfig.issueCompletion,
        issueNotesPath: effectiveConfig.paths.issueNotes,
        followupMaxIssuesPerSource: effectiveConfig.followup.maxIssuesPerSource,
        followupPreferredWorkflowTemplates: [
            ...effectiveConfig.followup.preferredWorkflowTemplates,
        ],
        followupAutoMaintainTemplates: effectiveConfig.followup.autoMaintainTemplates,
        commandContext: progress.isCompleted
            ? "The workflow plan is complete. Audit whether the source issue itself can close and decide any same-source follow-up recommendation."
            : "The workflow plan is not complete. Do not close the source issue.",
    };
}
