import { createHash } from 'node:crypto';
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';

// src/features/watch-pr-review/domain/review-marker.ts
var REVIEW_MARKER_PREFIX = "ju-hong-e:watch-pr-review";
function buildReviewSignature(commitOids) {
  const normalized = [...commitOids].sort().join(",");
  return createHash("sha1").update(normalized).digest("hex").slice(0, 12);
}
function hasReviewMarkerForSignature(commentBodies, signature) {
  const needle = `${REVIEW_MARKER_PREFIX} sig=${signature}`;
  return commentBodies.some((body) => body.includes(needle));
}
var GH_EXEC_OPTIONS = {
  encoding: "utf8",
  maxBuffer: 1024 * 1024 * 50,
  stdio: ["ignore", "pipe", "pipe"]
};
function getListOpenPullRequests(repo) {
  const stdout = execFileSync(
    "gh",
    [
      "pr",
      "list",
      "--repo",
      repo,
      "--state",
      "open",
      "--json",
      "number,title,url,author,baseRefName,headRefName"
    ],
    GH_EXEC_OPTIONS
  );
  const parsed = JSON.parse(stdout);
  return parsed.map((item) => ({
    author: item.author?.login ?? "(unknown)",
    baseRefName: item.baseRefName,
    headRefName: item.headRefName,
    number: item.number,
    title: item.title,
    url: item.url
  }));
}
function getPullRequestCommitOids(repo, prNumber) {
  const stdout = execFileSync(
    "gh",
    ["pr", "view", String(prNumber), "--repo", repo, "--json", "commits"],
    GH_EXEC_OPTIONS
  );
  const parsed = JSON.parse(stdout);
  return parsed.commits.map((commit) => commit.oid);
}
function getPullRequestComments(repo, prNumber) {
  const stdout = execFileSync(
    "gh",
    ["pr", "view", String(prNumber), "--repo", repo, "--json", "comments"],
    GH_EXEC_OPTIONS
  );
  const parsed = JSON.parse(stdout);
  return parsed.comments.map((comment) => comment.body);
}
var DEFAULT_POLL_INTERVAL_MS = 1e4;
var MIN_POLL_INTERVAL_MS = 3e3;
var DEFAULT_CONCURRENCY = 2;
var MIN_CONCURRENCY = 1;
var MAX_CONCURRENCY = 8;
var DEFAULT_CONFIG = {
  concurrency: DEFAULT_CONCURRENCY,
  pollIntervalMs: DEFAULT_POLL_INTERVAL_MS,
  repos: [{ repo: "owner/repository", enabled: false }]
};
function loadWatchConfig(watchPaths) {
  try {
    const raw = fs.readFileSync(watchPaths.configPath, "utf8");
    const parsed = JSON.parse(raw);
    return {
      concurrency: normalizeConcurrency(parsed.concurrency),
      pollIntervalMs: normalizePollInterval(parsed.pollIntervalMs),
      repos: Array.isArray(parsed.repos) ? parsed.repos : []
    };
  } catch {
    return { ...DEFAULT_CONFIG, repos: [] };
  }
}
function loadWatchHistory(watchPaths) {
  try {
    const raw = fs.readFileSync(watchPaths.historyPath, "utf8");
    const parsed = JSON.parse(raw);
    return { reviewed: Array.isArray(parsed.reviewed) ? parsed.reviewed : [] };
  } catch {
    return { reviewed: [] };
  }
}
function hasReviewedSameCommits(history, url, commitOids) {
  const previous = history.reviewed.find((entry) => entry.url === url);
  if (!previous) {
    return false;
  }
  return isSameCommitSet(previous.commitOids, commitOids);
}
function normalizePollInterval(value) {
  if (typeof value !== "number" || Number.isNaN(value)) {
    return DEFAULT_POLL_INTERVAL_MS;
  }
  return Math.max(MIN_POLL_INTERVAL_MS, value);
}
function normalizeConcurrency(value) {
  if (typeof value !== "number" || Number.isNaN(value)) {
    return DEFAULT_CONCURRENCY;
  }
  return Math.min(MAX_CONCURRENCY, Math.max(MIN_CONCURRENCY, Math.floor(value)));
}
function isSameCommitSet(a, b) {
  if (a.length !== b.length) {
    return false;
  }
  const setA = new Set(a);
  return b.every((oid) => setA.has(oid));
}

