import { Octokit } from 'octokit';
import { createHmac, timingSafeEqual } from 'crypto';
import { buildAnnotation, parseAnnotation, parseAnnotationFields } from '../lib/annotation.js';
import { modelDisplayName } from '../lib/review-models.js';
import { CROSSCHECK_REPO_URL } from '../lib/product.js';
export function createGithubClient(token) {
    return new Octokit({ auth: token });
}
async function readGithubErrorMessage(res) {
    try {
        const body = await res.json();
        if (typeof body.message === 'string' && body.message.trim().length > 0)
            return body.message;
    }
    catch {
        // Fall back to status text when GitHub returns a non-JSON error body.
    }
    return res.statusText || `HTTP ${res.status}`;
}
async function throwGithubRequestError(res, context) {
    const message = await readGithubErrorMessage(res);
    throw new Error(`GitHub API request failed (${context}) [${res.status}]: ${message}`);
}
function repoPath(owner, repo) {
    return `${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`;
}
export function verifyWebhookSignature(payload, signature, secret) {
    const expected = `sha256=${createHmac('sha256', secret).update(payload).digest('hex')}`;
    try {
        return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
    }
    catch {
        return false;
    }
}
export async function getPRDiff(octokit, owner, repo, pullNumber) {
    const { data } = await octokit.rest.pulls.get({
        owner,
        repo,
        pull_number: pullNumber,
        mediaType: { format: 'diff' },
    });
    return data;
}
export async function registerRepoWebhook(owner, repo, webhookUrl, secret, token) {
    const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/hooks`, {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${token}`,
            Accept: 'application/vnd.github+json',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            name: 'web',
            active: true,
            events: ['pull_request', 'issue_comment'],
            config: { url: webhookUrl, content_type: 'json', secret },
        }),
    });
    if (!res.ok) {
        const err = await res.json();
        throw new Error(`Failed to register repo webhook [${res.status}]: ${err.message ?? res.statusText}`);
    }
    const data = await res.json();
    return data.id;
}
export async function patchRepoWebhookEvents(owner, repo, hookId, events, token) {
    const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/hooks/${hookId}`, {
        method: 'PATCH',
        headers: {
            Authorization: `Bearer ${token}`,
            Accept: 'application/vnd.github+json',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ events }),
    });
    if (!res.ok) {
        const err = await res.json();
        throw new Error(`Failed to patch repo webhook [${res.status}]: ${err.message ?? res.statusText}`);
    }
}
export async function deleteRepoWebhook(owner, repo, hookId, token) {
    await fetch(`https://api.github.com/repos/${owner}/${repo}/hooks/${hookId}`, {
        method: 'DELETE',
        headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' },
    });
}
export async function registerOrgWebhook(org, webhookUrl, secret, token) {
    const res = await fetch(`https://api.github.com/orgs/${org}/hooks`, {
        method: 'POST',
        headers: {
            Authorization: `Bearer ${token}`,
            Accept: 'application/vnd.github+json',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            name: 'web',
            active: true,
            events: ['pull_request', 'issue_comment'],
            config: { url: webhookUrl, content_type: 'json', secret },
        }),
    });
    if (!res.ok) {
        const err = await res.json();
        throw new Error(`Failed to register org webhook [${res.status}]: ${err.message ?? res.statusText}`);
    }
    const data = await res.json();
    return data.id;
}
export async function patchOrgWebhookEvents(org, hookId, events, token) {
    const res = await fetch(`https://api.github.com/orgs/${org}/hooks/${hookId}`, {
        method: 'PATCH',
        headers: {
            Authorization: `Bearer ${token}`,
            Accept: 'application/vnd.github+json',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ events }),
    });
    if (!res.ok) {
        const err = await res.json();
        throw new Error(`Failed to patch org webhook [${res.status}]: ${err.message ?? res.statusText}`);
    }
}
export async function deleteOrgWebhook(org, hookId, token) {
    await fetch(`https://api.github.com/orgs/${org}/hooks/${hookId}`, {
        method: 'DELETE',
        headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' },
    });
}
export async function listUserOrgs(token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/user/memberships/orgs?state=active&per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (!res.ok)
            break;
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const m of data)
            results.push(m.organization.login);
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function checkRepoAccessible(owner, repo, token) {
    const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
        headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' },
    });
    return res.ok;
}
export async function listUserRepos(username, token, isSelf = false) {
    const results = [];
    let page = 1;
    while (true) {
        const url = isSelf
            ? `https://api.github.com/user/repos?affiliation=owner&visibility=all&per_page=100&page=${page}`
            : `https://api.github.com/users/${encodeURIComponent(username)}/repos?per_page=100&page=${page}&type=owner`;
        const res = await fetch(url, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (res.status === 404)
            return results;
        if (!res.ok)
            await throwGithubRequestError(res, `list user repos for ${username}`);
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const repo of data) {
            if (!repo.archived && repo.owner.login === username)
                results.push({ owner: repo.owner.login, name: repo.name });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listOrgRepos(org, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/orgs/${encodeURIComponent(org)}/repos?per_page=100&page=${page}&sort=pushed&type=all`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (res.status === 404)
            return results;
        if (!res.ok)
            throw new Error(`Failed to list org repos [${res.status}]: ${res.statusText} (GitHub API request failed)`);
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const repo of data) {
            if (!repo.archived) {
                results.push({ owner: org, name: repo.name, pushedAt: repo.pushed_at ? new Date(repo.pushed_at) : null });
            }
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listOpenPRs(owner, repo, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${repoPath(owner, repo)}/pulls?state=open&per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (res.status === 404)
            return results;
        if (!res.ok) {
            const message = await readGithubErrorMessage(res);
            throw new Error(`Failed to list open PRs for ${owner}/${repo} page ${page} [${res.status}]: ${message}`);
        }
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const pr of data) {
            results.push({
                number: pr.number,
                title: pr.title,
                author: pr.user?.login ?? 'ghost',
                headSha: pr.head.sha,
                headRef: pr.head.ref,
                headRepo: pr.head.repo?.full_name ?? null,
                baseRef: pr.base.ref,
                body: pr.body,
                createdAt: pr.created_at,
                updatedAt: pr.updated_at,
                url: pr.html_url,
            });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
async function githubJson(url, token, label) {
    const res = await fetch(url, {
        headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' },
    });
    if (!res.ok) {
        let message = res.statusText;
        try {
            const data = await res.json();
            message = data.message ?? message;
        }
        catch { /* keep status text */ }
        const context = res.status === 403 || res.status === 429
            ? 'GitHub rate limit or secondary rate limit'
            : 'GitHub API request';
        throw new Error(`${label} failed [${res.status}]: ${context}: ${message}`);
    }
    return await res.json();
}
export async function listOrgReposForScan(org, token) {
    const results = [];
    let page = 1;
    while (true) {
        const data = await githubJson(`https://api.github.com/orgs/${encodeURIComponent(org)}/repos?per_page=100&page=${page}&sort=pushed&type=all`, token, `List repos for org ${org}`);
        if (data.length === 0)
            break;
        for (const repo of data) {
            if (!repo.archived)
                results.push({ owner: org, name: repo.name });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listUserReposForScan(username, token, isSelf) {
    const results = [];
    let page = 1;
    while (true) {
        const url = isSelf
            ? `https://api.github.com/user/repos?affiliation=owner&visibility=all&per_page=100&page=${page}`
            : `https://api.github.com/users/${encodeURIComponent(username)}/repos?per_page=100&page=${page}&type=owner`;
        const data = await githubJson(url, token, `List repos for user ${username}`);
        if (data.length === 0)
            break;
        for (const repo of data) {
            if (!repo.archived && repo.owner.login.toLowerCase() === username.toLowerCase()) {
                results.push({ owner: repo.owner.login, name: repo.name });
            }
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listOpenPRsForScan(owner, repo, token) {
    const results = [];
    let page = 1;
    while (true) {
        const data = await githubJson(`https://api.github.com/repos/${repoPath(owner, repo)}/pulls?state=open&per_page=100&page=${page}`, token, `List open PRs for ${owner}/${repo}`);
        if (data.length === 0)
            break;
        for (const pr of data) {
            results.push({
                number: pr.number,
                title: pr.title,
                author: pr.user?.login ?? 'unknown',
                headSha: pr.head.sha,
                headRef: pr.head.ref,
                headRepo: pr.head.repo?.full_name ?? null,
                baseRef: pr.base.ref,
                createdAt: pr.created_at,
                updatedAt: pr.updated_at,
                url: pr.html_url,
            });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listIssueCommentsForScan(owner, repo, issueNumber, token) {
    const results = [];
    let page = 1;
    while (true) {
        const data = await githubJson(`https://api.github.com/repos/${repoPath(owner, repo)}/issues/${issueNumber}/comments?per_page=100&page=${page}&sort=created&direction=asc`, token, `List comments for ${owner}/${repo}#${issueNumber}`);
        if (data.length === 0)
            break;
        for (const comment of data) {
            results.push({
                id: comment.id,
                author: comment.user?.login ?? 'unknown',
                body: comment.body ?? '',
                createdAt: comment.created_at,
                updatedAt: comment.updated_at,
            });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listPRComments(owner, repo, prNumber, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/comments?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (!res.ok)
            throw new Error(`Failed to list PR comments [${res.status}]: ${res.statusText}`);
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const comment of data) {
            results.push({
                id: comment.id,
                body: comment.body,
                createdAt: comment.created_at,
                updatedAt: comment.updated_at,
            });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listPRCommitsDetailed(owner, repo, prNumber, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/commits?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (!res.ok)
            throw new Error(`Failed to list PR commits [${res.status}]: ${res.statusText}`);
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const commit of data) {
            const committedAt = commit.commit.committer.date ?? commit.commit.author.date;
            if (committedAt)
                results.push({ sha: commit.sha, committedAt });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listCommitStatuses(owner, repo, sha, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/commits/${sha}/statuses?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (!res.ok) {
            if (page === 1)
                throw new Error(`Failed to list commit statuses [${res.status}]: ${res.statusText}`);
            break;
        }
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const status of data) {
            results.push({ context: status.context, state: status.state, updatedAt: status.updated_at });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listIssueComments(owner, repo, issueNumber, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${repoPath(owner, repo)}/issues/${issueNumber}/comments?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (res.status === 404)
            return results;
        if (!res.ok)
            await throwGithubRequestError(res, `list issue comments for ${owner}/${repo}#${issueNumber}`);
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const comment of data) {
            results.push({ body: comment.body, createdAt: comment.created_at, updatedAt: comment.updated_at });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listPRReviewComments(owner, repo, pullNumber, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${repoPath(owner, repo)}/pulls/${pullNumber}/comments?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (res.status === 404)
            return results;
        if (!res.ok)
            await throwGithubRequestError(res, `list PR review comments for ${owner}/${repo}#${pullNumber}`);
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const comment of data) {
            results.push({ body: comment.body, createdAt: comment.created_at, updatedAt: comment.updated_at });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listPRCommitActivity(owner, repo, pullNumber, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${repoPath(owner, repo)}/pulls/${pullNumber}/commits?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (res.status === 404)
            return results;
        if (!res.ok)
            await throwGithubRequestError(res, `list PR commits for ${owner}/${repo}#${pullNumber}`);
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const commit of data) {
            const committedAt = commit.commit.committer?.date ?? commit.commit.author?.date;
            if (committedAt)
                results.push({ sha: commit.sha, committedAt });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listCheckRuns(owner, repo, ref, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${repoPath(owner, repo)}/commits/${encodeURIComponent(ref)}/check-runs?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (res.status === 404)
            return results;
        if (!res.ok)
            await throwGithubRequestError(res, `list check runs for ${owner}/${repo}@${ref}`);
        const data = await res.json();
        if (data.check_runs.length === 0)
            break;
        for (const run of data.check_runs) {
            const updatedAt = run.completed_at ?? run.started_at;
            if (updatedAt)
                results.push({ name: run.name, status: run.status, conclusion: run.conclusion, updatedAt });
        }
        if (data.check_runs.length < 100)
            break;
        page++;
    }
    return results;
}
export async function listTimelineEvents(owner, repo, issueNumber, token) {
    const results = [];
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${repoPath(owner, repo)}/issues/${issueNumber}/timeline?per_page=100&page=${page}`, {
            headers: {
                Authorization: `Bearer ${token}`,
                Accept: 'application/vnd.github+json',
            },
        });
        if (res.status === 404)
            return results;
        if (!res.ok)
            await throwGithubRequestError(res, `list timeline events for ${owner}/${repo}#${issueNumber}`);
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const event of data) {
            if (event.created_at)
                results.push({ name: event.event, updatedAt: event.created_at });
        }
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
// Returns true if any comment on the PR contains '[crosscheck]' — meaning it
// has already been reviewed by this tool.
export async function prHasCrossCheckComment(owner, repo, prNumber, token) {
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/comments?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (!res.ok)
            return false;
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const comment of data) {
            if (comment.body.includes('[crosscheck]'))
                return true;
        }
        if (data.length < 100)
            break;
        page++;
    }
    return false;
}
// True iff `body` is a fresh review comment that a recheck should link back to.
// Classification cascade:
//   1. Contract annotation parsed by annotation.ts with explicit type=review
//      → review. Other explicit types are not reviews.
//   2. Legacy annotations without type= fall through to the header check so
//      pre-type rechecks are still excluded by the "> Recheck of" prefix.
//   3. Bare summary/notification markers (`fix_failed`, `fix_applied`,
//      `conflict_resolved`, `no_diff_change`, future bare markers) are not reviews.
//   4. No annotation → legacy pre-annotation comment. Identify reviews by the
//      canonical "### Code Review by" header, exclude rechecks by the
//      "> Recheck of" prefix.
export function isFreshReviewComment(body) {
    const fields = parseAnnotationFields(body);
    const parsed = parseAnnotation(body);
    if (parsed && fields?.has('type')) {
        // Explicit type= field present — trust it directly.
        return parsed.type === 'review';
    }
    if (parsed && !fields?.has('type')) {
        // Pre-type-era annotation: type was defaulted to 'review', not stated.
        // Fall through to the header/prefix check so legacy rechecks are excluded.
    }
    else if (fields && !parsed) {
        // Has an annotation tag but no origin+reviewer — bare summary marker.
        return false;
    }
    // No annotation (or pre-type fallthrough): use header + recheck-prefix heuristic.
    return body.includes('### Code Review by') && !body.startsWith('> Recheck of');
}
// Returns the GitHub comment ID of the most recent crosscheck REVIEW comment.
// Used by recheck steps to link back to the original review that raised issues.
// Classification is delegated to `isFreshReviewComment` — annotation is the
// source of truth, with a header-based fallback for pre-annotation comments.
export async function getLastCrossCheckCommentId(owner, repo, prNumber, token) {
    return (await getLastCrossCheckReviewComment(owner, repo, prNumber, token))?.id;
}
export async function getLastCrossCheckReviewComment(owner, repo, prNumber, token) {
    let page = 1;
    let lastComment;
    while (true) {
        const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/comments?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (!res.ok)
            break;
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const comment of data) {
            if (isFreshReviewComment(comment.body)) {
                lastComment = { id: comment.id, body: comment.body };
            }
        }
        if (data.length < 100)
            break;
        page++;
    }
    return lastComment;
}
/**
 * Fetch one page of PR issue comments. All raw GitHub API calls for PR comments
 * are routed through this function so auth headers and URL construction stay in
 * the client layer.
 *
 * Returns the comments and the last-page number parsed from the Link header
 * (null when there is only one page or the header is absent).
 */
export async function fetchPRCommentPage(owner, repo, prNumber, token, opts = {}) {
    const params = new URLSearchParams({ per_page: '100' });
    if (opts.page !== undefined)
        params.set('page', String(opts.page));
    if (opts.since !== undefined)
        params.set('since', opts.since);
    const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/comments?${params}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
    if (!res.ok)
        return { comments: [], lastPage: null };
    const comments = await res.json();
    const link = res.headers.get('link') ?? '';
    const m = link.match(/page=(\d+)>;\s*rel="last"/);
    return { comments, lastPage: m ? parseInt(m[1], 10) : null };
}
export async function fetchPRCommitPage(owner, repo, prNumber, token, page = 1) {
    const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/commits?per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
    if (!res.ok)
        return [];
    return await res.json();
}
export async function getPRCommits(owner, repo, prNumber, token) {
    const results = [];
    let page = 1;
    while (true) {
        const data = await fetchPRCommitPage(owner, repo, prNumber, token, page);
        if (data.length === 0)
            break;
        for (const c of data)
            results.push(c.commit.message);
        if (data.length < 100)
            break;
        page++;
    }
    return results;
}
export async function getCommitMessage(owner, repo, sha, token) {
    const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/commits/${sha}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
    if (!res.ok)
        return null;
    const data = await res.json();
    return data.commit?.message ?? null;
}
export async function findOrgWebhook(org, url, token) {
    const res = await fetch(`https://api.github.com/orgs/${org}/hooks?per_page=100`, {
        headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' },
    });
    if (!res.ok)
        return null;
    const hooks = await res.json();
    return hooks.find(h => h.config.url === url)?.id ?? null;
}
export async function findRepoWebhook(owner, repo, url, token) {
    const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/hooks?per_page=100`, {
        headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' },
    });
    if (!res.ok)
        return null;
    const hooks = await res.json();
    return hooks.find(h => h.config.url === url)?.id ?? null;
}
export async function fetchActiveRepos(login, token) {
    const results = [];
    const now = Date.now();
    const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
    const ninetyDaysMs = 90 * 24 * 60 * 60 * 1000;
    let page = 1;
    while (true) {
        const res = await fetch(`https://api.github.com/user/repos?affiliation=owner&visibility=all&sort=pushed&per_page=100&page=${page}`, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' } });
        if (!res.ok)
            break;
        const data = await res.json();
        if (data.length === 0)
            break;
        for (const r of data) {
            if (r.archived || r.owner.login !== login)
                continue;
            const createdAt = new Date(r.created_at);
            const pushedAt = r.pushed_at ? new Date(r.pushed_at) : createdAt;
            const createdAgo = now - createdAt.getTime();
            const pushedAgo = now - pushedAt.getTime();
            const tier = createdAgo < sevenDaysMs ? 1 : pushedAgo < ninetyDaysMs ? 2 : 3;
            results.push({ tier, fullName: `${login}/${r.name}`, pushedAt, createdAt });
        }
        if (data.length < 100)
            break;
        page++;
    }
    // Tier 1 first (newest created), within each tier sort by pushedAt desc
    results.sort((a, b) => a.tier !== b.tier ? a.tier - b.tier : b.pushedAt.getTime() - a.pushedAt.getTime());
    return results;
}
export function buildReviewCommentBody(input) {
    const reviewer = input.reviewer;
    const brand = input.brand ?? {};
    const model = input.model ?? 'default';
    const round = input.round ?? 1;
    const stepType = input.stepType ?? (input.isRecheck ? 'recheck' : 'review');
    const serviceName = brand.service_name || 'crosscheck';
    const isClaude = reviewer === 'claude';
    const vendorLabel = isClaude ? '🤖 Claude Code' : '⚡ Codex';
    const modelDisplay = modelDisplayName(model);
    const serviceSegment = serviceName !== 'crosscheck' ? ` · ${serviceName}` : '';
    const modelSegment = modelDisplay ? ` · ${modelDisplay}` : '';
    const header = `### ${stepVerb(stepType)} by ${vendorLabel}${modelSegment}${serviceSegment}\n\n`;
    const defaultAttribution = isClaude
        ? `_Reviewed with [Claude Code](https://claude.ai/code) via [Crosscheck](${CROSSCHECK_REPO_URL})_`
        : `_Reviewed with [OpenAI Codex](https://openai.com/codex) via [Crosscheck](${CROSSCHECK_REPO_URL})_`;
    const attribution = brand.reviewer_attribution || defaultAttribution;
    const footer = `\n\n---\n${attribution}`;
    const customHeader = brand.comment_header ? `${brand.comment_header}\n\n` : '';
    const customFooter = brand.comment_footer ? `\n\n${brand.comment_footer}` : '';
    const annotationTag = `\n\n${buildAnnotation({
        origin: input.origin ?? 'human',
        reviewer,
        model,
        type: stepType,
        round,
        verdict: input.verdict ?? 'UNKNOWN',
        service: serviceName,
        ...(input.sha && { sha: input.sha }),
        ...(input.nextStep !== undefined && { next_step: input.nextStep }),
        ...(input.trigger !== undefined && { trigger: input.trigger }),
    })}`;
    const replyPrefix = input.replyToCommentId
        ? `> Recheck of [original review](#issuecomment-${input.replyToCommentId})\n\n`
        : '';
    return customHeader + replyPrefix + header + input.body + footer + customFooter + annotationTag;
}
export async function postReviewComment(octokit, owner, repo, pullNumber, body, reviewer, brand = {}, origin = 'human', verdict, replyToCommentId, isRecheck, model = 'default', stepType, round = 1, sha, nextStep, trigger) {
    const { data: comment } = await octokit.rest.issues.createComment({
        owner,
        repo,
        issue_number: pullNumber,
        body: buildReviewCommentBody({
            body,
            reviewer,
            brand,
            origin,
            verdict,
            replyToCommentId,
            isRecheck,
            model,
            stepType,
            round,
            sha,
            nextStep,
            trigger,
        }),
    });
    return comment.id;
}
function stepVerb(stepType) {
    if (stepType === 'recheck')
        return 'Recheck';
    if (stepType === 'fix')
        return 'Fixes';
    if (stepType === 'conflict-resolve')
        return 'Conflict resolution';
    return 'Code Review';
}
//# sourceMappingURL=client.js.map