"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.runServiceStatePatchPreflight = runServiceStatePatchPreflight;
const service_repo_state_1 = require("../../core/service-repo-state");
const service_repo_state_storage_1 = require("../../core/service-repo-state-storage");
const types_1 = require("../../platform/ticket-tracker/types");
const types_2 = require("../../platform/workflow-state/types");
const state_patch_1 = require("../state-patch");
const shared_1 = require("../service/shared");
const drain_1 = require("./drain");
const events_1 = require("./events");
const repo_helpers_1 = require("./repo-helpers");
const tracked_project_1 = require("./tracked-project");
async function runServiceStatePatchPreflight(input) {
    if (input.issues.length === 0) {
        return createReadyStatePatchPreflightResult();
    }
    const patchableIssues = input.issues.filter((issue) => !(0, repo_helpers_1.shouldSkipForServiceRuleLabels)(issue.workflowState.rules));
    if (patchableIssues.length === 0) {
        return createReadyStatePatchPreflightResult();
    }
    const drainSignal = input.drainSignal;
    const result = await (0, state_patch_1.patchCanonicalWorkflowStates)({
        cwd: input.entry.cwd,
        config: input.config,
        ticketTracker: input.ticketTracker,
        issues: patchableIssues,
        dryRun: false,
        ...(input.homeDirectory ? { homeDirectory: input.homeDirectory } : {}),
        ...(drainSignal
            ? {
                shouldStop: async () => await (0, drain_1.isServiceDrainRequested)(drainSignal),
            }
            : {}),
    });
    if (!result.completed) {
        return {
            completed: false,
            retryDelayMs: null,
            isolatedIssueIds: resolveIsolatedPatchIssueIds(result),
        };
    }
    if (result.summary.updated > 0) {
        console.log(`[${(0, tracked_project_1.formatTrackedProjectLabel)(input.entry)}] ${(0, state_patch_1.formatStatePatchSummary)(result.summary)}`);
        console.log(`[${(0, tracked_project_1.formatTrackedProjectLabel)(input.entry)}] state patch backup: ${result.backupFilePath}`);
    }
    const repoBlockingOutcome = result.outcomes.find(isRepoBlockingOutcome);
    const isolatedIssueIds = resolveIsolatedPatchIssueIds(result);
    if (repoBlockingOutcome === undefined) {
        await syncIsolatedPatchIssuesForHumanReview(input.ticketTracker, result);
        logIsolatedPatchIssues(input.entry, result);
        return { completed: true, retryDelayMs: null, isolatedIssueIds };
    }
    const reason = formatBlockingStatePatchReason(repoBlockingOutcome);
    (0, service_repo_state_1.recordRepoCooldown)(input.repoStateRegistry, input.trackedProjectKey, {
        nextRetryAtMs: Date.now() + input.retryMs,
        reason,
        lastError: reason,
        updatedAt: new Date().toISOString(),
    });
    await (0, service_repo_state_storage_1.persistRepoServiceStateRegistrySafely)(input.repoStateRegistry, input.homeDirectory);
    console.error(`[${(0, tracked_project_1.formatTrackedProjectLabel)(input.entry)}] ${reason}`);
    await (0, events_1.logServiceEvent)({
        type: shared_1.SERVICE_EVENT_TYPES.repoResult,
        ...(0, events_1.buildRepoEventBase)(input.entry),
        cwd: input.entry.cwd,
        result: shared_1.SERVICE_REPO_RESULTS.failed,
        reason,
        timestamp: new Date().toISOString(),
    });
    return { completed: true, retryDelayMs: input.retryMs, isolatedIssueIds };
}
function createReadyStatePatchPreflightResult() {
    return { completed: true, retryDelayMs: null, isolatedIssueIds: new Set() };
}
function isRepoBlockingOutcome(entry) {
    const outcome = entry.outcome;
    return (outcome.kind === "future" ||
        (outcome.kind === "failed" && outcome.scope === "repo"));
}
function resolveIsolatedPatchIssueIds(result) {
    return new Set(result.outcomes.flatMap((entry) => isIssueIsolatedOutcome(entry.outcome) ? [entry.issue.id] : []));
}
function isIssueIsolatedOutcome(outcome) {
    return (outcome.kind === "skipped" ||
        (outcome.kind === "failed" && outcome.scope === "issue"));
}
async function syncIsolatedPatchIssuesForHumanReview(ticketTracker, result) {
    const isolated = result.outcomes.filter((entry) => isIssueIsolatedOutcome(entry.outcome));
    await Promise.all(isolated.map(async (entry) => {
        if (!(0, types_1.hasTrackerWorkflowState)(entry.issue) ||
            entry.issue.workflowState.status === types_2.WORKFLOW_STATUSES.aiNeedsHuman) {
            return;
        }
        const nextWorkflowState = {
            status: types_2.WORKFLOW_STATUSES.aiNeedsHuman,
            rules: [...entry.issue.workflowState.rules],
        };
        await ticketTracker.syncWorkflowState({
            issueId: entry.issue.id,
            previous: entry.issue.workflowState,
            next: nextWorkflowState,
            ...(entry.issue.labelNames !== undefined &&
                entry.issue.labelNames.length > 0
                ? { currentLabelNames: entry.issue.labelNames }
                : {}),
        });
        entry.issue.workflowState = nextWorkflowState;
    }));
}
function logIsolatedPatchIssues(entry, result) {
    const isolated = result.outcomes.filter((outcome) => isIssueIsolatedOutcome(outcome.outcome));
    if (isolated.length === 0) {
        return;
    }
    console.error(`[${(0, tracked_project_1.formatTrackedProjectLabel)(entry)}] Workflow state patch isolated ${isolated
        .map((outcome) => outcome.issue.displayId)
        .join(", ")}; continuing with other issues.`);
}
function formatBlockingStatePatchReason(entry) {
    if (entry.outcome.kind === "future") {
        return entry.outcome.reason;
    }
    if (entry.outcome.kind === "failed") {
        return `Workflow state patch failed for ${entry.issue.displayId}: ${entry.outcome.reason}`;
    }
    return "Workflow state patch could not complete.";
}
