import { isAuthorAllowed } from './filter.js';
import { listCommitStatuses, listOpenPRs, listOrgRepos, listPRComments, listPRCommitsDetailed, listUserRepos, prHasCrossCheckComment, } from '../github/client.js';
import { foldPRStatus, isStale, } from './pr-status.js';
async function expandToRepos(scopes, token) {
    const repos = [];
    await Promise.all(scopes.map(async (scope) => {
        if ('org' in scope) {
            try {
                const orgRepos = await listOrgRepos(scope.org, token);
                for (const { owner, name } of orgRepos)
                    repos.push({ owner, repo: name });
            }
            catch { /* skip org on API error */ }
        }
        else {
            repos.push({ owner: scope.owner, repo: scope.repo });
        }
    }));
    return repos;
}
async function expandToReposWithFailures(scopes, token) {
    const expanded = await Promise.all(scopes.map(async (scope) => {
        if ('org' in scope) {
            try {
                const orgRepos = await listOrgRepos(scope.org, token);
                return {
                    repos: orgRepos.map(({ owner, name }) => ({ owner, repo: name })),
                    failures: [],
                };
            }
            catch (err) {
                return {
                    repos: [],
                    failures: [{ stage: 'scope', owner: scope.org, message: err instanceof Error ? err.message : String(err) }],
                };
            }
        }
        return { repos: [{ owner: scope.owner, repo: scope.repo }], failures: [] };
    }));
    return {
        repos: expanded.flatMap(result => result.repos),
        failures: expanded.flatMap(result => result.failures),
    };
}
// Build backtrace scopes from config (used by serve mode; watch mode passes
// its already-resolved scopes array directly).
export async function buildScopesFromConfig(config, token) {
    const scopes = [
        ...config.orgs.map(org => ({ org })),
        ...config.repos.map(r => ({ owner: r.owner, repo: r.name })),
    ];
    await Promise.all(config.users.map(async (user) => {
        try {
            const repos = await listUserRepos(user, token);
            for (const { owner, name } of repos)
                scopes.push({ owner, repo: name });
        }
        catch { /* skip user on API error */ }
    }));
    return scopes;
}
// Scan all open PRs in the given scopes and return those that have never
// received a [crosscheck] review comment and pass the allowed_authors filter.
// Results are sorted oldest-first. API errors for individual PRs or repos
// are silently skipped so one bad repo never blocks the rest.
export async function scanUnreviewedPRs(scopes, config, token) {
    if (scopes.length === 0) {
        return { queued: [], alreadyReviewed: 0, skippedAuthor: 0 };
    }
    const repoScopes = await expandToRepos(scopes, token);
    const perRepoResults = await Promise.all(repoScopes.map(async ({ owner, repo }) => {
        try {
            const prs = await listOpenPRs(owner, repo, token);
            return prs.map(pr => ({ ...pr, owner, repo }));
        }
        catch {
            return [];
        }
    }));
    const allPRs = perRepoResults.flat();
    const result = { queued: [], alreadyReviewed: 0, skippedAuthor: 0 };
    await Promise.all(allPRs.map(async (pr) => {
        if (!isAuthorAllowed(config.routing.allowed_authors, pr.author)) {
            result.skippedAuthor++;
            return;
        }
        try {
            if (await prHasCrossCheckComment(pr.owner, pr.repo, pr.number, token)) {
                result.alreadyReviewed++;
            }
            else {
                result.queued.push(pr);
            }
        }
        catch { /* skip PR on API error */ }
    }));
    result.queued.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
    return result;
}
export async function scanOpenPRStatuses(scopes, config, token, opts = {}) {
    if (scopes.length === 0) {
        return { statuses: [], failures: [], skippedAuthor: 0, scannedRepos: 0, scannedPRs: 0 };
    }
    const expanded = await expandToReposWithFailures(scopes, token);
    const perRepoResults = await Promise.all(expanded.repos.map(async ({ owner, repo }) => {
        try {
            const prs = await listOpenPRs(owner, repo, token);
            return { prs: prs.map(pr => ({ ...pr, owner, repo })), failures: [] };
        }
        catch (err) {
            return {
                prs: [],
                failures: [{ owner, repo, stage: 'repo', message: err instanceof Error ? err.message : String(err) }],
            };
        }
    }));
    const allPRs = perRepoResults.flatMap(result => result.prs);
    let skippedAuthor = 0;
    const allowedPRs = allPRs.filter((pr) => {
        if (isAuthorAllowed(config.routing.allowed_authors, pr.author))
            return true;
        skippedAuthor++;
        return false;
    });
    const perPRResults = await Promise.all(allowedPRs.map(async (pr) => {
        try {
            const [comments, commits, commitStatuses] = await Promise.all([
                listPRComments(pr.owner, pr.repo, pr.number, token),
                listPRCommitsDetailed(pr.owner, pr.repo, pr.number, token),
                listCommitStatuses(pr.owner, pr.repo, pr.headSha, token),
            ]);
            const statusPR = {
                ...pr,
                commits,
                commitStatuses,
            };
            const status = foldPRStatus(statusPR, comments, opts.logEvents ?? []);
            if (opts.staleAfter !== undefined)
                status.stale = isStale(status, opts.staleAfter);
            return { status, failure: null };
        }
        catch (err) {
            return {
                status: null,
                failure: {
                    owner: pr.owner,
                    repo: pr.repo,
                    pr: pr.number,
                    stage: 'pr',
                    message: err instanceof Error ? err.message : String(err),
                },
            };
        }
    }));
    const statuses = perPRResults.flatMap(result => result.status ? [result.status] : []);
    statuses.sort((a, b) => a.lastActive.getTime() - b.lastActive.getTime());
    return {
        statuses,
        failures: [
            ...expanded.failures,
            ...perRepoResults.flatMap(result => result.failures),
            ...perPRResults.flatMap(result => result.failure ? [result.failure] : []),
        ],
        skippedAuthor,
        scannedRepos: expanded.repos.length,
        scannedPRs: allPRs.length,
    };
}
//# sourceMappingURL=backtrace.js.map