#!/usr/bin/env node
/**
 * Token codemod: migrates v1 dark-theme tokens → v2 paper-and-ink tokens.
 *
 * Usage:
 *   node scripts/migrate-tokens.mjs            # dry-run (default)
 *   node scripts/migrate-tokens.mjs --apply    # write changes
 *   node scripts/migrate-tokens.mjs --apply --verbose
 *
 * Scope: src/**.{tsx,ts,css}. Never touches node_modules, dist, scripts.
 *
 * Mapping summary (the philosophical core):
 *   surface-canvas    → paper
 *   surface-raised    → paper-2
 *   surface-hover     → paper-2  (v2 raises ON hover, doesn't drop)
 *   surface-active    → paper-3
 *   surface-elevated  → paper-2
 *   fg-default        → ink
 *   fg-muted          → ink-2
 *   fg-subtle         → ink-3
 *   border-subtle     → hairline
 *   border-default    → hairline-2
 *   border-strong     → hairline-2
 *   accent            → accent              (kept; value changes from blue → mint)
 *   accent-hover      → mint-600
 *   accent-soft       → mint-100
 *   success           → ok
 *   warn / danger     → warn / danger       (kept; values shift to desaturated)
 *   font-display       (Clash Display)  →  font-sans   (Geist for headings in v2)
 *   font-sans          (Inter)          →  font-sans   (Geist; same class)
 *   font-mono          (Commit Mono)    →  font-mono   (Geist Mono; same class)
 *
 * Type-scale class renames (sizes shift slightly; visually compatible):
 *   text-hero        → text-h1
 *   text-display     → text-h3
 *   text-heading     → text-h4
 *   text-subheading  → text-body-lg
 *   text-body        → text-body-lg     (was 15px, now 16px reading body)
 *   text-small       → text-small       (same)
 *   text-label       → text-eyebrow     (uppercase eyebrow)
 *   text-caption     → text-mono        (mono mini-text)
 *
 * Components consuming these classes must be reviewed after the codemod —
 * the visual output will look different even where the rename is "safe."
 */

import { readdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
import { join, relative } from "node:path";
import { fileURLToPath } from "node:url";

const ROOT = join(fileURLToPath(import.meta.url), "..", "..");
const SRC = join(ROOT, "src");

const APPLY = process.argv.includes("--apply");
const VERBOSE = process.argv.includes("--verbose");

const RULES = [
  // Tailwind utility renames — colors. Order matters: longer patterns first.
  { from: /\bbg-surface-canvas\b/g,         to: "bg-paper",           label: "bg-surface-canvas → bg-paper" },
  { from: /\bbg-surface-raised\b/g,         to: "bg-paper-2",         label: "bg-surface-raised → bg-paper-2" },
  { from: /\bbg-surface-hover\b/g,          to: "bg-paper-2",         label: "bg-surface-hover → bg-paper-2" },
  { from: /\bbg-surface-active\b/g,         to: "bg-paper-3",         label: "bg-surface-active → bg-paper-3" },
  { from: /\bbg-surface-elevated\b/g,       to: "bg-paper-2",         label: "bg-surface-elevated → bg-paper-2" },
  { from: /\bhover:bg-surface-hover\b/g,    to: "hover:bg-paper-2",   label: "hover:bg-surface-hover → hover:bg-paper-2" },
  { from: /\bhover:bg-surface-active\b/g,   to: "hover:bg-paper-3",   label: "hover:bg-surface-active → hover:bg-paper-3" },
  { from: /\bhover:bg-surface-raised\b/g,   to: "hover:bg-paper-3",   label: "hover:bg-surface-raised → hover:bg-paper-3" },
  { from: /\bbg-scrim\b/g,                  to: "bg-paper-4",         label: "bg-scrim → bg-paper-4" },

  // Foreground
  { from: /\btext-fg-default\b/g,           to: "text-ink",           label: "text-fg-default → text-ink" },
  { from: /\btext-fg-muted\b/g,             to: "text-ink-2",         label: "text-fg-muted → text-ink-2" },
  { from: /\btext-fg-subtle\b/g,            to: "text-ink-3",         label: "text-fg-subtle → text-ink-3" },
  { from: /\btext-fg-placeholder\b/g,       to: "text-ink-3",         label: "text-fg-placeholder → text-ink-3" },
  { from: /\btext-fg-disabled\b/g,          to: "text-ink-3",         label: "text-fg-disabled → text-ink-3" },
  { from: /\btext-fg-on-accent\b/g,         to: "text-paper",         label: "text-fg-on-accent → text-paper" },
  { from: /\btext-fg-on-success\b/g,        to: "text-paper",         label: "text-fg-on-success → text-paper" },
  { from: /\btext-fg-on-warn\b/g,           to: "text-ink",           label: "text-fg-on-warn → text-ink" },
  { from: /\btext-fg-on-danger\b/g,         to: "text-paper",         label: "text-fg-on-danger → text-paper" },
  { from: /\bhover:text-fg-default\b/g,     to: "hover:text-ink",     label: "hover:text-fg-default → hover:text-ink" },
  { from: /\bhover:text-fg-muted\b/g,       to: "hover:text-ink-2",   label: "hover:text-fg-muted → hover:text-ink-2" },
  { from: /\bplaceholder:text-fg-subtle\b/g,to: "placeholder:text-ink-3", label: "placeholder:text-fg-subtle → ink-3" },
  { from: /\bplaceholder:text-fg-placeholder\b/g, to: "placeholder:text-ink-3", label: "placeholder:text-fg-placeholder → ink-3" },
  { from: /\bdisabled:bg-surface-raised\b/g,to: "disabled:bg-paper-2",label: "disabled:bg-surface-raised → disabled:bg-paper-2" },
  { from: /\bdisabled:text-fg-disabled\b/g, to: "disabled:text-ink-3",label: "disabled:text-fg-disabled → disabled:text-ink-3" },
  { from: /\bdisabled:border-border-subtle\b/g, to: "disabled:border-hairline", label: "disabled:border-border-subtle → hairline" },

  // Borders
  { from: /\bborder-border-subtle\b/g,      to: "border-hairline",    label: "border-border-subtle → border-hairline" },
  { from: /\bborder-border-default\b/g,     to: "border-hairline-2",  label: "border-border-default → border-hairline-2" },
  { from: /\bborder-border-strong\b/g,      to: "border-hairline-2",  label: "border-border-strong → border-hairline-2" },

  // Accent + state
  { from: /\bbg-accent\b/g,                 to: "bg-accent",          label: "(noop) bg-accent" },
  { from: /\bbg-accent-hover\b/g,           to: "bg-mint-600",        label: "bg-accent-hover → bg-mint-600" },
  { from: /\bhover:bg-accent-hover\b/g,     to: "hover:bg-mint-600",  label: "hover:bg-accent-hover → hover:bg-mint-600" },
  { from: /\bbg-accent-soft\b/g,            to: "bg-mint-100",        label: "bg-accent-soft → bg-mint-100" },
  { from: /\btext-accent\b/g,               to: "text-accent",        label: "(noop) text-accent" },
  { from: /\bborder-accent\b/g,             to: "border-accent",      label: "(noop) border-accent" },

  // Status
  { from: /\bbg-success\b/g,                to: "bg-mint-500",        label: "bg-success → bg-mint-500" },
  { from: /\bbg-success-hover\b/g,          to: "bg-mint-600",        label: "bg-success-hover → bg-mint-600" },
  { from: /\bhover:bg-success-hover\b/g,    to: "hover:bg-mint-600",  label: "hover:bg-success-hover → hover:bg-mint-600" },
  { from: /\bbg-success-soft\b/g,           to: "bg-mint-100",        label: "bg-success-soft → bg-mint-100" },
  { from: /\btext-success\b/g,              to: "text-ok",            label: "text-success → text-ok" },
  { from: /\bbg-warn\b/g,                   to: "bg-warn",            label: "(noop) bg-warn" },
  { from: /\bbg-warn-soft\b/g,              to: "bg-paper-2",         label: "bg-warn-soft → bg-paper-2" },
  { from: /\btext-warn\b/g,                 to: "text-warn",          label: "(noop) text-warn" },
  { from: /\bbg-danger\b/g,                 to: "bg-danger",          label: "(noop) bg-danger" },
  { from: /\bbg-danger-soft\b/g,            to: "bg-paper-2",         label: "bg-danger-soft → bg-paper-2" },
  { from: /\btext-danger\b/g,               to: "text-danger",        label: "(noop) text-danger" },

  // Tones (chat/files) — drop entirely; map to ink-2 for now
  { from: /\bbg-tint-chat\b/g,              to: "bg-paper-2",         label: "bg-tint-chat → bg-paper-2" },
  { from: /\btext-chat\b/g,                 to: "text-ink-2",         label: "text-chat → text-ink-2" },
  { from: /\btext-files\b/g,                to: "text-ink-2",         label: "text-files → text-ink-2" },

  // Type-scale class renames
  { from: /\btext-hero\b/g,                 to: "text-h1",            label: "text-hero → text-h1" },
  { from: /\btext-display\b/g,              to: "text-h3",            label: "text-display → text-h3" },
  { from: /\btext-heading\b/g,              to: "text-h4",            label: "text-heading → text-h4" },
  { from: /\btext-subheading\b/g,           to: "text-body-lg",       label: "text-subheading → text-body-lg" },
  { from: /\btext-label\b/g,                to: "text-eyebrow",       label: "text-label → text-eyebrow" },
  { from: /\btext-caption\b/g,              to: "text-mono",          label: "text-caption → text-mono" },
  // text-body and text-small keep their names; sizes shift slightly.

  // Font family
  { from: /\bfont-display\b/g,              to: "font-sans",          label: "font-display (Clash) → font-sans (Geist)" },

  // Shadow tokens — old names → new
  { from: /\bshadow-raised\b/g,             to: "shadow-1",           label: "shadow-raised → shadow-1" },
  { from: /\bshadow-overlay\b/g,            to: "shadow-2",           label: "shadow-overlay → shadow-2" },
  { from: /\bshadow-modal\b/g,              to: "shadow-3",           label: "shadow-modal → shadow-3" },

  // Radius — old names → new
  { from: /\brounded-md\b/g,                to: "rounded-md",         label: "(noop) rounded-md (now 10px)" },
  { from: /\brounded-lg\b/g,                to: "rounded-lg",         label: "(noop) rounded-lg (now 16px)" },

  // Component-size utilities
  { from: /\bh-control-sm\b/g,              to: "h-row-dense",        label: "h-control-sm → h-row-dense" },
  { from: /\bh-control\b/g,                 to: "h-row",              label: "h-control → h-row" },
  { from: /\bh-pill\b/g,                    to: "h-row-dense",        label: "h-pill → h-row-dense" },

  // Container widths
  { from: /\bmax-w-narrow\b/g,              to: "max-w-chat",         label: "max-w-narrow → max-w-chat" },
  { from: /\bmax-w-default\b/g,             to: "max-w-chat",         label: "max-w-default → max-w-chat" },
  { from: /\bmax-w-wide\b/g,                to: "max-w-content",      label: "max-w-wide → max-w-content" },

  // Z-index utility renames
  { from: /\bz-modal-scrim\b/g,             to: "z-overlay",          label: "z-modal-scrim → z-overlay" },

  // CSS variables (in .css and inline style)
  { from: /var\(--color-surface-canvas\)/g,   to: "var(--color-paper)",       label: "var(--color-surface-canvas) → paper" },
  { from: /var\(--color-surface-raised\)/g,   to: "var(--color-paper-2)",     label: "var(--color-surface-raised) → paper-2" },
  { from: /var\(--color-surface-hover\)/g,    to: "var(--color-paper-2)",     label: "var(--color-surface-hover) → paper-2" },
  { from: /var\(--color-surface-active\)/g,   to: "var(--color-paper-3)",     label: "var(--color-surface-active) → paper-3" },
  { from: /var\(--color-surface-elevated\)/g, to: "var(--color-paper-2)",     label: "var(--color-surface-elevated) → paper-2" },
  { from: /var\(--color-fg-default\)/g,       to: "var(--color-ink)",         label: "var(--color-fg-default) → ink" },
  { from: /var\(--color-fg-muted\)/g,         to: "var(--color-ink-2)",       label: "var(--color-fg-muted) → ink-2" },
  { from: /var\(--color-fg-subtle\)/g,        to: "var(--color-ink-3)",       label: "var(--color-fg-subtle) → ink-3" },
  { from: /var\(--color-fg-placeholder\)/g,   to: "var(--color-ink-3)",       label: "var(--color-fg-placeholder) → ink-3" },
  { from: /var\(--color-fg-disabled\)/g,      to: "var(--color-ink-3)",       label: "var(--color-fg-disabled) → ink-3" },
  { from: /var\(--color-border-subtle\)/g,    to: "var(--color-hairline)",    label: "var(--color-border-subtle) → hairline" },
  { from: /var\(--color-border-default\)/g,   to: "var(--color-hairline-2)",  label: "var(--color-border-default) → hairline-2" },
  { from: /var\(--color-border-strong\)/g,    to: "var(--color-hairline-2)",  label: "var(--color-border-strong) → hairline-2" },
  { from: /var\(--color-accent-soft\)/g,      to: "var(--color-mint-100)",    label: "var(--color-accent-soft) → mint-100" },
  { from: /var\(--color-success-soft\)/g,     to: "var(--color-mint-100)",    label: "var(--color-success-soft) → mint-100" },
  { from: /var\(--color-warn-soft\)/g,        to: "var(--color-paper-2)",     label: "var(--color-warn-soft) → paper-2" },
  { from: /var\(--color-danger-soft\)/g,      to: "var(--color-paper-2)",     label: "var(--color-danger-soft) → paper-2" },
];

const FLAG_RULES = [
  { pattern: /disabled:opacity-\d+/g, label: "disabled:opacity-* — manual fix",
    fix: "Replace with disabled:bg-paper-2 disabled:text-ink-3. See ANTI-PATTERNS.md." },
  { pattern: /\btransition-all\b/g, label: "transition-all — manual fix",
    fix: "Use .transition-default or specify properties explicitly." },
  { pattern: /#[0-9a-fA-F]{3,8}\b/g, label: "raw hex color — manual fix",
    fix: "Use a semantic token. See docs/tokens.md." },
  { pattern: /\bbg-bg\b|\btext-bg\b|\bborder-bg\b/g, label: "legacy --color-bg alias — manual fix",
    fix: "Use bg-paper / text-ink / border-hairline." },
];

function* walk(dir) {
  for (const entry of readdirSync(dir)) {
    if (entry.startsWith(".") || entry === "node_modules" || entry === "dist") continue;
    const path = join(dir, entry);
    const s = statSync(path);
    if (s.isDirectory()) yield* walk(path);
    else if (/\.(tsx?|css)$/.test(entry)) yield path;
  }
}

let totalRenames = 0;
let totalFlagged = 0;
const flaggedByFile = new Map();

for (const file of walk(SRC)) {
  const rel = relative(ROOT, file);
  const original = readFileSync(file, "utf8");
  let updated = original;
  let fileRenames = 0;
  const fileFlags = [];

  for (const rule of RULES) {
    const before = updated;
    updated = updated.replace(rule.from, rule.to);
    if (updated !== before) {
      const count = (before.match(rule.from) || []).length;
      fileRenames += count;
      if (VERBOSE) console.log(`  ${rel}: ${rule.label} (${count})`);
    }
  }

  const isIndexCss = rel.endsWith("src/index.css");
  for (const rule of FLAG_RULES) {
    if (isIndexCss && rule.label.startsWith("raw hex")) continue;
    const matches = original.match(rule.pattern);
    if (matches) fileFlags.push({ ...rule, count: matches.length });
  }

  if (fileRenames > 0) {
    totalRenames += fileRenames;
    if (!VERBOSE) console.log(`  ${rel}: ${fileRenames} rename(s)`);
    if (APPLY) writeFileSync(file, updated, "utf8");
  }

  if (fileFlags.length > 0) {
    totalFlagged += fileFlags.reduce((a, f) => a + f.count, 0);
    flaggedByFile.set(rel, fileFlags);
  }
}

console.log("");
console.log(`Renames: ${totalRenames}${APPLY ? " (applied)" : " (dry-run)"}`);
console.log(`Flagged for manual review: ${totalFlagged}`);

if (flaggedByFile.size > 0) {
  console.log("");
  console.log("Manual review needed:");
  for (const [file, flags] of flaggedByFile) {
    console.log(`  ${file}`);
    for (const f of flags) console.log(`    [${f.count}] ${f.label} — ${f.fix}`);
  }
}

if (!APPLY) {
  console.log("");
  console.log("Dry-run only. Re-run with --apply to write changes.");
}

if (APPLY && totalRenames > 0) {
  console.log("");
  console.log("Next steps:");
  console.log("  1. bun run build           # verify no broken utilities");
  console.log("  2. bun run check           # verify lint passes");
  console.log("  3. Component rewrites      # codemod is structural; visual fidelity needs Phase 2");
}
