"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.runClaimedIssuePass = runClaimedIssuePass;
const canonical_workflow_state_1 = require("../../comments/canonical-workflow-state");
const setup_1 = require("../../workspaces/setup");
const workflow_timeline_scan_1 = require("../../workflows/workflow-timeline-scan");
const log_text_1 = require("../log-text");
const dependencies_1 = require("./dependencies");
const failure_recovery_1 = require("./failure-recovery");
const freshness_1 = require("./freshness");
const run_state_helpers_1 = require("./run-state-helpers");
const gate_1 = require("./gate");
const handoffs_1 = require("./handoffs");
const issue_branch_target_1 = require("./issue-branch-target");
const merge_integration_preflight_1 = require("./merge-integration-preflight");
const presentation_1 = require("./presentation");
const interruption_1 = require("./publish/interruption");
const publish_claimed_pass_1 = require("./publish-claimed-pass");
const transition_summary_1 = require("./transition-summary");
const shared_1 = require("./shared");
const transitions_1 = require("./transitions");
const ai_run_accounting_1 = require("./ai-run-accounting");
const preflight_resolution_child_1 = require("./preflight-resolution-child");
const pre_worker_gate_failure_1 = require("./pre-worker-gate-failure");
const worker_1 = require("./worker");
const workflow_template_root_1 = require("./workflow-template-root");
async function runClaimedIssuePass(input) {
    input.context.ticketTracker.resetVolatileIssueReads(input.claimedIssue.originalIssue.id);
    const freshnessBefore = await (0, freshness_1.loadFreshnessSnapshot)(input.context.freshTicketTracker, input.claimedIssue.originalIssue.id);
    const interruptedBefore = await classifyStateChangedInterrupt({
        context: input.context,
        claimedIssue: input.claimedIssue,
        logger: input.logger,
        workflowLabels: input.workflowLabels,
        freshnessPass: input.freshnessPass,
        failureSeed: input.failureSeed,
        freshnessBefore,
    });
    if (interruptedBefore !== null) {
        await input.suspendHeartbeat();
        return interruptedBefore;
    }
    await input.logger.appendSection((0, log_text_1.issueLogSection)("freshnessRefresh"), (0, freshness_1.renderFreshnessRefreshSection)({
        workflowLabels: input.workflowLabels,
        pass: input.freshnessPass,
        maxPasses: shared_1.MAX_FRESHNESS_PASSES,
        latestIssue: freshnessBefore.latestIssue,
        commentCount: freshnessBefore.comments.length,
        currentConsecutiveAiRuns: freshnessBefore.currentConsecutiveAiRuns,
        currentTotalAiRunHours: freshnessBefore.currentTotalAiRunHours,
        cursor: freshnessBefore.cursor,
        note: "pass_start",
    }));
    const dependencyPreflight = await (0, dependencies_1.applyDependencyCompletionPreflight)({
        context: input.context,
        issue: input.claimedIssue.originalIssue,
        comments: freshnessBefore.comments,
        locale: input.logger.locale,
    });
    const freshnessForWorker = dependencyPreflight.workflowStateChanged
        ? await (0, freshness_1.loadFreshnessSnapshot)(input.context.freshTicketTracker, input.claimedIssue.originalIssue.id)
        : freshnessBefore;
    const workerAttemptResult = await runWorkerCycleAttempt({
        context: input.context,
        logger: input.logger,
        workspace: input.workspace,
        freshnessBefore: freshnessForWorker,
        failureSeed: input.failureSeed,
        continuationContext: input.continuationContext,
        suspendHeartbeat: input.suspendHeartbeat,
    });
    if (workerAttemptResult.kind === "complete") {
        return workerAttemptResult;
    }
    const freshnessAfter = await loadAndLogPostWorkerFreshness({
        ticketTracker: input.context.freshTicketTracker,
        issueId: input.claimedIssue.originalIssue.id,
        logger: input.logger,
        workflowLabels: input.workflowLabels,
        pass: input.freshnessPass,
    });
    return classifyPostWorkerPass({
        ...input,
        freshnessBefore,
        freshnessAfter,
        workerAttempt: workerAttemptResult.attempt,
    });
}
async function runWorkerCycleAttempt(input) {
    let workerState = null;
    let workerError = null;
    let nextFailureSeed = input.failureSeed;
    const preparation = await (0, gate_1.prepareWorkerRun)({
        context: input.context,
        snapshot: input.freshnessBefore,
        continuationContext: input.continuationContext,
        options: {
            allowPreflightReplan: false,
        },
    });
    if (preparation.kind === "handoff_to_human") {
        return {
            kind: "attempt",
            attempt: buildPreWorkerHandoffAttempt(input, preparation),
        };
    }
    if (preparation.kind === "preflight_resolution_required") {
        await input.suspendHeartbeat();
        const failureSeed = (0, pre_worker_gate_failure_1.buildPreWorkerGateFailureSeed)(input, preparation);
        return {
            kind: "complete",
            failureSeed,
            continuationContext: null,
            summary: await (0, preflight_resolution_child_1.resolvePreflightResolutionIssue)({
                issueContext: input.context,
                logger: input.logger,
                preparation,
            }),
        };
    }
    if (preparation.kind !== "run_worker") {
        throw new Error("Unexpected workflow replan gate after issue claim.");
    }
    const workerPreparation = await (0, merge_integration_preflight_1.prepareMergeIntegrationPreflight)({
        context: input.context,
        workspace: input.workspace,
        preparation,
    });
    if (workerPreparation.kind === "handoff_to_human") {
        return {
            kind: "attempt",
            attempt: buildPreWorkerHandoffAttempt(input, workerPreparation),
        };
    }
    await (0, setup_1.ensureWorkspaceSetup)({
        workspacePath: input.workspace.path,
        config: input.context.config.workspaces,
        logger: input.logger,
    });
    try {
        workerState = await (0, worker_1.runWorkerCycle)(input.context, input.logger, input.workspace, workerPreparation);
        if (workerState.kind === "preflight_resolution_required") {
            await input.suspendHeartbeat();
            const failureSeed = (0, pre_worker_gate_failure_1.buildPreWorkerGateFailureSeed)(input, workerState.preparation);
            return {
                kind: "complete",
                failureSeed,
                continuationContext: null,
                summary: await (0, preflight_resolution_child_1.resolvePreflightResolutionIssue)({
                    issueContext: input.context,
                    logger: input.logger,
                    preparation: workerState.preparation,
                }),
            };
        }
        if (workerState.kind === "completed") {
            nextFailureSeed = {
                issue: workerState.workerExecution.issue,
                branchName: workerState.workerExecution.branchName,
                pullRequestUrl: workerState.workerExecution.pullRequestUrl,
                commentUrl: null,
                consecutiveAiRuns: workerState.workerExecution.consecutiveAiRuns,
                totalAiRunHours: (0, ai_run_accounting_1.resolveWorkerExecutionTotalAiRunHours)(workerState.workerExecution),
                runMode: workerState.workerExecution.runMode,
                workflowTemplateName: workerState.workerExecution.workflowTemplateName,
                workflowState: workerState.workerExecution.workflowState,
                needsHumanReason: workerState.workerExecution.needsHumanReason,
            };
        }
    }
    catch (error) {
        if (!(error instanceof shared_1.WorkerCycleError)) {
            throw error;
        }
        nextFailureSeed = {
            ...error.seed,
            commentUrl: null,
        };
        workerError = error.cause;
    }
    return {
        kind: "attempt",
        attempt: {
            failureSeed: nextFailureSeed,
            workerState,
            workerError,
        },
    };
}
function buildPreWorkerHandoffAttempt(input, gate) {
    return {
        failureSeed: (0, pre_worker_gate_failure_1.buildPreWorkerGateFailureSeed)(input, gate),
        workerState: {
            kind: "handoff",
            handoff: {
                issue: input.freshnessBefore.latestIssue,
                responseLanguage: input.context.config.i18n.defaultLanguage,
                summary: gate.summary,
                branchName: input.workspace.branchName,
                workflowTemplateName: gate.workflowTemplateName,
                workflowState: gate.workflowState,
                totalAiRunHours: gate.currentTotalAiRunHours,
                needsHumanReason: gate.needsHumanReason,
            },
        },
        workerError: null,
    };
}
async function buildStateChangedInterruptResult(input) {
    return {
        kind: "complete",
        failureSeed: input.failureSeed,
        continuationContext: null,
        summary: await (0, interruption_1.handleInterruptedIssue)(input.logger, input.claimedIssue, input.currentSnapshot, "state-changed", input.context.config.i18n.defaultLanguage, (0, freshness_1.renderPublishAbortedSection)({
            workflowLabels: input.workflowLabels,
            reason: "state-changed",
            pass: input.freshnessPass,
            maxPasses: shared_1.MAX_FRESHNESS_PASSES,
            cursorBefore: input.cursorBefore,
            cursorAfter: input.cursorAfter,
            claimedSnapshot: input.claimedIssue.aiRunningSnapshot,
            currentSnapshot: input.currentSnapshot.workflowState,
        }), input.workflowLabels, input.context),
    };
}
async function classifyStateChangedInterrupt(input) {
    if ((0, shared_1.isClaimedFreshnessSnapshot)(input.claimedIssue.aiRunningSnapshot, input.freshnessBefore.latestIssue) &&
        hasValidClaimedIssueState(input.context, input.claimedIssue, input.freshnessBefore)) {
        return null;
    }
    return buildStateChangedInterruptResult({
        context: input.context,
        claimedIssue: input.claimedIssue,
        logger: input.logger,
        workflowLabels: input.workflowLabels,
        freshnessPass: input.freshnessPass,
        cursorBefore: input.freshnessBefore.cursor,
        cursorAfter: input.freshnessBefore.cursor,
        currentSnapshot: input.freshnessBefore.latestIssue,
        failureSeed: input.failureSeed,
    });
}
async function classifyPostWorkerPass(input) {
    const stateChangedResult = await classifyPostWorkerStateChanged(input);
    if (stateChangedResult !== null) {
        await input.suspendHeartbeat();
        return stateChangedResult;
    }
    const deferredResult = await classifyDeferredWorker(input);
    if (deferredResult !== null) {
        await input.suspendHeartbeat();
        return deferredResult;
    }
    const handoffResult = await classifyWorkerHandoff(input);
    if (handoffResult !== null) {
        await input.suspendHeartbeat();
        return handoffResult;
    }
    const rerunResult = await classifyActionableCommentRerun(input);
    if (rerunResult !== null) {
        await input.suspendHeartbeat();
        if (rerunResult.kind === "continue") {
            input.resumeHeartbeat();
        }
        return rerunResult;
    }
    const workerErrorResult = await classifyWorkerError(input);
    if (workerErrorResult !== null) {
        await input.suspendHeartbeat();
        return workerErrorResult;
    }
    return (0, publish_claimed_pass_1.publishOrContinueClaimedPass)(input);
}
async function classifyPostWorkerStateChanged(input) {
    if ((0, shared_1.isClaimedFreshnessSnapshot)(input.claimedIssue.aiRunningSnapshot, input.freshnessAfter.latestIssue) &&
        hasValidClaimedIssueState(input.context, input.claimedIssue, input.freshnessAfter)) {
        return null;
    }
    return buildStateChangedInterruptResult({
        context: input.context,
        claimedIssue: input.claimedIssue,
        logger: input.logger,
        workflowLabels: input.workflowLabels,
        freshnessPass: input.freshnessPass,
        cursorBefore: input.freshnessBefore.cursor,
        cursorAfter: input.freshnessAfter.cursor,
        currentSnapshot: input.freshnessAfter.latestIssue,
        failureSeed: input.workerAttempt.failureSeed,
    });
}
async function classifyDeferredWorker(input) {
    if (input.workerAttempt.workerState?.kind !== "deferred") {
        return null;
    }
    const deferred = input.workerAttempt.workerState.deferred;
    await input.logger.appendSection((0, log_text_1.issueLogSection)("workerDeferred"), (0, presentation_1.renderDeferredWorkerSection)(deferred, input.logger.locale));
    const summary = await (0, handoffs_1.deferIssueForCooldown)(input.context, input.claimedIssue, input.logger, deferred);
    await input.logger.appendSection((0, log_text_1.issueLogSection)("finalSummary"), (0, presentation_1.renderCliSummary)(summary, input.logger.locale));
    return {
        kind: "complete",
        failureSeed: input.workerAttempt.failureSeed,
        continuationContext: null,
        summary,
    };
}
async function classifyWorkerHandoff(input) {
    if (input.workerAttempt.workerState?.kind !== "handoff") {
        return null;
    }
    return {
        kind: "complete",
        failureSeed: input.workerAttempt.failureSeed,
        continuationContext: null,
        summary: await handleWorkerHandoffAfterFreshnessPass(input.context, input.claimedIssue, input.logger, input.workflowLabels, input.workerAttempt.workerState.handoff.consecutiveAiRuns ??
            input.freshnessAfter.currentConsecutiveAiRuns, input.freshnessAfter.currentTotalAiRunHours, input.workerAttempt.workerState.handoff),
    };
}
async function classifyActionableCommentRerun(input) {
    if (!(0, workflow_timeline_scan_1.hasActionableWorkflowCommentAfterCursor)(input.freshnessAfter.comments, input.freshnessBefore.cursor)) {
        return null;
    }
    return (0, interruption_1.handleFreshnessRerun)(input.context, input.claimedIssue, input.logger, input.workflowLabels, input.freshnessPass, input.freshnessBefore.cursor, input.freshnessAfter, input.workerAttempt.failureSeed, input.continuationContext, input.workerAttempt.workerState);
}
async function classifyWorkerError(input) {
    if (!input.workerAttempt.workerError) {
        return null;
    }
    return {
        kind: "complete",
        failureSeed: input.workerAttempt.failureSeed,
        continuationContext: input.continuationContext,
        summary: await (0, failure_recovery_1.handleClaimedIssueFailure)(input.context, input.claimedIssue, input.logger, input.workerAttempt.failureSeed, input.workerAttempt.workerError, input.workflowLabels),
    };
}
function hasValidClaimedIssueState(context, claimedIssue, freshnessSnapshot) {
    const resolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(freshnessSnapshot.comments, context.config.i18n.defaultLanguage);
    return (resolution.kind === "found" &&
        (0, shared_1.matchesClaimedExecutionOwnership)({
            originalIssue: claimedIssue.originalIssue,
            latestIssue: freshnessSnapshot.latestIssue,
            currentExecutionOwnership: resolution.workflowState.executionOwnership,
            expectedExecutionOwnership: claimedIssue.executionOwnership,
        }));
}
async function loadAndLogPostWorkerFreshness(input) {
    const freshness = await (0, freshness_1.loadFreshnessSnapshot)(input.ticketTracker, input.issueId);
    await input.logger.appendSection((0, log_text_1.issueLogSection)("freshnessRefresh"), (0, freshness_1.renderFreshnessRefreshSection)({
        workflowLabels: input.workflowLabels,
        pass: input.pass,
        maxPasses: shared_1.MAX_FRESHNESS_PASSES,
        latestIssue: freshness.latestIssue,
        commentCount: freshness.comments.length,
        currentConsecutiveAiRuns: freshness.currentConsecutiveAiRuns,
        currentTotalAiRunHours: freshness.currentTotalAiRunHours,
        cursor: freshness.cursor,
        note: "post_worker",
    }));
    return freshness;
}
async function handleWorkerHandoffAfterFreshnessPass(context, claimedIssue, logger, workflowLabels, consecutiveAiRuns, totalAiRunHours, handoff) {
    const transitionState = (0, transitions_1.buildWaitHumanTransitionState)({
        config: context.config,
        previousSnapshot: claimedIssue.aiRunningSnapshot,
        readySnapshot: claimedIssue.aiReadySnapshot,
        issue: handoff.issue,
        runMode: (0, run_state_helpers_1.inferWorkflowRunMode)(handoff.workflowState),
        summary: handoff.summary,
        needsHumanReason: handoff.needsHumanReason,
        facts: shared_1.EMPTY_FACTS,
        branchName: handoff.branchName,
        baseBranch: handoff.workflowState === null
            ? context.config.repositoryHost.baseBranch
            : (0, issue_branch_target_1.resolveIssueRunBaseBranch)(handoff.workflowState),
        pullRequestThreadId: null,
        pullRequestUrl: null,
        workflowTemplateName: handoff.workflowTemplateName,
        workflowState: handoff.workflowState,
        consecutiveAiRuns,
        totalAiRunHours: handoff.totalAiRunHours ?? totalAiRunHours,
        responseLanguage: handoff.responseLanguage,
    });
    return (0, transition_summary_1.publishTransitionSummary)({
        repoRoot: (0, workflow_template_root_1.getWorkflowTemplateRepoRoot)(context),
        config: context.config,
        currentUserLogin: context.currentUserLogin,
        repositoryHost: context.repositoryHost,
        ticketTracker: context.ticketTracker,
        issueId: claimedIssue.originalIssue.id,
        transitionState,
        logger,
        workflowLabels,
        fromStatus: claimedIssue.aiRunningSnapshot.status,
        resolvedFacts: shared_1.EMPTY_FACTS,
        worker: context.worker,
        ...(context.logHomeDirectory !== undefined
            ? { homeDirectory: context.logHomeDirectory }
            : {}),
    });
}
