"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isGhInstalled = isGhInstalled;
exports.isGhAuthenticated = isGhAuthenticated;
exports.getRemoteUrl = getRemoteUrl;
exports.isGitHubRemote = isGitHubRemote;
exports.createPullRequest = createPullRequest;
exports.getPrNumberForBranch = getPrNumberForBranch;
exports.fetchUnresolvedReviewThreads = fetchUnresolvedReviewThreads;
exports.getPullRequestMergeability = getPullRequestMergeability;
exports.mergePullRequest = mergePullRequest;
exports.replyToReviewThread = replyToReviewThread;
exports.filterUnresolvedByNonAidev = filterUnresolvedByNonAidev;
exports.resolveReviewThread = resolveReviewThread;
const node_child_process_1 = require("node:child_process");
const platform_1 = require("./platform");
const logger_1 = require("./logger");
function gh(args) {
    const result = (0, platform_1.spawnCommand)('gh', args, {
        encoding: 'utf8',
        timeout: 30000,
    });
    return {
        stdout: result.stdout || '',
        stderr: result.stderr || '',
        status: result.status ?? 1,
    };
}
function git(args) {
    const result = (0, node_child_process_1.spawnSync)('git', args, {
        cwd: process.cwd(),
        encoding: 'utf8',
    });
    return {
        stdout: result.stdout || '',
        stderr: result.stderr || '',
        status: result.status ?? 1,
    };
}
function isGhInstalled() {
    return (0, platform_1.commandExists)('gh');
}
function isGhAuthenticated() {
    if (!isGhInstalled())
        return false;
    const result = gh(['auth', 'status']);
    return result.status === 0;
}
function getRemoteUrl(remote) {
    const result = git(['remote', 'get-url', remote]);
    if (result.status !== 0)
        return '';
    return result.stdout.trim();
}
function isGitHubRemote(remote) {
    const url = getRemoteUrl(remote);
    return /github\.com/i.test(url);
}
function createPullRequest(baseBranch, headBranch, title, body) {
    logger_1.logger.debug(`Creating PR: ${headBranch} → ${baseBranch}`);
    const result = gh([
        'pr', 'create',
        '--base', baseBranch,
        '--head', headBranch,
        '--title', title,
        '--body', body,
    ]);
    if (result.status !== 0) {
        logger_1.logger.warn(`gh pr create failed: ${result.stderr.trim()}`);
        return { success: false, url: '', error: result.stderr.trim() };
    }
    const url = result.stdout.trim();
    logger_1.logger.info(`PR created: ${url}`);
    return { success: true, url, error: '' };
}
function getPrNumberForBranch(branch) {
    const result = gh(['pr', 'view', branch, '--json', 'number']);
    if (result.status !== 0)
        return null;
    try {
        const parsed = JSON.parse(result.stdout);
        return parsed.number ?? null;
    }
    catch {
        return null;
    }
}
function fetchUnresolvedReviewThreads(owner, repo, prNumber) {
    const query = `query($owner: String!, $repo: String!, $prNumber: Int!) {
    repository(owner: $owner, name: $repo) {
      pullRequest(number: $prNumber) {
        reviewThreads(first: 100) {
          nodes {
            id
            isResolved
            path
            line
            comments(first: 50) {
              nodes {
                body
                author { login }
              }
            }
          }
        }
      }
    }
  }`;
    const result = gh([
        'api', 'graphql',
        '-f', `query=${query}`,
        '-F', `owner=${owner}`,
        '-F', `repo=${repo}`,
        '-F', `prNumber=${prNumber}`,
    ]);
    if (result.status !== 0) {
        logger_1.logger.warn(`Failed to fetch review threads: ${result.stderr.trim()}`);
        return [];
    }
    try {
        const response = JSON.parse(result.stdout);
        const threads = response.data.repository.pullRequest.reviewThreads.nodes;
        return threads
            .filter((t) => !t.isResolved)
            .map((t) => ({
            id: t.id,
            path: t.path,
            line: t.line,
            comments: t.comments.nodes.map((c) => ({
                body: c.body,
                author: c.author?.login ?? 'unknown',
            })),
        }));
    }
    catch (err) {
        logger_1.logger.warn(`Failed to parse review threads: ${err}`);
        return [];
    }
}
/**
 * Returns the GitHub-reported mergeability of the PR for the given branch.
 * `UNKNOWN` covers both transient unknown states (GitHub still computing) and
 * the case where no PR is found / gh fails — callers must treat UNKNOWN as
 * "do not assume it can merge".
 */
function getPullRequestMergeability(branch) {
    const result = gh(['pr', 'view', branch, '--json', 'mergeable']);
    if (result.status !== 0)
        return 'UNKNOWN';
    try {
        const parsed = JSON.parse(result.stdout);
        const value = (parsed.mergeable || '').toUpperCase();
        if (value === 'MERGEABLE')
            return 'MERGEABLE';
        if (value === 'CONFLICTING')
            return 'CONFLICTING';
        return 'UNKNOWN';
    }
    catch {
        return 'UNKNOWN';
    }
}
function mergePullRequest(branch) {
    logger_1.logger.debug(`Merging PR for branch: ${branch} (squash + delete branch)`);
    const result = gh([
        'pr', 'merge', branch,
        '--squash',
        '--delete-branch',
    ]);
    if (result.status !== 0) {
        // gh writes user-facing failure text to either stderr (most cases) or
        // stdout (e.g. "Pull request is not mergeable: ..."). Combine both so
        // callers always get something descriptive instead of an empty string.
        const stderr = result.stderr.trim();
        const stdout = result.stdout.trim();
        const combined = [stderr, stdout].filter(Boolean).join('\n').trim();
        const error = combined || `gh pr merge exited with status ${result.status}`;
        return { success: false, error };
    }
    return { success: true, error: '' };
}
function replyToReviewThread(threadId, body) {
    const mutation = `mutation($threadId: ID!, $body: String!) {
    addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) {
      comment { id }
    }
  }`;
    const result = gh([
        'api', 'graphql',
        '-f', `query=${mutation}`,
        '-F', `threadId=${threadId}`,
        '-F', `body=${body}`,
    ]);
    if (result.status !== 0) {
        logger_1.logger.debug(`Failed to reply to thread ${threadId}: ${result.stderr.trim()}`);
        return false;
    }
    return true;
}
function filterUnresolvedByNonAidev(threads, commentPrefix) {
    return threads.filter((thread) => {
        if (thread.comments.length === 0)
            return true;
        const lastComment = thread.comments[thread.comments.length - 1];
        return !lastComment.body.startsWith(commentPrefix);
    });
}
function resolveReviewThread(threadId) {
    const mutation = `mutation($threadId: ID!) {
    resolveReviewThread(input: { threadId: $threadId }) {
      thread { isResolved }
    }
  }`;
    const result = gh([
        'api', 'graphql',
        '-f', `query=${mutation}`,
        '-F', `threadId=${threadId}`,
    ]);
    if (result.status !== 0) {
        logger_1.logger.debug(`Failed to resolve thread ${threadId}: ${result.stderr.trim()}`);
        return false;
    }
    return true;
}
//# sourceMappingURL=github.js.map