// data.jsx — REAL data layer for the Codex: static type/archetype maps + async loaders that fetch the
// exported data (/data/manifest.json, /data/heads/head-<id>.json, /data/teams.json) and serve sprites
// from /if-fusions/<head>.<body>.png. Exposes to window: TYPE_COLORS, TYPE_LIST, ARCHETYPES,
// archetypeMeta, prettify, SPRITE_BASE, IF { loadManifest, loadHead, loadTeams }.

// ── 18 Pokémon type base colors (chips tint/outline these). Keys are Title-Case. ──
const TYPE_COLORS = {
  Normal: "#9aa0a8",
  Fire: "#ff7a3c",
  Water: "#4f9bff",
  Electric: "#ffcf36",
  Grass: "#52c66a",
  Ice: "#5fd0e0",
  Fighting: "#e85b46",
  Poison: "#bd6bd6",
  Ground: "#e0b24e",
  Flying: "#8fa9ff",
  Psychic: "#ff6fa3",
  Bug: "#a3c23f",
  Rock: "#c9b06b",
  Ghost: "#8d72d8",
  Dragon: "#6f5def",
  Dark: "#7c7a88",
  Steel: "#69b6c8",
  Fairy: "#ff9ad0",
};
const TYPE_LIST = Object.keys(TYPE_COLORS);

const ARCHETYPES = {
  SETUP_SWEEPER: { label: "Setup Sweeper", short: "Sweeper", hue: "#ff5d8f" },
  REGEN_PIVOT: { label: "Regen Pivot", short: "Pivot", hue: "#2fd4c4" },
  HAZARD_LEAD: { label: "Hazard Lead", short: "Hazards", hue: "#f0a93c" },
  WALLBREAKER: { label: "Wallbreaker", short: "Breaker", hue: "#ff6b54" },
  WEATHER_CORE: { label: "Weather Core", short: "Weather", hue: "#52a7ff" },
  TRICK_ROOM: { label: "Trick Room", short: "Tr. Room", hue: "#b07cff" },
  STATUS: { label: "Status", short: "Status", hue: "#62cf7a" },
  MAGIC_GUARD_LO: { label: "Magic Guard", short: "M.Guard", hue: "#7d8cff" },
  BULKY_ATTACKER: { label: "Bulky Attacker", short: "Bulky Atk", hue: "#e0a14e" },
};
const prettify = s =>
  String(s || "")
    .toLowerCase()
    .replace(/_/g, " ")
    .replace(/\b\w/g, c => c.toUpperCase());
const archetypeMeta = k => ARCHETYPES[k] || { label: prettify(k || "—"), short: "—", hue: "#8b93a7" };

const SPRITE_BASE = "/if-fusions";

// The exporter emits types as enum names (e.g. "FIRE"); the UI keys colors Title-Case ("Fire").
const titleType = t => prettify(t);
const normFusion = f => ({ ...f, types: (f.types || []).map(titleType) });
const normMember = m => ({ ...m, types: (m.types || []).map(titleType) });
const normTeam = t => ({
  ...t,
  specialty: t.specialty ? titleType(t.specialty) : null,
  members: (t.members || []).map(normMember),
});

// ── loaders (cached) ──────────────────────────────────────────────────────────
let _manifest = null;
async function loadManifest() {
  if (!_manifest) {
    const r = await fetch("/data/manifest.json");
    if (!r.ok) {
      throw new Error(`manifest.json (${r.status}) — run "pnpm run codex:export"`);
    }
    _manifest = await r.json();
  }
  return _manifest;
}

const _headCache = new Map();
function loadHead(id) {
  if (!_headCache.has(id)) {
    _headCache.set(
      id,
      fetch(`/data/heads/head-${id}.json`)
        .then(r => {
          if (!r.ok) {
            throw new Error(`head-${id}.json (${r.status})`);
          }
          return r.json();
        })
        .then(a => a.map(normFusion)),
    );
  }
  return _headCache.get(id);
}

let _teams = null;
function loadTeams() {
  if (!_teams) {
    _teams = fetch("/data/teams.json")
      .then(r => {
        if (!r.ok) {
          throw new Error(`teams.json (${r.status})`);
        }
        return r.json();
      })
      .then(a => a.map(normTeam));
  }
  return _teams;
}

// Global fuzzy-search index across EVERY fusion (head OR body, name/ability/axis/moves). One compact
// positional-array file (~18MB); each row is hydrated into a slim fusion object + a lowercased
// haystack. Generated by export/build-search-index.mjs. Cached after first load.
const STAT_LABELS = ["HP", "Atk", "Def", "SpA", "SpD", "Spe"];
let _searchIndex = null;
function loadSearchIndex() {
  if (!_searchIndex) {
    _searchIndex = fetch("/data/search-index.json")
      .then(r => {
        if (!r.ok) {
          throw new Error(`search-index.json (${r.status}) — run "node fusion-review-app/export/build-search-index.mjs"`);
        }
        return r.json();
      })
      .then(rows =>
        rows.map(row => {
          const [headId, bodyId, name, headName, bodyName, typesU, bst, sprite, archetype, ability, hiddenAbility, signatureAxis, sigNames, statVals] = row;
          const types = typesU ? typesU.split(" ").map(titleType) : [];
          const stats = (statVals || []).map((v, i) => ({ label: STAT_LABELS[i], value: v }));
          const signatureSet = (sigNames || []).map((nm, i) => ({ id: headId * 100000 + bodyId * 10 + i, name: nm }));
          const hay = `${name} ${headName} ${bodyName} ${ability} ${hiddenAbility} ${signatureAxis} ${(sigNames || []).join(" ")} ${archetype}`.toLowerCase();
          return { headId, bodyId, name, headName, bodyName, types, bst, sprite, archetype, ability, hiddenAbility, signatureAxis, stats, signatureSet, _hay: hay, _slim: true };
        }),
      );
  }
  return _searchIndex;
}

Object.assign(window, {
  TYPE_COLORS,
  TYPE_LIST,
  ARCHETYPES,
  archetypeMeta,
  prettify,
  SPRITE_BASE,
  IF: { loadManifest, loadHead, loadTeams, loadSearchIndex },
});
