const fs = require("node:fs");
const path = require("node:path");

const EVAL_FIELDS = new Set(["name", "slot", "level", "mainStat", "mainValue", "setName", "equipped", "substats"]);

function argValue(name, fallback = "") {
  const prefix = `--${name}=`;
  const match = process.argv.find((entry) => entry.startsWith(prefix));
  return match ? match.slice(prefix.length) : fallback;
}

function parseExpectedFields() {
  const inlineJson = argValue("expect-json");
  const expectFile = argValue("expect-file");
  if (!inlineJson && !expectFile) {
    throw new Error("Missing expected labels. Pass --expect-json=... or --expect-file=...");
  }
  const raw = (inlineJson || fs.readFileSync(path.resolve(expectFile), "utf8")).replace(/^\uFEFF/, "");
  const parsed = JSON.parse(raw);
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
    throw new Error("Expected labels must be a JSON object.");
  }
  const expect = {};
  for (const [field, value] of Object.entries(parsed)) {
    if (!EVAL_FIELDS.has(field)) throw new Error(`Unknown expected field: ${field}`);
    if (field === "level") {
      if (!Number.isInteger(value) || value < 0) throw new Error("Expected level must be a non-negative integer.");
      expect[field] = value;
      continue;
    }
    if (field === "substats") {
      if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string" && entry.trim())) {
        throw new Error("Expected substats must be a non-empty string array.");
      }
      expect[field] = value;
      continue;
    }
    if (typeof value !== "string" || !value.trim()) throw new Error(`Expected ${field} must be a non-empty string.`);
    expect[field] = value;
  }
  if (Object.keys(expect).length === 0) throw new Error("Expected labels must include at least one field.");
  return expect;
}

function loadCandidate(inputPath, candidateId) {
  const payload = JSON.parse(fs.readFileSync(path.resolve(inputPath), "utf8"));
  const candidates = Array.isArray(payload?.candidates) ? payload.candidates : [];
  const candidate = candidates.find((entry) => entry.id === candidateId);
  if (!candidate) throw new Error(`Candidate not found: ${candidateId}`);
  if (!candidate.ocr || typeof candidate.ocr !== "object" || Object.keys(candidate.ocr).length === 0) {
    throw new Error(`Candidate has no OCR payload: ${candidateId}`);
  }
  return candidate;
}

function stableId(value) {
  return String(value || "")
    .replace(/[^0-9A-Za-z]+/g, "-")
    .replace(/^-+|-+$/g, "")
    .slice(0, 80) || "review-case";
}

function objectLiteral(value, indent = 2) {
  return JSON.stringify(value, null, indent).replace(/"([A-Za-z_$][0-9A-Za-z_$]*)":/g, "$1:");
}

function snippetFor(candidate, expect) {
  const id = stableId(argValue("id", `confirmed-${candidate.id}`));
  const entry = {
    id,
    confirmed: true,
    ocr: candidate.ocr,
    expect,
    meta: {
      source: "review-sample",
      resolution: candidate.resolution || undefined,
      note: `${candidate.reason || "review-sample"} | savedAt=${candidate.savedAt || "unknown"} | sourceCandidate=${candidate.id}`,
    },
  };
  return `${objectLiteral(entry, 2)},\n`;
}

function defaultInputPath() {
  return path.join(process.cwd(), "outputs", "review-eval-candidates", "review-eval-candidates.json");
}

function main() {
  const inputPath = argValue("input", defaultInputPath());
  const candidateId = argValue("candidate");
  if (!candidateId) throw new Error("Missing candidate id. Pass --candidate=<id>.");
  const outputPath = path.resolve(argValue("out", path.join(process.cwd(), "outputs", "review-eval-candidates", `${stableId(candidateId)}.confirmed.ts`)));
  const candidate = loadCandidate(inputPath, candidateId);
  const expect = parseExpectedFields();
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
  const snippet = snippetFor(candidate, expect);
  fs.writeFileSync(outputPath, snippet, "utf8");
  console.log(JSON.stringify({ ok: true, candidateId, outputPath, labeledFields: Object.keys(expect) }, null, 2));
}

if (require.main === module) {
  main();
}

module.exports = {
  loadCandidate,
  parseExpectedFields,
  snippetFor,
  stableId,
};
