"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveMergeFinalizationClosingWorkspace = resolveMergeFinalizationClosingWorkspace;
const issue_branch_target_1 = require("./issue-branch-target");
const run_state_helpers_1 = require("./run-state-helpers");
async function resolveMergeFinalizationClosingWorkspace(input) {
    const baseBranch = (0, issue_branch_target_1.resolveIssueRunBaseBranch)(input.workflowState);
    const expectedCommits = await resolveMergeFinalizationExpectedCommits(input);
    const candidates = buildMergeFinalizationWorkspaceCandidates({
        sourceBranchName: input.sourceBranchName,
        baseBranch,
        expectedCommits,
    });
    if (candidates.length === 0) {
        return { kind: "blocked", reason: "missing_pull_request_commit_oid" };
    }
    const failures = [];
    const workspacesByBranch = new Map();
    const workspaceFailuresByBranch = new Map();
    for (const candidate of candidates) {
        const resolution = await verifyMergeFinalizationWorkspaceCandidate({
            ...input,
            baseBranch,
            candidate,
            workspacesByBranch,
            workspaceFailuresByBranch,
        });
        if (resolution.kind === "ready") {
            return resolution;
        }
        if (resolution.error !== undefined &&
            (0, run_state_helpers_1.isWorkspaceSyncBlockedError)(resolution.error)) {
            return resolution;
        }
        failures.push(resolution.reason);
    }
    return {
        kind: "blocked",
        reason: failures.join("; ") || "workspace_freshness_not_verified",
    };
}
async function verifyMergeFinalizationWorkspaceCandidate(input) {
    try {
        const workspace = await ensureCandidateWorkspace(input);
        const verified = await isExpectedCommitReachableFromWorkspaceHead({
            context: input.context,
            workspacePath: workspace.path,
            expectedCommitOid: input.candidate.expectedCommitOid,
        });
        if (verified) {
            return {
                kind: "ready",
                workspace,
                freshnessExpectation: {
                    expectedCommitOid: input.candidate.expectedCommitOid,
                    expectedCommitKind: input.candidate.expectedCommitKind,
                    pullRequestUrl: input.pullRequest.url,
                    ...(input.pullRequest.number !== undefined
                        ? { pullRequestNumber: input.pullRequest.number }
                        : {}),
                },
            };
        }
        return {
            kind: "blocked",
            reason: `${input.candidate.label}:workspace_head_missing_${input.candidate.expectedCommitKind}:${input.candidate.expectedCommitOid}`,
        };
    }
    catch (error) {
        return {
            kind: "blocked",
            reason: renderCandidateFreshnessFailure(input, error),
            error,
        };
    }
}
async function ensureCandidateWorkspace(input) {
    const previousWorkspaceFailure = input.workspaceFailuresByBranch.get(input.candidate.branchName);
    if (previousWorkspaceFailure !== undefined) {
        throw previousWorkspaceFailure;
    }
    const cachedWorkspace = input.workspacesByBranch.get(input.candidate.branchName);
    if (cachedWorkspace !== undefined) {
        return cachedWorkspace;
    }
    try {
        const workspace = await input.context.issueWorkspaceManager.ensureWorkspace({
            repositoryRoot: input.context.cwd,
            repositoryHost: input.context.config.repositoryHost,
            ticketTracker: input.context.config.ticketTracker,
            issueId: input.issue.id,
            branchName: input.candidate.branchName,
            baseBranch: input.baseBranch,
        });
        input.workspacesByBranch.set(input.candidate.branchName, workspace);
        return workspace;
    }
    catch (error) {
        input.workspaceFailuresByBranch.set(input.candidate.branchName, error);
        throw error;
    }
}
async function resolveMergeFinalizationExpectedCommits(input) {
    const directHead = normalizeCommitOid(input.pullRequest.headRefOid);
    const head = directHead !== null
        ? { oid: directHead, kind: "pull_request_head" }
        : await resolvePullRequestCommitListHead(input);
    const merge = normalizeCommitOid(input.pullRequest.mergeCommitOid);
    return {
        head,
        merge: merge === null ? null : { oid: merge, kind: "pull_request_merge" },
    };
}
async function resolvePullRequestCommitListHead(input) {
    if (input.pullRequest.number === undefined) {
        return null;
    }
    let commits;
    try {
        commits = await input.context.repositoryHost.listPullRequestCommits(input.pullRequest.number);
    }
    catch {
        return null;
    }
    const oid = normalizeCommitOid(commits.at(-1)?.oid);
    return oid === null ? null : { oid, kind: "pull_request_commits" };
}
function buildMergeFinalizationWorkspaceCandidates(input) {
    const candidates = [];
    if (input.expectedCommits.head !== null) {
        candidates.push({
            label: "source_head",
            branchName: input.sourceBranchName,
            expectedCommitOid: input.expectedCommits.head.oid,
            expectedCommitKind: input.expectedCommits.head.kind,
        });
    }
    if (input.expectedCommits.merge !== null) {
        candidates.push({
            label: "source_merge",
            branchName: input.sourceBranchName,
            expectedCommitOid: input.expectedCommits.merge.oid,
            expectedCommitKind: input.expectedCommits.merge.kind,
        }, {
            label: "base_merge",
            branchName: input.baseBranch,
            expectedCommitOid: input.expectedCommits.merge.oid,
            expectedCommitKind: input.expectedCommits.merge.kind,
        });
    }
    if (input.expectedCommits.head !== null) {
        candidates.push({
            label: "base_head",
            branchName: input.baseBranch,
            expectedCommitOid: input.expectedCommits.head.oid,
            expectedCommitKind: input.expectedCommits.head.kind,
        });
    }
    return uniqueMergeFinalizationWorkspaceCandidates(candidates);
}
function uniqueMergeFinalizationWorkspaceCandidates(candidates) {
    const seen = new Set();
    return candidates.filter((candidate) => {
        const key = [
            candidate.branchName,
            candidate.expectedCommitOid,
            candidate.expectedCommitKind,
        ].join("\0");
        if (seen.has(key)) {
            return false;
        }
        seen.add(key);
        return true;
    });
}
async function isExpectedCommitReachableFromWorkspaceHead(input) {
    const workspaceHeadCommitOid = await input.context.gitRepository.getHeadCommitOid(input.workspacePath);
    return (workspaceHeadCommitOid === input.expectedCommitOid ||
        (await input.context.gitRepository.isCommitAncestorOfHead(input.workspacePath, input.expectedCommitOid)));
}
function renderCandidateFreshnessFailure(input, error) {
    if ((0, run_state_helpers_1.isWorkspaceSyncBlockedError)(error)) {
        return `${input.candidate.label}:sync_blocked:${(0, run_state_helpers_1.buildWorkspaceSyncBlockedSummary)({
            responseLanguage: input.responseLanguage,
            reason: error.reason,
            conflictedFiles: error.conflictedFiles ?? [],
        })}`;
    }
    return `${input.candidate.label}:workspace_error:${renderError(error)}`;
}
function normalizeCommitOid(value) {
    const normalized = value?.trim();
    return normalized ? normalized : null;
}
function renderError(error) {
    return error instanceof Error ? error.message : String(error);
}
