"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleClaimedIssueFailure = handleClaimedIssueFailure;
const transient_network_error_1 = require("../../core/transient-network-error");
const network_recovery_state_1 = require("../../core/network-recovery-state");
const tracked_project_1 = require("../../core/tracked-project");
const catalog_1 = require("../../i18n/catalog");
const provider_resolution_1 = require("../../platform/provider-resolution");
const errors_1 = require("../../worker/errors");
const constants_1 = require("../constants");
const log_text_1 = require("../log-text");
const effect_log_1 = require("./effect-log");
const freshness_1 = require("./freshness");
const handoffs_1 = require("./handoffs");
const owned_workflow_state_publication_1 = require("./owned-workflow-state-publication");
const pending_publication_recovery_1 = require("./pending-publication-recovery");
const presentation_1 = require("./presentation");
const publish_1 = require("./publish");
const results_1 = require("./results");
const shared_1 = require("./shared");
const shared_2 = require("./shared");
const types_1 = require("./types");
const WORKER_IDLE_TIMEOUT_HANDOFF_ATTEMPT_COUNT = 2;
async function handleClaimedIssueFailure(context, claimedIssue, logger, failureSeed, error, workflowLabels) {
    await logger.appendSection((0, log_text_1.issueLogSection)("failureDetails"), (0, results_1.renderFailureDetails)(error, logger.locale));
    const partialEffects = (0, results_1.extractEffectExecutionResult)(error, (0, results_1.createEffectExecutionResult)(failureSeed.pullRequestUrl, failureSeed.commentUrl));
    if ((0, transient_network_error_1.isTransientNetworkError)(error)) {
        return handleTransientNetworkFailure({
            context,
            claimedIssue,
            logger,
            failureSeed,
            partialEffects,
            error,
            workflowLabels,
        });
    }
    if (isWorkerIdleTimeout(error)) {
        const recoverySummary = await handleWorkerIdleTimeoutFailure({
            context,
            claimedIssue,
            logger,
            failureSeed,
            partialEffects,
            error,
        });
        if (recoverySummary !== null) {
            return recoverySummary;
        }
    }
    const responseLanguage = context.config.i18n.defaultLanguage;
    const failureSummary = (0, results_1.resolveFailureSummary)(error, responseLanguage, failureSeed.issue.displayId);
    try {
        return await (0, handoffs_1.handOffClaimedIssueToHuman)({
            context,
            claimedIssue,
            issue: failureSeed.issue,
            logger,
            failureSeed,
            summary: failureSummary.public,
            needsHumanReason: resolveFailureNeedsHumanReason(error),
            responseLanguage,
            workflowLabels,
            dependencies: {
                publishTransitionSummary: publish_1.publishTransitionSummary,
            },
            recordsRunOutcome: false,
            incrementsConsecutiveAiRuns: false,
            publicationOrigin: "worker_error_handoff",
        });
    }
    catch (handoffError) {
        const summary = (0, results_1.buildRunCliSummary)({
            issue: failureSeed.issue,
            outcome: constants_1.RUN_ISSUE_OUTCOMES.queuedHuman,
            responseLanguage,
            summary: failureSummary.public,
            commentUrl: partialEffects.commentUrl,
            prUrl: partialEffects.pullRequestUrl,
            pullRequestThreadId: partialEffects.pullRequestThreadId,
            nextStep: (0, catalog_1.translate)(responseLanguage, "run.failureRecovery.internalErrorNext"),
        });
        await logger.appendSection((0, log_text_1.issueLogSection)("finalSummary"), (0, presentation_1.renderCliSummary)(summary, logger.locale));
        if (handoffError instanceof Error) {
            await (0, effect_log_1.appendEffectFailureLog)(logger, [
                (0, results_1.renderFailureDetails)(handoffError, logger.locale),
            ]);
        }
        return summary;
    }
}
async function handleTransientNetworkFailure(input) {
    const responseLanguage = input.context.config.i18n.defaultLanguage;
    const pendingPublicationRecovery = input.error instanceof shared_1.EffectExecutionError
        ? input.error.pendingPublicationRecovery
        : undefined;
    if (pendingPublicationRecovery !== undefined) {
        const recoveryDecision = await (0, pending_publication_recovery_1.schedulePendingWaitHumanPublicationRecovery)({
            context: input.context,
            logger: input.logger,
            issue: input.failureSeed.issue,
            transitionState: pendingPublicationRecovery.transitionState,
            publishRequest: pendingPublicationRecovery.publishRequest,
            effectResult: input.partialEffects,
            error: input.error,
        });
        await releaseIssueOwnershipForRecovery(input);
        await input.logger.appendSection((0, log_text_1.issueLogSection)("finalSummary"), (0, presentation_1.renderCliSummary)(recoveryDecision.summary, input.logger.locale));
        return recoveryDecision.summary;
    }
    await (0, owned_workflow_state_publication_1.publishOwnedIssueWorkflowState)({
        context: input.context,
        claimedIssue: input.claimedIssue,
        issue: input.failureSeed.issue,
        responseLanguage,
    });
    const recoveryDecision = await (0, freshness_1.scheduleTransientNetworkRecovery)(input.context, input.logger, input.failureSeed.issue, input.partialEffects, input.error);
    if (recoveryDecision.kind === "retry") {
        await input.logger.appendSection((0, log_text_1.issueLogSection)("finalSummary"), (0, presentation_1.renderCliSummary)(recoveryDecision.summary, input.logger.locale));
        return recoveryDecision.summary;
    }
    return (0, handoffs_1.handOffClaimedIssueToHuman)({
        context: input.context,
        claimedIssue: input.claimedIssue,
        issue: input.failureSeed.issue,
        logger: input.logger,
        failureSeed: input.failureSeed,
        summary: buildTransientNetworkHandoffSummary(responseLanguage, recoveryDecision.retryAt),
        needsHumanReason: types_1.WORKER_NEEDS_HUMAN_REASONS.systemError,
        responseLanguage,
        workflowLabels: input.workflowLabels,
        dependencies: {
            publishTransitionSummary: publish_1.publishTransitionSummary,
        },
    });
}
async function handleWorkerIdleTimeoutFailure(input) {
    const recoveryDecision = await scheduleWorkerIdleTimeoutRecovery({
        context: input.context,
        logger: input.logger,
        issue: input.failureSeed.issue,
        effectResult: input.partialEffects,
        error: input.error,
    });
    if (recoveryDecision.kind !== "retry") {
        return null;
    }
    await releaseIssueOwnershipForRecovery(input);
    await input.logger.appendSection((0, log_text_1.issueLogSection)("finalSummary"), (0, presentation_1.renderCliSummary)(recoveryDecision.summary, input.logger.locale));
    return recoveryDecision.summary;
}
async function releaseIssueOwnershipForRecovery(input) {
    try {
        await (0, owned_workflow_state_publication_1.publishOwnedIssueWorkflowState)({
            context: input.context,
            claimedIssue: input.claimedIssue,
            issue: input.failureSeed.issue,
            responseLanguage: input.context.config.i18n.defaultLanguage,
        });
    }
    catch (ownershipReleaseError) {
        await input.logger.appendSection((0, log_text_1.issueLogSection)("failureDetails"), (0, results_1.renderFailureDetails)(ownershipReleaseError, input.logger.locale));
    }
}
async function scheduleWorkerIdleTimeoutRecovery(input) {
    const trackedProjectKey = (0, provider_resolution_1.getTrackedProjectKey)((0, tracked_project_1.toTrackedProjectRef)(input.context.config));
    const existingEntry = (await (0, network_recovery_state_1.listRepoNetworkRecoveryEntries)(trackedProjectKey, input.context.logHomeDirectory)).find((entry) => entry.issueId === input.issue.id &&
        entry.reason === shared_2.WORKER_IDLE_TIMEOUT_RECOVERY_REASON);
    const attemptCount = (existingEntry?.attemptCount ?? 0) + 1;
    const retryAt = new Date().toISOString();
    const lastError = (0, results_1.buildFailureSummary)(input.error);
    if (attemptCount >= WORKER_IDLE_TIMEOUT_HANDOFF_ATTEMPT_COUNT) {
        await (0, network_recovery_state_1.removeNetworkRecoveryEntry)(trackedProjectKey, input.issue.id, shared_2.WORKER_IDLE_TIMEOUT_RECOVERY_REASON, input.context.logHomeDirectory);
        await input.logger.appendSection((0, log_text_1.issueLogSection)("networkRecoveryEscalated"), [
            (0, log_text_1.issueLogField)("reason", shared_2.WORKER_IDLE_TIMEOUT_RECOVERY_REASON),
            (0, log_text_1.issueLogField)("attemptCount", attemptCount),
            (0, log_text_1.issueLogField)("retryAt", retryAt),
            (0, log_text_1.issueLogField)("lastError", lastError),
        ].join("\n"));
        return {
            kind: "handoff",
            attemptCount,
            retryAt,
        };
    }
    await (0, network_recovery_state_1.upsertNetworkRecoveryEntry)({
        trackedProjectKey,
        issueId: input.issue.id,
        reason: shared_2.WORKER_IDLE_TIMEOUT_RECOVERY_REASON,
        attemptCount,
        nextRetryAt: retryAt,
        lastError,
        updatedAt: retryAt,
    }, input.context.logHomeDirectory);
    await input.logger.appendSection((0, log_text_1.issueLogSection)("networkRecoveryScheduled"), [
        (0, log_text_1.issueLogField)("reason", shared_2.WORKER_IDLE_TIMEOUT_RECOVERY_REASON),
        (0, log_text_1.issueLogField)("attemptCount", attemptCount),
        (0, log_text_1.issueLogField)("retryAt", retryAt),
    ].join("\n"));
    return {
        kind: "retry",
        attemptCount,
        retryAt,
        summary: buildWorkerIdleTimeoutRecoverySummary({
            issue: input.issue,
            effectResult: input.effectResult,
            responseLanguage: input.context.config.i18n.defaultLanguage,
            retryAt,
        }),
    };
}
function buildWorkerIdleTimeoutRecoverySummary(input) {
    return (0, results_1.buildRunCliSummary)({
        issue: input.issue,
        outcome: constants_1.RUN_ISSUE_OUTCOMES.failed,
        responseLanguage: input.responseLanguage,
        summary: (0, catalog_1.translate)(input.responseLanguage, "run.summary.workerIdleTimeout"),
        commentUrl: input.effectResult.commentUrl,
        prUrl: input.effectResult.pullRequestUrl,
        pullRequestThreadId: input.effectResult.pullRequestThreadId,
        nextStep: (0, catalog_1.translate)(input.responseLanguage, "run.summary.workerIdleTimeout.next"),
        deferredReason: shared_2.WORKER_IDLE_TIMEOUT_RECOVERY_REASON,
        cooldownUntil: input.retryAt,
    });
}
function isWorkerIdleTimeout(error) {
    let current = error;
    while (current instanceof Error) {
        if (current instanceof errors_1.WorkerTimeoutError) {
            return current.timeoutKind === "idle";
        }
        current = "cause" in current ? current.cause : undefined;
    }
    return false;
}
function resolveFailureNeedsHumanReason(error) {
    let current = error;
    while (current instanceof Error) {
        if ((0, errors_1.isWorkerFailureError)(current)) {
            return current.failureKind === errors_1.WORKER_FAILURE_KINDS.timeout
                ? types_1.WORKER_NEEDS_HUMAN_REASONS.timeout
                : types_1.WORKER_NEEDS_HUMAN_REASONS.systemError;
        }
        current = "cause" in current ? current.cause : undefined;
    }
    return types_1.WORKER_NEEDS_HUMAN_REASONS.systemError;
}
function buildTransientNetworkHandoffSummary(responseLanguage, retryAt) {
    return [
        (0, catalog_1.translate)(responseLanguage, "run.summary.transientNetwork.handoff", {
            attemptCount: freshness_1.TRANSIENT_NETWORK_HANDOFF_ATTEMPT_COUNT,
        }),
        (0, catalog_1.translate)(responseLanguage, "run.summary.transientNetwork.handoff.retryAt", {
            retryAt,
        }),
    ].join("\n");
}
