#!/usr/bin/env node
import {
  buildReviewPrompt,
  buildSecurityReviewPrompt,
  truncateDiff
} from "./chunk-HLWUDRBO.js";
import {
  ProviderRegistry
} from "./chunk-2TWARH5X.js";
import "./chunk-HIU2SH4V.js";
import {
  ConfigManager
} from "./chunk-2UGK2RXK.js";
import "./chunk-TZQHYZKT.js";
import {
  VERSION
} from "./chunk-KRU4DFRH.js";
import "./chunk-PDX44BCA.js";

// src/cli/ci.ts
import { execFileSync, execSync } from "child_process";
var CI_COMMENT_MARKER = "<!-- aicli-ci-review -->";
function fetchDiff(opts) {
  if (opts.diffOverride != null) return opts.diffOverride;
  if (opts.pr != null) {
    try {
      return execFileSync("gh", ["pr", "diff", String(opts.pr)], {
        encoding: "utf-8",
        maxBuffer: 50 * 1024 * 1024
      });
    } catch (err) {
      const msg = err.stderr?.toString() ?? err.message;
      throw new Error(`gh pr diff ${opts.pr} failed: ${msg.trim()}`);
    }
  }
  if (opts.base) {
    try {
      return execSync(`git diff ${shellQuote(opts.base)}...HEAD`, {
        encoding: "utf-8",
        maxBuffer: 50 * 1024 * 1024,
        timeout: 3e4
      });
    } catch (err) {
      throw new Error(`git diff ${opts.base}...HEAD failed: ${err.message}`);
    }
  }
  throw new Error("aicli ci: must supply --pr <num> or --base <ref> to determine the diff source");
}
function shellQuote(ref) {
  if (!/^[A-Za-z0-9._/\-]+$/.test(ref)) {
    throw new Error(`unsafe ref: ${ref}`);
  }
  return ref;
}
function buildGitContextStr(opts) {
  const parts = [];
  if (opts.pr != null) parts.push(`PR: #${opts.pr}`);
  if (opts.base) parts.push(`Base: ${opts.base}`);
  try {
    const branch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8", timeout: 5e3 }).trim();
    if (branch) parts.push(`Branch: ${branch}`);
  } catch {
  }
  parts.push(`Reviewer: aicli v${VERSION}`);
  return parts.join(" | ");
}
function countSeverity(md) {
  const count = (re) => (md.match(re) ?? []).length;
  return {
    critical: count(/🔴\s*(?:CRITICAL|Critical|critical)/g),
    high: count(/🟠\s*(?:HIGH|High|high)/g),
    warning: count(/🟡\s*(?:WARNING|Warning|warning|MEDIUM|Medium|medium)/g),
    info: count(/🔵\s*(?:INFO|Info|info|LOW|Low|low)/g)
  };
}
async function runOnePrompt(registry, providerId, modelId, prompt) {
  const provider = registry.get(providerId);
  const resp = await provider.chat({
    messages: [{ role: "user", content: prompt }],
    model: modelId,
    stream: false,
    temperature: 0.3,
    maxTokens: 8192
  });
  return resp.content?.trim() ?? "";
}
function postOrUpdatePrComment(prNumber, body, update) {
  const fullBody = `${body}

${CI_COMMENT_MARKER}`;
  if (update) {
    let comments = [];
    try {
      const out = execFileSync("gh", ["pr", "view", String(prNumber), "--json", "comments"], {
        encoding: "utf-8",
        maxBuffer: 20 * 1024 * 1024
      });
      const parsed = JSON.parse(out);
      comments = parsed.comments ?? [];
    } catch {
    }
    const existing = comments.find((c) => c.body.includes(CI_COMMENT_MARKER));
    if (existing) {
      execFileSync(
        "gh",
        ["api", "--method", "PATCH", `repos/{owner}/{repo}/issues/comments/${gqlIdToRest(existing.id)}`, "-f", `body=${fullBody}`],
        { stdio: "pipe" }
      );
      return { updated: true, id: existing.id };
    }
  }
  execFileSync("gh", ["pr", "comment", String(prNumber), "--body-file", "-"], {
    input: fullBody,
    stdio: ["pipe", "pipe", "pipe"]
  });
  return { updated: false };
}
function gqlIdToRest(graphqlId) {
  if (/^\d+$/.test(graphqlId)) return graphqlId;
  const query = `query($id:ID!){ node(id:$id){ ... on IssueComment { databaseId } } }`;
  const out = execFileSync("gh", ["api", "graphql", "-f", `query=${query}`, "-F", `id=${graphqlId}`], {
    encoding: "utf-8"
  });
  const parsed = JSON.parse(out);
  const id = parsed.data?.node?.databaseId;
  if (!id) throw new Error(`could not resolve comment id ${graphqlId}`);
  return String(id);
}
async function runCi(opts) {
  const maxDiff = opts.maxDiffChars ?? 3e4;
  let diff;
  try {
    diff = fetchDiff(opts).trim();
  } catch (err) {
    return {
      exitCode: 2,
      markdown: `\u274C ${err.message}`,
      posted: false,
      severity: { critical: 0, high: 0, warning: 0, info: 0 }
    };
  }
  if (!diff) {
    return {
      exitCode: 0,
      markdown: "\u2705 No changes to review.",
      posted: false,
      severity: { critical: 0, high: 0, warning: 0, info: 0 }
    };
  }
  const { diff: trimmedDiff, truncated } = truncateDiff(diff, maxDiff);
  const gitCtx = buildGitContextStr(opts);
  const config = new ConfigManager();
  await config.load();
  const registry = new ProviderRegistry(config);
  await registry.initialize();
  const providerId = opts.provider ?? config.getDefaultProvider();
  if (!registry.has(providerId)) {
    return {
      exitCode: 2,
      markdown: `\u274C Provider '${providerId}' is not configured. Set the right AICLI_API_KEY_* env var or pass --provider.`,
      posted: false,
      severity: { critical: 0, high: 0, warning: 0, info: 0 }
    };
  }
  const providerInfo = registry.get(providerId).info;
  const modelId = opts.model ?? config.get("defaultModels")[providerId] ?? providerInfo.defaultModel;
  const sections = [];
  sections.push(`## \u{1F916} aicli code review`);
  sections.push(`*Provider: \`${providerId}\` \xB7 Model: \`${modelId}\` \xB7 aicli v${VERSION}*`);
  sections.push("");
  try {
    if (!opts.skipCode) {
      const prompt = buildReviewPrompt(trimmedDiff, gitCtx, !!opts.detailed);
      const review = await runOnePrompt(registry, providerId, modelId, prompt);
      sections.push("### Code Review");
      sections.push(review);
      sections.push("");
    }
    if (!opts.skipSecurity) {
      const prompt = buildSecurityReviewPrompt(trimmedDiff, gitCtx);
      const review = await runOnePrompt(registry, providerId, modelId, prompt);
      sections.push("### Security Review");
      sections.push(review);
      sections.push("");
    }
  } catch (err) {
    return {
      exitCode: 2,
      markdown: `\u274C Review call failed: ${err.message}`,
      posted: false,
      severity: { critical: 0, high: 0, warning: 0, info: 0 }
    };
  }
  if (truncated) {
    sections.push(`> \u26A0 Diff was truncated to ${maxDiff} chars (original: ${diff.length}). Consider splitting large PRs.`);
  }
  const markdown = sections.join("\n");
  const severity = countSeverity(markdown);
  let posted = false;
  let updatedCommentId;
  if (opts.post && opts.pr != null && !opts.dryRun) {
    try {
      const result = postOrUpdatePrComment(opts.pr, markdown, opts.update !== false);
      posted = true;
      updatedCommentId = result.id;
    } catch (err) {
      return {
        exitCode: 2,
        markdown: `${markdown}

---

\u274C Failed to post comment: ${err.message}`,
        posted: false,
        severity
      };
    }
  }
  const hasBlocker = severity.critical > 0 || severity.high > 0;
  return {
    exitCode: hasBlocker ? 1 : 0,
    markdown,
    posted,
    updatedCommentId,
    severity
  };
}
export {
  CI_COMMENT_MARKER,
  countSeverity,
  runCi
};
