"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleFreshnessRerun = handleFreshnessRerun;
exports.handlePostPublishFreshnessInterrupt = handlePostPublishFreshnessInterrupt;
exports.handleInterruptedIssue = handleInterruptedIssue;
const catalog_1 = require("../../../i18n/catalog");
const types_1 = require("../../../platform/ticket-tracker/types");
const types_2 = require("../../../worker/types");
const assignee_resolution_1 = require("../../../workflows/assignee-resolution");
const approval_instructions_1 = require("../../../workflows/approval-instructions");
const template_bindings_1 = require("../../../workflows/template-bindings");
const publisher_shared_1 = require("../../../workflows/publisher-shared");
const workflow_state_read_support_1 = require("../../../workflows/workflow-state-read-support");
const workflow_timeline_scan_1 = require("../../../workflows/workflow-timeline-scan");
const default_issue_workspace_manager_1 = require("../../../workspaces/default-issue-workspace-manager");
const log_text_1 = require("../../log-text");
const effect_log_1 = require("../effect-log");
const freshness_1 = require("../freshness");
const run_state_helpers_1 = require("../run-state-helpers");
const handoffs_1 = require("../handoffs");
const owned_workflow_state_publication_1 = require("../owned-workflow-state-publication");
const presentation_1 = require("../presentation");
const publisher_factory_1 = require("../publisher-factory");
const shared_1 = require("../shared");
const transition_summary_1 = require("../transition-summary");
const types_3 = require("../types");
async function handleFreshnessRerun(context, claimedIssue, logger, workflowLabels, freshnessPass, cursorBefore, freshnessAfter, failureSeed, continuationContext, workerState) {
    const evaluation = await evaluateFreshnessInterrupt({
        context,
        claimedIssue,
        logger,
        workflowLabels,
        freshnessPass,
        cursorBefore,
        freshnessAfter,
        continuationContext,
        workerState,
    });
    if (evaluation.kind === "continue") {
        return {
            kind: "continue",
            failureSeed,
            continuationContext: evaluation.continuationContext,
        };
    }
    if (evaluation.kind === "interrupt") {
        return {
            kind: "complete",
            failureSeed,
            continuationContext: null,
            summary: evaluation.summary,
        };
    }
    return {
        kind: "continue",
        failureSeed,
        continuationContext,
    };
}
async function handlePostPublishFreshnessInterrupt(input) {
    const evaluation = await evaluateFreshnessInterrupt({
        context: input.context,
        claimedIssue: input.claimedIssue,
        logger: input.logger,
        workflowLabels: input.workflowLabels,
        freshnessPass: input.freshnessPass,
        cursorBefore: input.cursorBefore,
        freshnessAfter: input.freshnessAfter,
        continuationContext: null,
        workerState: null,
    });
    if (evaluation.kind === "none") {
        return { kind: "none" };
    }
    if (evaluation.kind === "continue") {
        return { kind: "rerun" };
    }
    return { kind: "interrupt", summary: evaluation.summary };
}
async function evaluateFreshnessInterrupt(input) {
    if (!(0, shared_1.isClaimedFreshnessSnapshot)(input.claimedIssue.aiRunningSnapshot, input.freshnessAfter.latestIssue)) {
        return {
            kind: "interrupt",
            summary: await buildFreshnessInterruptSummary({
                context: input.context,
                claimedIssue: input.claimedIssue,
                logger: input.logger,
                workflowLabels: input.workflowLabels,
                freshnessPass: input.freshnessPass,
                cursorBefore: input.cursorBefore,
                freshnessAfter: input.freshnessAfter,
                reason: "state-changed",
            }),
        };
    }
    if (!(0, workflow_timeline_scan_1.hasActionableWorkflowCommentAfterCursor)(input.freshnessAfter.comments, input.cursorBefore)) {
        return { kind: "none" };
    }
    await input.logger.appendSection((0, log_text_1.issueLogSection)("freshnessRerun"), (0, freshness_1.renderFreshnessRerunSection)({
        pass: input.freshnessPass,
        maxPasses: shared_1.MAX_FRESHNESS_PASSES,
        reason: "new-comment",
        cursorBefore: input.cursorBefore,
        cursorAfter: input.freshnessAfter.cursor,
    }));
    await persistFreshnessRerunCursor({
        context: input.context,
        claimedIssue: input.claimedIssue,
        logger: input.logger,
        freshnessAfter: input.freshnessAfter,
    });
    if (input.freshnessPass < shared_1.MAX_FRESHNESS_PASSES) {
        return {
            kind: "continue",
            continuationContext: input.workerState?.kind === "completed"
                ? (0, run_state_helpers_1.buildContinuationContext)(input.workerState.workerExecution, "freshness_rerun")
                : input.continuationContext,
        };
    }
    return {
        kind: "interrupt",
        summary: await buildFreshnessInterruptSummary({
            context: input.context,
            claimedIssue: input.claimedIssue,
            logger: input.logger,
            workflowLabels: input.workflowLabels,
            freshnessPass: input.freshnessPass,
            cursorBefore: input.cursorBefore,
            freshnessAfter: input.freshnessAfter,
            reason: "new-comment",
        }),
    };
}
async function buildFreshnessInterruptSummary(input) {
    return handleInterruptedIssue(input.logger, input.claimedIssue, input.freshnessAfter.latestIssue, input.reason, input.context.config.i18n.defaultLanguage, (0, freshness_1.renderPublishAbortedSection)({
        workflowLabels: input.workflowLabels,
        reason: input.reason,
        pass: input.freshnessPass,
        maxPasses: shared_1.MAX_FRESHNESS_PASSES,
        cursorBefore: input.cursorBefore,
        cursorAfter: input.freshnessAfter.cursor,
        currentSnapshot: input.freshnessAfter.latestIssue.workflowState,
        claimedSnapshot: input.claimedIssue.aiRunningSnapshot,
    }), input.workflowLabels, input.context);
}
async function handleInterruptedIssue(logger, claimedIssue, issue, reason, responseLanguage, details, workflowLabels, context) {
    if (context &&
        reason === "state-changed" &&
        (isAssigneeDrift(claimedIssue, issue) ||
            isIssueBodyDrift(claimedIssue, issue))) {
        return handleForeignInputDriftInterruptedIssue({
            logger,
            claimedIssue,
            issue,
            responseLanguage,
            details,
            workflowLabels,
            context,
        });
    }
    if (context) {
        await (0, owned_workflow_state_publication_1.publishOwnedIssueWorkflowState)({
            context,
            claimedIssue,
            issue,
            responseLanguage,
        });
    }
    await logger.appendSection((0, log_text_1.issueLogSection)("publishAborted"), details);
    await logger.appendSection((0, log_text_1.issueLogSection)("interruptReason"), reason);
    await logger.appendSection((0, log_text_1.issueLogSection)("resolvedTransition"), (0, presentation_1.renderStatusTransitionSection)({
        workflowLabels,
        from: claimedIssue.aiRunningSnapshot.status,
        to: reason === "new-comment"
            ? claimedIssue.aiRunningSnapshot.status
            : issue.workflowState.status,
        facts: shared_1.EMPTY_FACTS,
        note: reason,
        locale: logger.locale,
    }));
    const summary = (0, freshness_1.buildInterruptedSummary)(issue, reason, responseLanguage);
    await logger.appendSection((0, log_text_1.issueLogSection)("finalSummary"), (0, presentation_1.renderCliSummary)(summary, logger.locale));
    return summary;
}
async function handleForeignInputDriftInterruptedIssue(input) {
    await input.logger.appendSection((0, log_text_1.issueLogSection)("publishAborted"), input.details);
    await input.logger.appendSection((0, log_text_1.issueLogSection)("interruptReason"), "state-changed");
    const notice = isAssigneeDrift(input.claimedIssue, input.issue)
        ? buildAssigneeDriftNoticeBody(input.claimedIssue, input.issue, input.responseLanguage)
        : (0, approval_instructions_1.buildForeignApprovalSummary)(input.responseLanguage, input.issue.id);
    const persistedWorkflowState = await (0, workflow_state_read_support_1.resolvePersistedIssueWorkflowState)(input.context.ticketTracker, input.issue.id);
    const assigneeGate = (0, assignee_resolution_1.resolveWorkflowAssigneeGate)({
        assignees: input.issue.assignees,
    });
    const currentWorkflowState = persistedWorkflowState;
    const needsHumanReason = assigneeGate.kind === "ok"
        ? types_3.WORKER_NEEDS_HUMAN_REASONS.foreignApproval
        : "assignee_resolution";
    const handoffSummary = assigneeGate.kind === "ok"
        ? notice
        : (0, assignee_resolution_1.buildAssigneeResolutionSummary)(input.responseLanguage, assigneeGate.kind);
    const summary = await (0, handoffs_1.handOffClaimedIssueToHuman)({
        context: input.context,
        claimedIssue: input.claimedIssue,
        issue: input.issue,
        logger: input.logger,
        failureSeed: {
            issue: input.issue,
            branchName: input.issue.sourceBranchName ??
                (0, default_issue_workspace_manager_1.resolveIssueBranchName)(input.context.config.branchPrefix, input.issue),
            pullRequestUrl: null,
            commentUrl: null,
            consecutiveAiRuns: input.claimedIssue.previousConsecutiveAiRuns,
            totalAiRunHours: currentWorkflowState?.totalAiRunHours ?? 0,
            runMode: types_2.WORKER_RUN_MODES.workflowExecute,
            workflowTemplateName: currentWorkflowState === null
                ? null
                : (0, template_bindings_1.getWorkflowTemplateName)((0, template_bindings_1.getActiveWorkflowTemplateBinding)(currentWorkflowState)),
            workflowState: currentWorkflowState,
            ...(assigneeGate.kind !== "ok"
                ? { assigneeResolution: assigneeGate.kind }
                : {}),
            needsHumanReason,
        },
        summary: handoffSummary,
        needsHumanReason,
        responseLanguage: input.responseLanguage,
        workflowLabels: input.workflowLabels,
        dependencies: {
            publishTransitionSummary: transition_summary_1.publishTransitionSummary,
        },
        recordsRunOutcome: false,
        incrementsConsecutiveAiRuns: false,
    });
    await publishAssigneeDriftPullRequestCommentIfNeeded(input.context, input.issue, notice, input.responseLanguage);
    return summary;
}
function isAssigneeDrift(claimedIssue, issue) {
    const previousAssignee = (0, shared_1.resolveSingleAssignee)(claimedIssue.originalIssue);
    if (previousAssignee === null) {
        return false;
    }
    return !(issue.assignees.length === 1 && issue.assignees[0] === previousAssignee);
}
function buildAssigneeDriftNoticeBody(claimedIssue, issue, responseLanguage) {
    const previousAssignee = (0, shared_1.resolveSingleAssignee)(claimedIssue.originalIssue) ?? "unassigned";
    const nextAssignee = issue.assignees.join(", ") || "unassigned";
    return (0, catalog_1.translate)(responseLanguage, "run.summary.assigneeDrift", {
        previousAssignee,
        nextAssignee,
    });
}
async function publishAssigneeDriftPullRequestCommentIfNeeded(context, issue, summary, responseLanguage) {
    if (issue.kind !== types_1.TRACKER_ITEM_KINDS.issue) {
        return;
    }
    const branchName = issue.sourceBranchName ??
        (0, default_issue_workspace_manager_1.resolveIssueBranchName)(context.config.branchPrefix, issue);
    try {
        const pullRequest = await context.repositoryHost.findPullRequestByBranch(branchName);
        if (pullRequest?.state === "open") {
            const pullRequestThread = await context.ticketTracker.getPullRequestThreadRecord({
                pullRequestNumber: pullRequest.number,
                pullRequestUrl: pullRequest.url,
            });
            if (pullRequestThread === null) {
                return;
            }
            const publisher = (0, publisher_factory_1.createRunWorkflowPublisher)(context);
            await publisher.publishWorkflow({
                target: {
                    kind: "pull_request_thread",
                    pullRequestThreadId: pullRequestThread.id,
                    sourceIssueId: issue.id,
                    preloadedIssue: pullRequestThread,
                },
                workflowState: null,
                visibleUpdate: {
                    kind: "feedback",
                    summary,
                },
                commentCursorPolicy: {
                    mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve,
                },
                responseLanguage,
            });
        }
    }
    catch {
        // Best effort. The handoff still succeeds even if the PR notice fails.
    }
}
function isIssueBodyDrift(claimedIssue, issue) {
    return claimedIssue.originalIssue.body !== issue.body;
}
async function persistFreshnessRerunCursor(input) {
    const publication = await (0, owned_workflow_state_publication_1.resolveOwnedIssueWorkflowStatePublication)({
        context: input.context,
        claimedIssue: input.claimedIssue,
        issue: input.freshnessAfter.latestIssue,
        comments: input.freshnessAfter.comments,
        responseLanguage: input.logger.locale,
    });
    if (publication === null) {
        return;
    }
    const nextScannedCursor = input.freshnessAfter.cursor;
    if (publication.sourceLastScannedCommentCursor?.commentId ===
        nextScannedCursor?.commentId &&
        publication.sourceLastScannedCommentCursor?.createdAt ===
            nextScannedCursor?.createdAt &&
        publication.currentWorkflowState.consecutiveAiRuns ===
            input.freshnessAfter.currentConsecutiveAiRuns &&
        publication.currentWorkflowState.totalAiRunHours ===
            input.freshnessAfter.currentTotalAiRunHours) {
        return;
    }
    try {
        await publication.publishWorkflowState({
            ...publication.currentWorkflowState,
            consecutiveAiRuns: input.freshnessAfter.currentConsecutiveAiRuns,
            totalAiRunHours: input.freshnessAfter.currentTotalAiRunHours,
            lastScannedCommentCursor: nextScannedCursor,
        });
    }
    catch (error) {
        await (0, effect_log_1.appendEffectFailureLog)(input.logger, [
            (0, log_text_1.issueLogField)("effect", "advance_scanned_comment_cursor"),
            (0, log_text_1.issueLogField)("source", publication.sourceCommentId),
            (0, log_text_1.issueLogField)("error", error instanceof Error ? error.message : String(error)),
        ]);
    }
}
