import tokens from "@/design/design-tokens.json";

// Reads design/design-tokens.json (source of truth) and resolves {color.x.y} references to hex.
type Node = { $value?: string; $description?: string; [k: string]: unknown };

export type ColorToken = { name: string; path: string; hex: string; ref: string | null; description: string };

function walk(node: Node, path: string[], out: { path: string[]; value: string; description: string }[]) {
  for (const [k, v] of Object.entries(node)) {
    if (k.startsWith("$") && k !== "$root") continue;
    const child = v as Node;
    if (typeof child?.$value === "string") {
      out.push({ path: k === "$root" ? path : [...path, k], value: child.$value, description: child.$description ?? "" });
    } else if (child && typeof child === "object") {
      walk(child, k === "$root" ? path : [...path, k], out);
    }
  }
}

const raw: { path: string[]; value: string; description: string }[] = [];
walk(tokens.color as unknown as Node, [], raw);

const byPath = new Map(raw.map((r) => [r.path.join("."), r]));

function resolve(value: string): string {
  const m = /^\{color\.(.+)\}$/.exec(value);
  if (!m) return value.toUpperCase();
  const key = m[1].replace(/\.\$root$/, "");
  const target = byPath.get(key);
  return target ? resolve(target.value) : value;
}

const PRIMITIVE_HUES = ["white", "ink", "teal", "lime", "clay", "cobalt", "ochre", "gray", "mist", "green", "amber", "red", "blue", "slate"];

const all: ColorToken[] = raw.map((r) => ({
  name: r.path.join("-"),
  path: r.path.join("."),
  hex: resolve(r.value),
  ref: r.value.startsWith("{") ? r.value.replace(/^\{color\./, "").replace(/\}$/, "").replace(/\.\$root$/, "") : null,
  description: r.description,
}));

export const primitives: { hue: string; steps: ColorToken[] }[] = PRIMITIVE_HUES.map((hue) => ({
  hue,
  steps: all.filter((t) => t.path === hue || t.path.startsWith(hue + ".")),
}));

export const semantic: ColorToken[] = all.filter((t) => !PRIMITIVE_HUES.includes(t.path.split(".")[0]));

// Chart tokens (design-tokens.json → chart.*), resolved to hex like the colour tokens.
const chartRaw: { path: string[]; value: string; description: string }[] = [];
walk(tokens.chart as unknown as Node, [], chartRaw);
export const chart: ColorToken[] = chartRaw.map((r) => ({
  name: `chart-${r.path.join("-")}`,
  path: r.path.join("."),
  hex: resolve(r.value),
  ref: r.value.startsWith("{") ? r.value.replace(/^\{color\./, "").replace(/\}$/, "") : null,
  description: r.description,
}));
/** A chart group's description; no group gives the chart set's own. */
export const chartGroupDescription = (group?: string) => {
  const c = tokens.chart as unknown as Node & Record<string, Node>;
  return ((group ? c[group]?.$description : c.$description) as string | undefined) ?? "";
};

// ---- contrast (WCAG 2.x) ----------------------------------------------------

function lum(hex: string): number {
  const h = hex.replace("#", "");
  const [r, g, b] = [0, 2, 4]
    .map((i) => parseInt(h.slice(i, i + 2), 16) / 255)
    .map((c) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4));
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
export function contrast(a: string, b: string): number {
  const [hi, lo] = [lum(a), lum(b)].sort((x, y) => y - x);
  return (hi + 0.05) / (lo + 0.05);
}

export type Level = "AAA" | "AA" | "AA large" | "Fail";
export function textLevel(ratio: number): Level {
  return ratio >= 7 ? "AAA" : ratio >= 4.5 ? "AA" : ratio >= 3 ? "AA large" : "Fail";
}
export function uiLevel(ratio: number): "Pass 3:1" | "Fail" {
  return ratio >= 3 ? "Pass 3:1" : "Fail";
}

export const hexOf = (path: string) => all.find((t) => t.path === path)!.hex;
