#!/usr/bin/env node
/**
 * Runs the test suite with Vitest's JSON reporter (plus the normal live output)
 * and prints ONE compact, copy-pasteable summary at the end — pass/fail counts
 * and every failing test with a one-line reason. Paste the block between the
 * ===== markers to hand off the full picture without a wall of logs.
 *
 * Exits with the suite's own status, so `make test` / the pre-push hook still
 * block on failure. This file is the same across all my projects; only RUNS
 * differs (a project with several vitest configs lists one entry per config).
 */
import { spawnSync } from "node:child_process";
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

// The npm script(s) that together make up "the whole suite". Run through npm (not
// `npx vitest` directly) so nested tooling in a globalSetup — e.g. `npx drizzle-kit
// migrate` — resolves with the right PATH. A project split across several vitest
// configs lists one entry per config.
const RUNS = [{ label: "all", args: ["test"] }];

const reporterArgs = (out) => ["--", "--reporter=default", "--reporter=json", `--outputFile.json=${out}`];

const projectName = (() => {
  try {
    return JSON.parse(readFileSync("package.json", "utf8")).name ?? "project";
  } catch {
    return "project";
  }
})();

const workDir = mkdtempSync(join(tmpdir(), "test-summary-"));
const started = Date.now();
let status = 0;
const reports = [];

for (const run of RUNS) {
  const out = join(workDir, `${run.label}.json`);
  const res = spawnSync("npm", ["run", ...run.args, ...reporterArgs(out)], {
    stdio: "inherit",
    shell: process.platform === "win32",
  });
  if (res.status !== 0) status = res.status ?? 1;
  try {
    reports.push(JSON.parse(readFileSync(out, "utf8")));
  } catch {
    /* a crash before the report was written — status already non-zero */
  }
}

let total = 0;
let passed = 0;
let failed = 0;
let skipped = 0;
let files = 0;
let filesFailed = 0;
const failures = [];
const cwd = process.cwd();

for (const r of reports) {
  total += r.numTotalTests ?? 0;
  passed += r.numPassedTests ?? 0;
  failed += r.numFailedTests ?? 0;
  skipped += (r.numPendingTests ?? 0) + (r.numTodoTests ?? 0);
  for (const tr of r.testResults ?? []) {
    files += 1;
    const fileFailed = tr.status === "failed" || (tr.assertionResults ?? []).some((a) => a.status === "failed");
    if (fileFailed) filesFailed += 1;
    for (const a of tr.assertionResults ?? []) {
      if (a.status !== "failed") continue;
      const file = (tr.name ?? "").replace(`${cwd}/`, "");
      const name = a.fullName || [...(a.ancestorTitles ?? []), a.title].filter(Boolean).join(" › ");
      const reason =
        (a.failureMessages ?? [])
          .join("\n")
          .split("\n")
          .map((l) => l.trim())
          .find((l) => l && !l.startsWith("at ")) ?? "";
      failures.push({ file, name, reason: reason.slice(0, 200) });
    }
  }
}

const elapsed = ((Date.now() - started) / 1000).toFixed(1);
const ok = status === 0 && failed === 0 && reports.length > 0;
const L = "=".repeat(38);

const lines = [];
lines.push(L);
lines.push(`TEST SUMMARY — ${projectName}  (${new Date().toISOString().slice(0, 16).replace("T", " ")})`);
if (reports.length === 0) {
  lines.push("❌ CRASH — the test run failed before producing a report (see output above).");
} else {
  lines.push(
    `${ok ? "✅ PASS" : "❌ FAIL"} — ${passed}/${total} tests passed` +
      (failed ? `, ${failed} failed` : "") +
      (skipped ? `, ${skipped} skipped` : "") +
      `  ·  ${files} files (${filesFailed} failed)  ·  ${elapsed}s`,
  );
  if (failures.length) {
    lines.push("");
    lines.push(`FAILED (${failures.length}):`);
    for (const f of failures) {
      lines.push(`  ✗ ${f.file} › ${f.name}`);
      if (f.reason) lines.push(`      ${f.reason}`);
    }
  }
}
lines.push(L);

console.log(`\n${lines.join("\n")}`);
rmSync(workDir, { recursive: true, force: true });
process.exit(status);