// src/features/watch-pr-review/application/producer.ts
var PLACEHOLDER_REPO = "owner/repository";
async function enqueueReviewTargets(context, queue) {
  const config = loadWatchConfig(context.watchPaths);
  const activeRepos = config.repos.filter((repo) => repo.enabled !== false && repo.repo && repo.repo !== PLACEHOLDER_REPO);
  if (activeRepos.length === 0) {
    context.trace("watch-pr-review:producer:no-repos", context.watchPaths.configPath);
    return 0;
  }
  const history = loadWatchHistory(context.watchPaths);
  let enqueuedCount = 0;
  for (const repo of activeRepos) {
    if (context.isStopped()) {
      break;
    }
    let pullRequests;
    try {
      pullRequests = getListOpenPullRequests(repo.repo);
    } catch (error) {
      console.log(`\u26A0\uFE0F  PR \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328 (${repo.repo}): ${getErrorMessage(error)}`);
      context.trace("watch-pr-review:producer:list-error", `${repo.repo} ${getErrorMessage(error)}`);
      continue;
    }
    for (const pullRequest of pullRequests) {
      if (context.isStopped()) {
        break;
      }
      if (await enqueuePullRequest(context, queue, history, repo.repo, pullRequest)) {
        enqueuedCount += 1;
      }
    }
  }
  return enqueuedCount;
}
async function enqueuePullRequest(context, queue, history, repo, pullRequest) {
  if (queue.isTracked(pullRequest.url)) {
    return false;
  }
  let commitOids;
  try {
    commitOids = getPullRequestCommitOids(repo, pullRequest.number);
  } catch (error) {
    console.log(`\u26A0\uFE0F  PR \uCEE4\uBC0B \uC870\uD68C \uC2E4\uD328 (${pullRequest.url}): ${getErrorMessage(error)}`);
    context.trace("watch-pr-review:producer:commit-error", `${pullRequest.url} ${getErrorMessage(error)}`);
    return false;
  }
  if (hasReviewedSameCommits(history, pullRequest.url, commitOids)) {
    return false;
  }
  if (await isAlreadyReviewedRemotely(context, repo, pullRequest, commitOids)) {
    return false;
  }
  const added = queue.enqueue({ commitOids, pullRequest, repo });
  if (added) {
    console.log(`\u2795 \uB9AC\uBDF0 \uB300\uAE30\uC5F4 \uCD94\uAC00: PR #${pullRequest.number} ${pullRequest.title}`);
    context.trace("watch-pr-review:producer:enqueue", pullRequest.url);
  }
  return added;
}
async function isAlreadyReviewedRemotely(context, repo, pullRequest, commitOids) {
  const signature = buildReviewSignature(commitOids);
  let commentBodies;
  try {
    commentBodies = getPullRequestComments(repo, pullRequest.number);
  } catch (error) {
    context.trace("watch-pr-review:producer:comments-error", `${pullRequest.url} ${getErrorMessage(error)}`);
    return false;
  }
  if (!hasReviewMarkerForSignature(commentBodies, signature)) {
    return false;
  }
  await context.recordReviewed({ commitOids, reviewedAt: (/* @__PURE__ */ new Date()).toISOString(), url: pullRequest.url });
  console.log(`\u21A9\uFE0F  \uC774\uBBF8 \uB9AC\uBDF0\uB41C PR(\uC6D0\uACA9 \uB313\uAE00 \uC874\uC7AC) \u2014 \uAC74\uB108\uB700: PR #${pullRequest.number} ${pullRequest.title}`);
  context.trace("watch-pr-review:producer:remote-dedup", pullRequest.url);
  return true;
}
function getErrorMessage(error) {
  return error instanceof Error ? error.message : String(error);
}

export { enqueueReviewTargets };
