/**
 * Re-review narrowing filter + stale-SHA guard (see design.md → Component
 * Design + Error Handling → Stale prior-review SHA).
 *
 * Given the PR's prior provide-remote reviews and a `(rail, project, scope)`
 * tuple, pick the most recent matching review and decide whether the current
 * pass can narrow to `<prior_sha>..<HEAD>`. The guard runs existence + ancestry
 * checks (via an injected {@link GitInvoker}) before declaring a narrowing
 * range, so a rebased/force-pushed/shallow prior SHA never produces a
 * misleading partial range.
 */
/** Does a prior review match the current `(rail, project, scope)` tuple? */
function matchesTuple(review, input) {
    if (input.rail === 'ad-hoc') {
        // Ad-hoc: scope sentinel must match AND there must be no project key.
        return review.scope === 'ad-hoc' && review.project === undefined;
    }
    // Project rail: same project AND same scope.
    return (review.project !== undefined &&
        review.project === input.project &&
        review.scope === input.scope);
}
function mostRecentMatch(input) {
    const matches = input.reviews.filter((r) => matchesTuple(r, input));
    if (matches.length === 0) {
        return null;
    }
    // Descending by submission time — newest matching review wins.
    matches.sort((a, b) => b.submittedAt.localeCompare(a.submittedAt));
    return matches[0];
}
/**
 * Run the two-step stale-SHA guard. In diff-only mode, attempt a single-ref
 * fetch before re-checking existence. Returns true only when the prior SHA both
 * exists locally and is an ancestor of the current PR HEAD.
 */
async function guardPasses(priorSha, input) {
    let exists = await input.git.objectExists(priorSha);
    if (!exists && input.diffOnly) {
        const fetched = await input.git.fetchRef(priorSha);
        if (!fetched) {
            return false;
        }
        exists = await input.git.objectExists(priorSha);
    }
    if (!exists) {
        return false;
    }
    return input.git.isAncestor(priorSha, input.headSha);
}
/**
 * Pick the narrowing target for the current re-review pass.
 */
export async function pickNarrowingTarget(input) {
    const prior = mostRecentMatch(input);
    if (!prior) {
        return {
            kind: 'full-scope-fallback',
            reason: 'no-prior-review',
            prompted: false,
        };
    }
    const passed = await guardPasses(prior.headSha, input);
    if (passed) {
        return {
            kind: 'narrow-range',
            priorSha: prior.headSha,
            headSha: input.headSha,
            // Auto-narrow skips the prompt; otherwise the caller still confirms.
            prompted: !input.autoNarrow,
        };
    }
    // Guard failed. The user explicitly asked to narrow → hard error.
    if (input.forceNarrow) {
        return {
            kind: 'hard-error',
            reason: 'stale-sha',
            priorSha: prior.headSha,
        };
    }
    // Otherwise fall back to full scope. Auto-narrow surfaces this as the
    // auto-fallback notice (no prompt); manual mode does not prompt here either —
    // the fallback is informational.
    return {
        kind: 'full-scope-fallback',
        reason: 'stale-sha',
        priorSha: prior.headSha,
        prompted: false,
    };
}
