#!/usr/bin/env node

import path from "node:path";
import { readFile, writeFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";

function escapeHtml(value) {
  return String(value ?? "")
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;");
}

export function generateHtml(data, { autoRefresh = false, skillName = "" } = {}) {
  const history = Array.isArray(data?.history) ? data.history : [];
  const titlePrefix = skillName ? `${escapeHtml(skillName)} — ` : "";
  const refreshTag = autoRefresh ? '    <meta http-equiv="refresh" content="5">\n' : "";

  const trainQueries = [];
  const testQueries = [];
  if (history.length > 0) {
    for (const result of history[0].train_results ?? history[0].results ?? []) {
      trainQueries.push({ query: result.query, should_trigger: result.should_trigger ?? true });
    }
    for (const result of history[0].test_results ?? []) {
      testQueries.push({ query: result.query, should_trigger: result.should_trigger ?? true });
    }
  }

  let html = `<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
${refreshTag}    <title>${titlePrefix}Skill Description Optimization</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;600&family=Lora:wght@400;500&display=swap" rel="stylesheet">
    <style>
        body { font-family: 'Lora', Georgia, serif; max-width: 100%; margin: 0 auto; padding: 20px; background: #faf9f5; color: #141413; }
        h1 { font-family: 'Poppins', sans-serif; color: #141413; }
        .explainer, .summary { background: white; padding: 15px; border-radius: 6px; margin-bottom: 20px; border: 1px solid #e8e6dc; }
        .explainer { color: #b0aea5; font-size: 0.875rem; line-height: 1.6; }
        .summary p { margin: 5px 0; }
        .best { color: #788c5d; font-weight: bold; }
        .table-container { overflow-x: auto; width: 100%; }
        table { border-collapse: collapse; background: white; border: 1px solid #e8e6dc; border-radius: 6px; font-size: 12px; min-width: 100%; }
        th, td { padding: 8px; text-align: left; border: 1px solid #e8e6dc; white-space: normal; word-wrap: break-word; }
        th { font-family: 'Poppins', sans-serif; background: #141413; color: #faf9f5; font-weight: 500; }
        th.test-col { background: #6a9bcc; }
        th.query-col { min-width: 200px; }
        td.description { font-family: monospace; font-size: 11px; word-wrap: break-word; max-width: 400px; }
        td.result { text-align: center; font-size: 16px; min-width: 40px; }
        td.test-result { background: #f0f6fc; }
        .pass { color: #788c5d; }
        .fail { color: #c44; }
        .rate { font-size: 9px; color: #b0aea5; display: block; }
        tr:hover { background: #faf9f5; }
        .score { display: inline-block; padding: 2px 6px; border-radius: 4px; font-weight: bold; font-size: 11px; }
        .score-good { background: #eef2e8; color: #788c5d; }
        .score-ok { background: #fef3c7; color: #d97706; }
        .score-bad { background: #fceaea; color: #c44; }
        .best-row { background: #f5f8f2; }
        th.positive-col { border-bottom: 3px solid #788c5d; }
        th.negative-col { border-bottom: 3px solid #c44; }
        .legend { font-family: 'Poppins', sans-serif; display: flex; gap: 20px; margin-bottom: 10px; font-size: 13px; align-items: center; }
        .legend-item { display: flex; align-items: center; gap: 6px; }
        .legend-swatch { width: 16px; height: 16px; border-radius: 3px; display: inline-block; }
        .swatch-positive { background: #141413; border-bottom: 3px solid #788c5d; }
        .swatch-negative { background: #141413; border-bottom: 3px solid #c44; }
        .swatch-test { background: #6a9bcc; }
        .swatch-train { background: #141413; }
    </style>
</head>
<body>
    <h1>${titlePrefix}Skill Description Optimization</h1>
    <div class="explainer">
        <strong>Optimizing your skill's description.</strong> This page updates automatically as Claude tests different versions of your skill's description. Each row is an iteration — a new description attempt. The columns show test queries: green checkmarks mean the skill triggered correctly (or correctly didn't trigger), red crosses mean it got it wrong.
    </div>
    <div class="summary">
        <p><strong>Original:</strong> ${escapeHtml(data?.original_description ?? "N/A")}</p>
        <p class="best"><strong>Best:</strong> ${escapeHtml(data?.best_description ?? "N/A")}</p>
        <p><strong>Best Score:</strong> ${escapeHtml(data?.best_score ?? "N/A")}</p>
        <p><strong>Iterations:</strong> ${escapeHtml(data?.iterations_run ?? 0)} | <strong>Train:</strong> ${escapeHtml(data?.train_size ?? "?")} | <strong>Test:</strong> ${escapeHtml(data?.test_size ?? "?")}</p>
    </div>
    <div class="legend">
        <span style="font-weight:600">Query columns:</span>
        <span class="legend-item"><span class="legend-swatch swatch-positive"></span> Should trigger</span>
        <span class="legend-item"><span class="legend-swatch swatch-negative"></span> Should NOT trigger</span>
        <span class="legend-item"><span class="legend-swatch swatch-train"></span> Train</span>
        <span class="legend-item"><span class="legend-swatch swatch-test"></span> Test</span>
    </div>
    <div class="table-container">
    <table>
        <thead>
            <tr>
                <th>Iter</th>
                <th>Train</th>
                <th>Test</th>
                <th class="query-col">Description</th>
`;

  for (const info of trainQueries) {
    html += `                <th class="${info.should_trigger ? "positive-col" : "negative-col"}">${escapeHtml(info.query)}</th>\n`;
  }
  for (const info of testQueries) {
    html += `                <th class="test-col ${info.should_trigger ? "positive-col" : "negative-col"}">${escapeHtml(info.query)}</th>\n`;
  }
  html += `            </tr>
        </thead>
        <tbody>
`;

  const bestIteration = history.length === 0
    ? null
    : testQueries.length > 0
      ? history.reduce((best, item) => (item.test_passed ?? -1) > (best?.test_passed ?? -1) ? item : best, null)?.iteration
      : history.reduce((best, item) => (item.train_passed ?? item.passed ?? -1) > (best?.train_passed ?? best?.passed ?? -1) ? item : best, null)?.iteration;

  for (const item of history) {
    const trainResults = item.train_results ?? item.results ?? [];
    const testResults = item.test_results ?? [];
    const trainByQuery = new Map(trainResults.map((entry) => [entry.query, entry]));
    const testByQuery = new Map(testResults.map((entry) => [entry.query, entry]));

    const aggregateRuns = (results) => {
      let correct = 0;
      let total = 0;
      for (const result of results) {
        const runs = result.runs ?? 0;
        const triggers = result.triggers ?? 0;
        total += runs;
        correct += result.should_trigger ? triggers : runs - triggers;
      }
      return { correct, total };
    };

    const trainAgg = aggregateRuns(trainResults);
    const testAgg = aggregateRuns(testResults);
    const scoreClass = ({ correct, total }) => {
      const ratio = total > 0 ? correct / total : 0;
      if (ratio >= 0.8) return "score-good";
      if (ratio >= 0.5) return "score-ok";
      return "score-bad";
    };

    html += `            <tr class="${item.iteration === bestIteration ? "best-row" : ""}">
                <td>${escapeHtml(item.iteration)}</td>
                <td><span class="score ${scoreClass(trainAgg)}">${trainAgg.correct}/${trainAgg.total}</span></td>
                <td><span class="score ${scoreClass(testAgg)}">${testAgg.correct}/${testAgg.total}</span></td>
                <td class="description">${escapeHtml(item.description ?? "")}</td>
`;
    for (const info of trainQueries) {
      const result = trainByQuery.get(info.query) ?? {};
      html += `                <td class="result ${result.pass ? "pass" : "fail"}">${result.pass ? "✓" : "✗"}<span class="rate">${result.triggers ?? 0}/${result.runs ?? 0}</span></td>\n`;
    }
    for (const info of testQueries) {
      const result = testByQuery.get(info.query) ?? {};
      html += `                <td class="result test-result ${result.pass ? "pass" : "fail"}">${result.pass ? "✓" : "✗"}<span class="rate">${result.triggers ?? 0}/${result.runs ?? 0}</span></td>\n`;
    }
    html += "            </tr>\n";
  }

  html += `        </tbody>
    </table>
    </div>
</body>
</html>
`;
  return html;
}

async function main(argv = process.argv) {
  const args = parseArgs(argv.slice(2));
  const inputPath = args._[0];
  if (!inputPath) {
    throw new Error("Usage: node generate_report.mjs <input.json> [-o output.html] [--skill-name name]");
  }
  const raw = inputPath === "-" ? await readStdin() : await readFile(inputPath, "utf8");
  const data = JSON.parse(raw);
  const output = generateHtml(data, { skillName: args["skill-name"] ?? "", autoRefresh: false });
  if (args.output || args.o) {
    await writeFile(args.output || args.o, output);
    process.stderr.write(`Report written to ${args.output || args.o}\n`);
    return;
  }
  process.stdout.write(output);
}

function parseArgs(args) {
  const out = { _: [] };
  for (let i = 0; i < args.length; i += 1) {
    const item = args[i];
    if (!item.startsWith("-")) {
      out._.push(item);
      continue;
    }
    const key = item.startsWith("--") ? item.slice(2) : item.slice(1);
    const next = args[i + 1];
    if (!next || next.startsWith("-")) {
      out[key] = true;
      continue;
    }
    out[key] = next;
    i += 1;
  }
  return out;
}

async function readStdin() {
  let data = "";
  for await (const chunk of process.stdin) {
    data += String(chunk);
  }
  return data;
}

const entryPath = fileURLToPath(import.meta.url);
if (process.argv[1] && path.resolve(process.argv[1]) === entryPath) {
  main().catch((error) => {
    process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
    process.exitCode = 1;
  });
}
