#!/usr/bin/env node
/**
 * @file One-off codemod: rewrites raw Tailwind gray-scale utility classes
 * (neutral, gray, slate, zinc, stone shades) across client/src to the
 * semantic surface/border/fg tokens introduced for dark/light mode.
 * Status colors (emerald/red/amber) and the categorical hue palette
 * (violet/indigo/cyan/teal/sky/rose/pink/orange/yellow) are deliberately
 * out of scope — see docs/superpowers/plans/2026-07-31-color-redesign-dark-light-mode.md.
 * @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
 */
import { readdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";

const MAP = {
  text: {
    "gray-50": "fg-primary",
    "gray-100": "fg-primary",
    "gray-200": "fg-primary",
    "neutral-50": "fg-primary",
    "neutral-100": "fg-primary",
    "gray-300": "fg-secondary",
    "gray-400": "fg-secondary",
    "neutral-200": "fg-secondary",
    "neutral-300": "fg-secondary",
    "neutral-400": "fg-secondary",
    "slate-300": "fg-secondary",
    "gray-500": "fg-muted",
    "gray-600": "fg-muted",
    "gray-700": "fg-muted",
    "neutral-500": "fg-muted",
    "neutral-600": "fg-muted",
  },
  placeholder: {
    "gray-500": "fg-muted",
    "gray-600": "fg-muted",
  },
  fill: {
    "gray-100": "fg-primary",
    "gray-300": "fg-secondary",
    "gray-600": "fg-muted",
  },
  bg: {
    "neutral-900": "surface-0",
    "gray-900": "surface-0",
    "neutral-800": "surface-2",
    "gray-800": "surface-2",
    "neutral-700": "surface-3",
    "gray-700": "surface-3",
    "neutral-500": "surface-4",
    "gray-500": "surface-4",
    "gray-400": "surface-4",
    "gray-600": "surface-4",
    "slate-500": "surface-4",
  },
  border: {
    "neutral-800": "border",
    "gray-800": "border",
    "neutral-700": "border-light",
    "gray-700": "border-light",
    "neutral-500": "border-light",
    "gray-500": "border-light",
    "gray-600": "border-light",
  },
  ring: {
    "slate-500": "border-light",
    "slate-400": "border-light",
  },
};

function rewrite(text) {
  let out = text;
  for (const [prop, shadeMap] of Object.entries(MAP)) {
    for (const [rawShade, token] of Object.entries(shadeMap)) {
      // Matches `bg-gray-500`, `bg-gray-500/10`, `bg-gray-500/50` etc. — the
      // opacity suffix is preserved verbatim since the token's CSS var
      // already supports `<alpha-value>`.
      const re = new RegExp(`\\b${prop}-${rawShade}(\\/[0-9]+)?\\b`, "g");
      out = out.replace(re, (_m, opacity) => `${prop}-${token}${opacity || ""}`);
    }
  }
  return out;
}

function walk(dir, files = []) {
  for (const entry of readdirSync(dir)) {
    const full = join(dir, entry);
    const st = statSync(full);
    if (st.isDirectory()) {
      if (entry === "node_modules") continue;
      walk(full, files);
    } else if (entry.endsWith(".tsx")) {
      files.push(full);
    }
  }
  return files;
}

const scriptDir = fileURLToPath(new URL(".", import.meta.url));
const root = join(scriptDir, "..", "client", "src");
const files = walk(root);
let changed = 0;
for (const file of files) {
  const before = readFileSync(file, "utf8");
  const after = rewrite(before);
  if (after !== before) {
    writeFileSync(file, after);
    changed++;
  }
}
console.log(`rewrote ${changed} file(s)`);
