import React, { useState, useEffect, useCallback, useRef } from "react";
import "./fabula-ultima-sheet.css";

const STATUSES = ["Slow", "Enraged", "Dazed", "Weak", "Poisoned", "Shaken"];
const FEELINGS = [
  "Admiration",
  "Inferiority",
  "Loyalty",
  "Mistrust",
  "Affection",
  "Hatred",
];
const MARTIAL_ITEMS = [
  "Martial Armor",
  "Martial Shields",
  "Martial Melee",
  "Martial Ranged",
];
const DISCIPLINES = [
  "Arcanism",
  "Chimerism",
  "Elementalism",
  "Entropism",
  "Ritualism",
  "Spiritism",
];

interface Fields {
  charName: string;
  charPronouns: string;
  charIdentity: string;
  charTheme: string;
  charOrigin: string;
  charTraits: string;
  xpCurrent: string | number;
  zenit: string | number;
  initMod: string | number;
  defense: string | number;
  magDef: string | number;
  dexBase: string | number;
  dexCur: string | number;
  insBase: string | number;
  insCur: string | number;
  migBase: string | number;
  migCur: string | number;
  wlpBase: string | number;
  wlpCur: string | number;
  hpMax: string | number;
  hpCur: string | number;
  mpMax: string | number;
  mpCur: string | number;
  ipMax: string | number;
  ipCur: string | number;
  backpack: string;
  heroicSkills: string;
  ritualsNotes: string;
  accName: string;
  accDesc: string;
  armName: string;
  armDesc: string;
  mhName: string;
  mhDesc: string;
  ohName: string;
  ohDesc: string;
}

interface Bond {
  name: string;
  feelings: string[];
}

interface ClassEntry {
  name: string;
  benefits: string;
  skills: string;
}

interface Spell {
  name: string;
  notes: string;
  mp: string | number;
  targets: string;
  duration: string;
}

type CheckMap = Record<string, boolean>;

// Saved/shared payloads use abbreviated keys (with legacy long-name
// fallbacks), so the shape is intentionally loose.
type SavedData = Record<string, any>;

const BLANK_FIELDS: Fields = {
  charName: "",
  charPronouns: "",
  charIdentity: "",
  charTheme: "",
  charOrigin: "",
  charTraits: "",
  xpCurrent: 0,
  zenit: 0,
  initMod: 0,
  defense: 0,
  magDef: 0,
  dexBase: 6,
  dexCur: 6,
  insBase: 6,
  insCur: 6,
  migBase: 6,
  migCur: 6,
  wlpBase: 6,
  wlpCur: 6,
  hpMax: "",
  hpCur: "",
  mpMax: "",
  mpCur: "",
  ipMax: 6,
  ipCur: 6,
  backpack: "",
  heroicSkills: "",
  ritualsNotes: "",
  accName: "",
  accDesc: "",
  armName: "",
  armDesc: "",
  mhName: "",
  mhDesc: "",
  ohName: "",
  ohDesc: "",
};

const BLANK_BONDS: Bond[] = Array.from({ length: 6 }, () => ({
  name: "",
  feelings: [],
}));

// URL-safe base64 (RFC 4648 §5): avoids +, /, = so the result needs no
// percent-encoding in a query string. Accepts standard base64 on decode too,
// so links shared before this change still load.
function bytesToBase64Url(bytes: Uint8Array) {
  let binary = "";
  for (let i = 0; i < bytes.length; i++)
    binary += String.fromCharCode(bytes[i]);
  return btoa(binary)
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

function base64UrlToBytes(b64: string) {
  let std = b64.replace(/-/g, "+").replace(/_/g, "/");
  if (std.length % 4) std += "=".repeat(4 - (std.length % 4));
  return Uint8Array.from(atob(std), (c) => c.charCodeAt(0));
}

async function compressToBase64(str: string) {
  const stream = new CompressionStream("deflate-raw");
  const writer = stream.writable.getWriter();
  writer.write(new TextEncoder().encode(str));
  writer.close();
  const buf = await new Response(stream.readable).arrayBuffer();
  return bytesToBase64Url(new Uint8Array(buf));
}

async function decompressFromBase64(b64: string) {
  try {
    const bytes = base64UrlToBytes(b64);
    const stream = new DecompressionStream("deflate-raw");
    const writer = stream.writable.getWriter();
    writer.write(bytes);
    writer.close();
    const buf = await new Response(stream.readable).arrayBuffer();
    return new TextDecoder().decode(buf);
  } catch {
    return decodeURIComponent(escape(atob(b64)));
  }
}

// Drop empty strings and empty arrays before encoding: a sheet is mostly blank
// text fields, and applyData() restores any missing key to its default.
function pruneEmpty(data: Record<string, unknown>) {
  return Object.fromEntries(
    Object.entries(data).filter(
      ([, v]) => v !== "" && !(Array.isArray(v) && v.length === 0),
    ),
  );
}

// Resolve a share-service path relative to the current page so it works under
// any base path (e.g. /fabula/) where Caddy reverse-proxies /api/* to the
// backend. Returns e.g. https://host/fabula/api/s.
function apiURL(path: string) {
  return new URL(path, window.location.href).toString();
}

// Copy text to the clipboard. navigator.clipboard is only available in a
// secure context (HTTPS or localhost); over plain HTTP it's undefined, so we
// fall back to the deprecated execCommand("copy"), which still works there.
// Must be called from within a user gesture (e.g. a click handler).
async function copyToClipboard(text: string) {
  if (navigator.clipboard && window.isSecureContext) {
    await navigator.clipboard.writeText(text);
    return;
  }
  const ta = document.createElement("textarea");
  ta.value = text;
  ta.style.position = "fixed";
  ta.style.opacity = "0";
  document.body.appendChild(ta);
  ta.focus();
  ta.select();
  try {
    document.execCommand("copy");
  } finally {
    document.body.removeChild(ta);
  }
}

export default function CharacterSheet() {
  const [activeTab, setActiveTab] = useState("main");
  const [urlMode, setUrlMode] = useState(false);
  const [saveStatus, setSaveStatus] = useState(false);
  const [copyStatus, setCopyStatus] = useState(false);
  const [level, setLevel] = useState(1);
  const [fp, setFp] = useState(0);
  const [fields, setFields] = useState<Fields>(BLANK_FIELDS);
  const [bonds, setBonds] = useState<Bond[]>(BLANK_BONDS);
  const [primaryClasses, setPrimaryClasses] = useState<ClassEntry[]>([]);
  const [otherClasses, setOtherClasses] = useState<ClassEntry[]>([]);
  const [spells, setSpells] = useState<Spell[]>([]);
  const [statuses, setStatuses] = useState<CheckMap>({});
  const [martial, setMartial] = useState<CheckMap>({});
  const [disciplines, setDisciplines] = useState<CheckMap>({});
  const [theme, setTheme] = useState<string>(
    () =>
      localStorage.getItem("fabulaUltimaTheme") ||
      (window.matchMedia("(prefers-color-scheme: light)").matches
        ? "light"
        : "dark"),
  );

  const importFileRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    document.documentElement.dataset.theme = theme;
    localStorage.setItem("fabulaUltimaTheme", theme);
  }, [theme]);

  const f = useCallback(
    <K extends keyof Fields>(key: K, val: Fields[K]) =>
      setFields((prev) => ({ ...prev, [key]: val })),
    [],
  );

  const xp = parseInt(String(fields.xpCurrent)) || 0;
  const xpPct = Math.min((xp % 10) * 10, 100);
  const hpMax = parseInt(String(fields.hpMax)) || 0;
  const hpCur = parseInt(String(fields.hpCur)) || 0;
  const inCrisis = hpMax > 0 && hpCur <= Math.floor(hpMax / 2);
  const fpTotal = Math.max(10, fp);

  const collectData = useCallback(
    () => ({
      n: fields.charName,
      pn: fields.charPronouns,
      id: fields.charIdentity,
      th: fields.charTheme,
      og: fields.charOrigin,
      tr: fields.charTraits,
      lv: level,
      xp: fields.xpCurrent,
      z: fields.zenit,
      im: fields.initMod,
      df: fields.defense,
      md: fields.magDef,
      dxb: fields.dexBase,
      dxc: fields.dexCur,
      inb: fields.insBase,
      inc: fields.insCur,
      mgb: fields.migBase,
      mgc: fields.migCur,
      wpb: fields.wlpBase,
      wpc: fields.wlpCur,
      hx: fields.hpMax,
      hc: fields.hpCur,
      mx: fields.mpMax,
      mc: fields.mpCur,
      ix: fields.ipMax,
      ic: fields.ipCur,
      fp,
      bp: fields.backpack,
      acn: fields.accName,
      acd: fields.accDesc,
      amn: fields.armName,
      amd: fields.armDesc,
      mhn: fields.mhName,
      mhd: fields.mhDesc,
      ohn: fields.ohName,
      ohd: fields.ohDesc,
      hs: fields.heroicSkills,
      rn: fields.ritualsNotes,
      sa: STATUSES.filter((s) => statuses[s]),
      ma: MARTIAL_ITEMS.filter((m) => martial[m]),
      da: DISCIPLINES.filter((d) => disciplines[d]),
      bo: bonds.map((b) => ({ n: b.name, f: b.feelings })),
      pc: primaryClasses.map((c) => ({
        n: c.name,
        b: c.benefits,
        s: c.skills,
      })),
      oc: otherClasses.map((c) => ({ n: c.name, b: c.benefits, s: c.skills })),
      sp: spells.map((s) => ({
        n: s.name,
        nt: s.notes,
        mp: s.mp,
        tg: s.targets,
        dr: s.duration,
      })),
    }),
    [
      fields,
      level,
      fp,
      bonds,
      primaryClasses,
      otherClasses,
      spells,
      statuses,
      martial,
      disciplines,
    ],
  );

  const applyData = useCallback((d: SavedData) => {
    setFields({
      charName: d.n ?? d.name ?? "",
      charPronouns: d.pn ?? d.pronouns ?? "",
      charIdentity: d.id ?? d.identity ?? "",
      charTheme: d.th ?? d.theme ?? "",
      charOrigin: d.og ?? d.origin ?? "",
      charTraits: d.tr ?? d.traits ?? "",
      xpCurrent: d.xp ?? 0,
      zenit: d.z ?? d.zenit ?? 0,
      initMod: d.im ?? d.initMod ?? 0,
      defense: d.df ?? d.defense ?? 0,
      magDef: d.md ?? d.magDef ?? 0,
      dexBase: d.dxb ?? d.dexBase ?? 6,
      dexCur: d.dxc ?? d.dexCur ?? 6,
      insBase: d.inb ?? d.insBase ?? 6,
      insCur: d.inc ?? d.insCur ?? 6,
      migBase: d.mgb ?? d.migBase ?? 6,
      migCur: d.mgc ?? d.migCur ?? 6,
      wlpBase: d.wpb ?? d.wlpBase ?? 6,
      wlpCur: d.wpc ?? d.wlpCur ?? 6,
      hpMax: d.hx ?? d.hpMax ?? "",
      hpCur: d.hc ?? d.hpCur ?? "",
      mpMax: d.mx ?? d.mpMax ?? "",
      mpCur: d.mc ?? d.mpCur ?? "",
      ipMax: d.ix ?? d.ipMax ?? 6,
      ipCur: d.ic ?? d.ipCur ?? 6,
      backpack: d.bp ?? d.backpack ?? "",
      accName: d.acn ?? d.accName ?? "",
      accDesc: d.acd ?? d.accDesc ?? "",
      armName: d.amn ?? d.armName ?? "",
      armDesc: d.amd ?? d.armDesc ?? "",
      mhName: d.mhn ?? d.mhName ?? "",
      mhDesc: d.mhd ?? d.mhDesc ?? "",
      ohName: d.ohn ?? d.ohName ?? "",
      ohDesc: d.ohd ?? d.ohDesc ?? "",
      heroicSkills: d.hs ?? d.heroicSkills ?? "",
      ritualsNotes: d.rn ?? d.ritualsNotes ?? "",
    });
    setLevel(d.lv ?? d.level ?? 1);
    setFp(parseInt(d.fp) || 0);

    const sa = d.sa ?? d.statusesActive ?? [];
    setStatuses(Object.fromEntries(STATUSES.map((s) => [s, sa.includes(s)])));
    const ma = d.ma ?? d.martialChecked ?? [];
    setMartial(
      Object.fromEntries(MARTIAL_ITEMS.map((m) => [m, ma.includes(m)])),
    );
    const da = d.da ?? d.disciplinesChecked ?? [];
    setDisciplines(
      Object.fromEntries(DISCIPLINES.map((disc) => [disc, da.includes(disc)])),
    );

    const rawBonds = d.bo ?? d.bonds;
    if (rawBonds)
      setBonds(
        rawBonds.map((b: SavedData) => ({
          name: b.n ?? b.name ?? "",
          feelings: b.f ?? b.feelings ?? [],
        })),
      );

    const rawPrimary = d.pc ?? d.primaryClasses;
    if (rawPrimary)
      setPrimaryClasses(
        rawPrimary.map((c: SavedData) => ({
          name: c.n ?? c.name ?? "",
          benefits: c.b ?? c.benefits ?? "",
          skills: c.s ?? c.skills ?? "",
        })),
      );

    const rawOther = d.oc ?? d.otherClasses;
    if (rawOther)
      setOtherClasses(
        rawOther.map((c: SavedData) => ({
          name: c.n ?? c.name ?? "",
          benefits: c.b ?? c.benefits ?? "",
          skills: c.s ?? c.skills ?? "",
        })),
      );

    const rawSpells = d.sp ?? d.spells;
    if (rawSpells)
      setSpells(
        rawSpells.map((s: SavedData) => ({
          name: s.n ?? s.name ?? "",
          notes: s.nt ?? s.notes ?? "",
          mp: s.mp ?? "",
          targets: s.tg ?? s.targets ?? "",
          duration: s.dr ?? s.duration ?? "",
        })),
      );
  }, []);

  // Init
  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const shortId = params.get("s");
    const encoded = params.get("c");
    if (shortId) {
      // Short link: fetch the sheet JSON from the share service.
      fetch(apiURL("api/s/" + encodeURIComponent(shortId)))
        .then((res) => {
          if (!res.ok) throw new Error(String(res.status));
          return res.json();
        })
        .then((data) => {
          applyData(data);
          setUrlMode(true);
        })
        .catch(() => tryAutoLoad());
    } else if (encoded) {
      // Legacy/offline self-contained link with the data inline.
      decompressFromBase64(encoded)
        .then((json) => {
          applyData(JSON.parse(json));
          setUrlMode(true);
        })
        .catch(() => tryAutoLoad());
    } else {
      tryAutoLoad();
    }
    function tryAutoLoad() {
      const raw = localStorage.getItem("fabulaUltimaSheet");
      if (raw)
        try {
          applyData(JSON.parse(raw));
        } catch (e) {}
    }
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  // Auto-save every 30s
  useEffect(() => {
    const id = setInterval(() => {
      if (urlMode) return;
      localStorage.setItem("fabulaUltimaSheet", JSON.stringify(collectData()));
    }, 30000);
    return () => clearInterval(id);
  }, [urlMode, collectData]);

  const saveSheet = useCallback(() => {
    if (urlMode) return;
    localStorage.setItem("fabulaUltimaSheet", JSON.stringify(collectData()));
    setSaveStatus(true);
    setTimeout(() => setSaveStatus(false), 2000);
  }, [urlMode, collectData]);

  const loadSheet = useCallback(() => {
    const raw = localStorage.getItem("fabulaUltimaSheet");
    if (!raw) return alert("No saved sheet found.");
    try {
      applyData(JSON.parse(raw));
    } catch {
      alert("Could not load sheet.");
    }
  }, [applyData]);

  const exportSheet = useCallback(() => {
    const data = collectData();
    const blob = new Blob([JSON.stringify(data, null, 2)], {
      type: "application/json",
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download =
      ((data.n || "character").replace(/[^a-z0-9_\- ]/gi, "").trim() ||
        "character") + "-fabula-ultima.json";
    a.click();
    URL.revokeObjectURL(url);
  }, [collectData]);

  const handleImportFile = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const file = e.target.files?.[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = (ev) => {
        try {
          applyData(JSON.parse(ev.target?.result as string));
          saveSheet();
        } catch {
          alert("Could not import: invalid JSON file.");
        }
      };
      reader.readAsText(file);
    },
    [applyData, saveSheet],
  );

  const copyShareURL = useCallback(async () => {
    const json = JSON.stringify(pruneEmpty(collectData()));
    const base = window.location.origin + window.location.pathname;
    let shareURL: string;
    try {
      // Preferred: store the sheet server-side and share a short ?s=<id> link.
      const res = await fetch(apiURL("api/s"), {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: json,
      });
      if (!res.ok) throw new Error(String(res.status));
      const { id } = await res.json();
      shareURL = base + "?s=" + id;
    } catch {
      // Backend unreachable: fall back to a self-contained inline ?c= link.
      shareURL = base + "?c=" + (await compressToBase64(json));
    }
    await copyToClipboard(shareURL);
    setCopyStatus(true);
    setTimeout(() => setCopyStatus(false), 2000);
  }, [collectData]);

  const calcHP = useCallback(() => {
    const mig = parseInt(String(fields.migBase)) || 6;
    const max = mig * 5 + level;
    setFields((prev) => ({ ...prev, hpMax: max, hpCur: prev.hpCur || max }));
  }, [fields.migBase, level]);

  const calcMP = useCallback(() => {
    const wlp = parseInt(String(fields.wlpBase)) || 6;
    const max = wlp * 5 + level;
    setFields((prev) => ({ ...prev, mpMax: max, mpCur: prev.mpCur || max }));
  }, [fields.wlpBase, level]);

  const toggleStatus = (s: string) =>
    setStatuses((prev) => ({ ...prev, [s]: !prev[s] }));
  const toggleMartial = (m: string) =>
    setMartial((prev) => ({ ...prev, [m]: !prev[m] }));
  const toggleDisc = (d: string) =>
    setDisciplines((prev) => ({ ...prev, [d]: !prev[d] }));

  const toggleFeeling = (bondIdx: number, feeling: string) => {
    setBonds((prev) =>
      prev.map((b, i) => {
        if (i !== bondIdx) return b;
        const has = b.feelings.includes(feeling);
        return {
          ...b,
          feelings: has
            ? b.feelings.filter((f) => f !== feeling)
            : [...b.feelings, feeling],
        };
      }),
    );
  };

  return (
    <>
      <header>
        <div className="logo">Fabula Ultima</div>
        <div className="tabs">
          {["main", "classes", "spells", "manage"].map((tab, i) => (
            <button
              key={tab}
              className={`tab${activeTab === tab ? " active" : ""}`}
              onClick={() => setActiveTab(tab)}
            >
              {["Character", "Classes", "Arcana & Spells", "Manage"][i]}
            </button>
          ))}
        </div>
        <div className="toolbar">
          <span className={`save-status${saveStatus ? " show" : ""}`}>
            Saved!
          </span>
          <button className="btn-print" onClick={() => window.print()}>
            ⎙ Print
          </button>
          <button
            className="btn-theme"
            onClick={() => setTheme((t) => (t === "light" ? "dark" : "light"))}
          >
            {theme === "light" ? "☾ Dark" : "☀ Light"}
          </button>
        </div>
      </header>

      {urlMode && (
        <div className="url-banner">
          <span className="url-banner-icon">⚑</span> Viewing a shared character
          — auto-save is disabled. Use <strong>Manage → Save to Browser</strong>{" "}
          to keep any changes.
        </div>
      )}

      {/* ── PAGE 1: MAIN ── */}
      <div
        className={`page${activeTab === "main" ? " active" : ""}`}
        id="page-main"
      >
        {/* Row 1: Identity + Level */}
        <div className="grid-2" style={{ marginBottom: 20 }}>
          <div className="section">
            <div className="section-title">
              <span className="icon">✦</span> Identity &amp; Traits
            </div>
            <div className="field-row">
              <div className="field" style={{ flex: 2 }}>
                <label>Name</label>
                <input
                  type="text"
                  value={fields.charName}
                  onChange={(e) => f("charName", e.target.value)}
                  placeholder="Character name…"
                />
              </div>
              <div className="field">
                <label>Pronouns</label>
                <input
                  type="text"
                  value={fields.charPronouns}
                  onChange={(e) => f("charPronouns", e.target.value)}
                  placeholder="they/them"
                />
              </div>
            </div>
            <div className="field">
              <label>Identity</label>
              <input
                type="text"
                value={fields.charIdentity}
                onChange={(e) => f("charIdentity", e.target.value)}
                placeholder="Who are you?"
              />
            </div>
            <div className="field-row">
              <div className="field">
                <label>Theme</label>
                <input
                  type="text"
                  value={fields.charTheme}
                  onChange={(e) => f("charTheme", e.target.value)}
                  placeholder="Your theme…"
                />
              </div>
              <div className="field">
                <label>Origin</label>
                <input
                  type="text"
                  value={fields.charOrigin}
                  onChange={(e) => f("charOrigin", e.target.value)}
                  placeholder="Where from?"
                />
              </div>
            </div>
            <div className="field">
              <label>Traits (comma-separated)</label>
              <textarea
                value={fields.charTraits}
                onChange={(e) => f("charTraits", e.target.value)}
                placeholder="Brave, Reckless, Loyal to a fault…"
                style={{ minHeight: 55 }}
              />
            </div>
          </div>

          <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
            <div className="section">
              <div className="section-title">
                <span className="icon">⬡</span> Level &amp; Experience
              </div>
              <div className="grid-2" style={{ gap: 14 }}>
                <div className="level-display">
                  <span className="level-num">{level}</span>
                  <span className="level-text">Character Level</span>
                  <button
                    className="add-btn"
                    style={{
                      marginTop: 10,
                      width: "100%",
                      justifyContent: "center",
                    }}
                    onClick={() => setLevel((l) => Math.min(50, l + 1))}
                  >
                    + Level Up
                  </button>
                  <button
                    className="add-btn"
                    style={{
                      marginTop: 4,
                      width: "100%",
                      justifyContent: "center",
                      borderColor: "var(--border-bright)",
                      color: "var(--text-dim)",
                    }}
                    onClick={() => setLevel((l) => Math.max(1, l - 1))}
                  >
                    − Level Down
                  </button>
                </div>
                <div>
                  <div className="field">
                    <label>Experience Points (XP)</label>
                    <input
                      type="number"
                      value={fields.xpCurrent}
                      onChange={(e) => f("xpCurrent", e.target.value)}
                      min="0"
                    />
                  </div>
                  <div className="xp-bar-wrap">
                    <div className="xp-bar" style={{ width: xpPct + "%" }} />
                  </div>
                  <div className="xp-label">
                    <span>{xp} XP</span>
                    <span>10 XP = Level</span>
                  </div>
                  <div className="field" style={{ marginTop: 12 }}>
                    <label>Zenit (currency)</label>
                    <input
                      type="number"
                      value={fields.zenit}
                      onChange={(e) => f("zenit", e.target.value)}
                      min="0"
                    />
                  </div>
                </div>
              </div>
            </div>

            <div className="section">
              <div className="section-title">
                <span className="icon">⊕</span> Defenses
              </div>
              <div className="def-row">
                <div className="def-block">
                  <label>Initiative Mod</label>
                  <input
                    type="number"
                    value={fields.initMod}
                    onChange={(e) => f("initMod", e.target.value)}
                  />
                </div>
                <div className="def-block">
                  <label>Defense</label>
                  <input
                    type="number"
                    value={fields.defense}
                    onChange={(e) => f("defense", e.target.value)}
                  />
                </div>
                <div className="def-block">
                  <label>Magic Defense</label>
                  <input
                    type="number"
                    value={fields.magDef}
                    onChange={(e) => f("magDef", e.target.value)}
                  />
                </div>
              </div>
            </div>
          </div>
        </div>

        {/* Row 2: Attributes + Status + Vitals */}
        <div className="grid-3" style={{ marginBottom: 20 }}>
          <div className="section">
            <div className="section-title">
              <span className="icon">◈</span> Attributes
            </div>
            <div className="attr-grid">
              {([
                { label: "Dexterity", base: "dexBase", cur: "dexCur" },
                { label: "Insight", base: "insBase", cur: "insCur" },
                { label: "Might", base: "migBase", cur: "migCur" },
                { label: "Willpower", base: "wlpBase", cur: "wlpCur" },
              ] as const).map(({ label, base, cur }) => (
                <div key={label} className="attr-block">
                  <div className="attr-name">{label}</div>
                  <div className="attr-inputs">
                    <div style={{ flex: 1 }}>
                      <label style={{ fontSize: "0.48rem" }}>Base</label>
                      <input
                        type="number"
                        value={fields[base]}
                        onChange={(e) => f(base, e.target.value)}
                        min="6"
                        max="12"
                      />
                    </div>
                    <div className="attr-sep">→</div>
                    <div style={{ flex: 1 }}>
                      <label style={{ fontSize: "0.48rem" }}>Current</label>
                      <input
                        type="number"
                        value={fields[cur]}
                        onChange={(e) => f(cur, e.target.value)}
                        min="6"
                        max="12"
                      />
                    </div>
                  </div>
                </div>
              ))}
            </div>
          </div>

          <div className="section">
            <div className="section-title">
              <span className="icon">⚠</span> Status Effects
            </div>
            <div className="status-grid">
              {STATUSES.map((s) => (
                <div
                  key={s}
                  className={`status-item${statuses[s] ? " active-status" : ""}`}
                  onClick={() => toggleStatus(s)}
                >
                  <div className="status-check">{statuses[s] ? "✗" : ""}</div>
                  <div className="status-label">{s}</div>
                </div>
              ))}
            </div>
          </div>

          <div className="section">
            <div className="section-title">
              <span className="icon">♥</span> Hit, Mind &amp; Inventory Points
            </div>

            <div className="vital-block">
              <div className="vital-label hp">HP</div>
              <div style={{ flex: 1 }}>
                <div className="vital-formula">MIG×5 + Level + Other</div>
                <div className="vital-inputs">
                  <input
                    type="number"
                    value={fields.hpCur}
                    onChange={(e) => f("hpCur", e.target.value)}
                    placeholder="Cur"
                  />
                  <div className="vital-sep">/</div>
                  <input
                    type="number"
                    value={fields.hpMax}
                    onChange={(e) => f("hpMax", e.target.value)}
                    placeholder="Max"
                  />
                  {inCrisis && <div className="crisis-badge">CRISIS</div>}
                </div>
              </div>
              <button
                className="add-btn"
                style={{ padding: "4px 8px" }}
                onClick={calcHP}
                title="Auto-calculate from Might"
              >
                Calc
              </button>
            </div>

            <div className="vital-block">
              <div className="vital-label mp">MP</div>
              <div style={{ flex: 1 }}>
                <div className="vital-formula">WLP×5 + Level + Other</div>
                <div className="vital-inputs">
                  <input
                    type="number"
                    value={fields.mpCur}
                    onChange={(e) => f("mpCur", e.target.value)}
                    placeholder="Cur"
                  />
                  <div className="vital-sep">/</div>
                  <input
                    type="number"
                    value={fields.mpMax}
                    onChange={(e) => f("mpMax", e.target.value)}
                    placeholder="Max"
                  />
                </div>
              </div>
              <button
                className="add-btn"
                style={{ padding: "4px 8px" }}
                onClick={calcMP}
                title="Auto-calculate from Willpower"
              >
                Calc
              </button>
            </div>

            <div className="vital-block">
              <div className="vital-label ip">IP</div>
              <div style={{ flex: 1 }}>
                <div className="vital-formula">6 + Other</div>
                <div className="vital-inputs">
                  <input
                    type="number"
                    value={fields.ipCur}
                    onChange={(e) => f("ipCur", e.target.value)}
                    placeholder="Cur"
                  />
                  <div className="vital-sep">/</div>
                  <input
                    type="number"
                    value={fields.ipMax}
                    onChange={(e) => f("ipMax", e.target.value)}
                    placeholder="Max"
                  />
                </div>
              </div>
            </div>
          </div>
        </div>

        {/* Row 3: FP + Bonds */}
        <div className="grid-2" style={{ marginBottom: 20 }}>
          <div className="section">
            <div className="section-title">
              <span className="icon">✦</span> Fabula Points
            </div>
            <div
              style={{
                display: "flex",
                alignItems: "center",
                gap: 14,
                flexWrap: "wrap",
              }}
            >
              <div>
                <label>Current FP</label>
                <input
                  type="number"
                  value={fp}
                  min="0"
                  max="20"
                  onChange={(e) => setFp(parseInt(e.target.value) || 0)}
                  style={{
                    width: 70,
                    textAlign: "center",
                    fontSize: "1.4rem",
                    fontFamily: "var(--font-mono)",
                  }}
                />
              </div>
              <div className="fp-pips">
                {Array.from({ length: fpTotal }, (_, i) => (
                  <div
                    key={i}
                    className={`fp-pip${i < fp ? " filled" : ""}`}
                    onClick={() => setFp(i < fp ? i : i + 1)}
                  />
                ))}
              </div>
            </div>
            <div className="fp-rules">
              <div className="fp-rule">
                <strong>+1 FP</strong> if you have none at start of session.
              </div>
              <div className="fp-rule">
                <strong>+1 FP</strong> when a Villain makes an entrance.
              </div>
              <div className="fp-rule">
                <strong>+1 FP</strong> when you fumble a Check.
              </div>
              <div className="fp-rule">
                <strong>+2 FP</strong> if you surrender at zero HP.
              </div>
              <div className="fp-rule" style={{ marginTop: 6 }}>
                <strong>Spend 1 FP</strong> to invoke a trait: reroll one or
                both dice.
              </div>
              <div className="fp-rule">
                <strong>Spend 1 FP</strong> to invoke a bond: add its strength
                to the result.
              </div>
              <div className="fp-rule">
                <strong>Spend 1 FP</strong> to alter the story.
              </div>
            </div>
          </div>

          <div className="section">
            <div className="section-title">
              <span className="icon">⊗</span> Bonds
            </div>
            {bonds.map((bond, idx) => (
              <div key={idx} className="bond-block">
                <div className="bond-header">
                  <div className="bond-num">{idx + 1}</div>
                  <input
                    type="text"
                    placeholder="Bond target name…"
                    value={bond.name}
                    onChange={(e) =>
                      setBonds((prev) =>
                        prev.map((b, i) =>
                          i === idx ? { ...b, name: e.target.value } : b,
                        ),
                      )
                    }
                  />
                </div>
                <div className="bond-feelings">
                  {FEELINGS.map((feeling) => (
                    <div
                      key={feeling}
                      className={`bond-feeling${bond.feelings.includes(feeling) ? " active" : ""}`}
                      onClick={() => toggleFeeling(idx, feeling)}
                    >
                      <div className="bond-feeling-box">
                        {bond.feelings.includes(feeling) ? "✓" : ""}
                      </div>
                      <span>{feeling}</span>
                    </div>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>

        {/* Row 4: Equipment + Backpack */}
        <div className="grid-2" style={{ marginBottom: 20 }}>
          <div className="section">
            <div className="section-title">
              <span className="icon">⚔</span> Equipment
            </div>
            <div className="martial-row">
              {MARTIAL_ITEMS.map((m) => (
                <div
                  key={m}
                  className={`martial-item${martial[m] ? " checked" : ""}`}
                  onClick={() => toggleMartial(m)}
                >
                  <div className="martial-box">{martial[m] ? "✓" : ""}</div>
                  <span>{m}</span>
                </div>
              ))}
            </div>
            <div style={{ marginTop: 14 }}>
              {([
                {
                  slot: "Accessory",
                  name: "accName",
                  desc: "accDesc",
                  namePh: "Item name",
                  descPh: "Description / effect",
                },
                {
                  slot: "Armor",
                  name: "armName",
                  desc: "armDesc",
                  namePh: "Item name",
                  descPh: "Defense bonus / effect",
                },
                {
                  slot: "Main Hand",
                  name: "mhName",
                  desc: "mhDesc",
                  namePh: "Weapon name",
                  descPh: "Damage / effect",
                },
                {
                  slot: "Off-Hand",
                  name: "ohName",
                  desc: "ohDesc",
                  namePh: "Weapon / shield",
                  descPh: "Damage / effect",
                },
              ] as const).map(({ slot, name, desc, namePh, descPh }) => (
                <div key={slot} className="equip-row">
                  <div className="equip-slot">{slot}</div>
                  <div className="equip-fields">
                    <input
                      type="text"
                      value={fields[name]}
                      onChange={(e) => f(name, e.target.value)}
                      placeholder={namePh}
                    />
                    <input
                      type="text"
                      value={fields[desc]}
                      onChange={(e) => f(desc, e.target.value)}
                      placeholder={descPh}
                    />
                  </div>
                </div>
              ))}
            </div>
          </div>

          <div className="section">
            <div className="section-title">
              <span className="icon">◉</span> Backpack &amp; Notes
            </div>
            <textarea
              value={fields.backpack}
              onChange={(e) => f("backpack", e.target.value)}
              placeholder="Items, notes, lore…"
              style={{ minHeight: 200 }}
            />
          </div>
        </div>
      </div>

      {/* ── PAGE 2: CLASSES ── */}
      <div
        className={`page${activeTab === "classes" ? " active" : ""}`}
        id="page-classes"
      >
        <div className="grid-2" style={{ marginBottom: 20 }}>
          <div className="section">
            <div className="section-title">
              <span className="icon">✦</span> Primary Classes (up to 3 levels
              each)
            </div>
            {primaryClasses.map((cls, idx) => (
              <div key={idx} className="class-block">
                <div className="class-header">
                  <input
                    type="text"
                    placeholder="Class name…"
                    value={cls.name || ""}
                    onChange={(e) =>
                      setPrimaryClasses((prev) =>
                        prev.map((c, i) =>
                          i === idx ? { ...c, name: e.target.value } : c,
                        ),
                      )
                    }
                  />
                  <input
                    type="text"
                    placeholder="Free benefits…"
                    value={cls.benefits || ""}
                    onChange={(e) =>
                      setPrimaryClasses((prev) =>
                        prev.map((c, i) =>
                          i === idx ? { ...c, benefits: e.target.value } : c,
                        ),
                      )
                    }
                  />
                </div>
                <div className="class-skills">
                  <textarea
                    placeholder="Skill information…"
                    value={cls.skills || ""}
                    onChange={(e) =>
                      setPrimaryClasses((prev) =>
                        prev.map((c, i) =>
                          i === idx ? { ...c, skills: e.target.value } : c,
                        ),
                      )
                    }
                  />
                </div>
                <div
                  style={{
                    padding: "6px 10px",
                    borderTop: "1px solid var(--border)",
                    display: "flex",
                    justifyContent: "flex-end",
                  }}
                >
                  <button
                    className="spell-del-btn"
                    onClick={() =>
                      setPrimaryClasses((prev) =>
                        prev.filter((_, i) => i !== idx),
                      )
                    }
                  >
                    ✕ Remove
                  </button>
                </div>
              </div>
            ))}
            <button
              className="add-btn"
              onClick={() =>
                setPrimaryClasses((prev) => [
                  ...prev,
                  { name: "", benefits: "", skills: "" },
                ])
              }
            >
              + Add Primary Class
            </button>
          </div>

          <div className="section">
            <div className="section-title">
              <span className="icon">◎</span> Other Classes (max 3 non-mastered)
            </div>
            {otherClasses.map((cls, idx) => (
              <div key={idx} className="class-block">
                <div className="class-header">
                  <input
                    type="text"
                    placeholder="Class name…"
                    value={cls.name || ""}
                    onChange={(e) =>
                      setOtherClasses((prev) =>
                        prev.map((c, i) =>
                          i === idx ? { ...c, name: e.target.value } : c,
                        ),
                      )
                    }
                  />
                  <input
                    type="text"
                    placeholder="Free benefits…"
                    value={cls.benefits || ""}
                    onChange={(e) =>
                      setOtherClasses((prev) =>
                        prev.map((c, i) =>
                          i === idx ? { ...c, benefits: e.target.value } : c,
                        ),
                      )
                    }
                  />
                </div>
                <div className="class-skills">
                  <textarea
                    placeholder="Skill information…"
                    value={cls.skills || ""}
                    onChange={(e) =>
                      setOtherClasses((prev) =>
                        prev.map((c, i) =>
                          i === idx ? { ...c, skills: e.target.value } : c,
                        ),
                      )
                    }
                  />
                </div>
                <div
                  style={{
                    padding: "6px 10px",
                    borderTop: "1px solid var(--border)",
                    display: "flex",
                    justifyContent: "flex-end",
                  }}
                >
                  <button
                    className="spell-del-btn"
                    onClick={() =>
                      setOtherClasses((prev) =>
                        prev.filter((_, i) => i !== idx),
                      )
                    }
                  >
                    ✕ Remove
                  </button>
                </div>
              </div>
            ))}
            <button
              className="add-btn"
              onClick={() =>
                setOtherClasses((prev) => [
                  ...prev,
                  { name: "", benefits: "", skills: "" },
                ])
              }
            >
              + Add Class
            </button>
          </div>
        </div>

        <div className="section">
          <div className="section-title">
            <span className="icon">★</span> Heroic Skills
          </div>
          <textarea
            value={fields.heroicSkills}
            onChange={(e) => f("heroicSkills", e.target.value)}
            placeholder="Record your heroic skill abilities here…"
            style={{ minHeight: 100 }}
          />
        </div>
      </div>

      {/* ── PAGE 3: ARCANA & SPELLS ── */}
      <div
        className={`page${activeTab === "spells" ? " active" : ""}`}
        id="page-spells"
      >
        <div className="section" style={{ marginBottom: 20 }}>
          <div className="section-title">
            <span className="icon">✦</span> Arcana &amp; Spells
          </div>
          <table className="spells-table">
            <thead>
              <tr>
                <th className="spell-name-col">Name / Notes</th>
                <th className="spell-mp-col">MP Cost</th>
                <th className="spell-targets-col">Targets</th>
                <th className="spell-dur-col">Duration</th>
                <th className="spell-del-col"></th>
              </tr>
            </thead>
            <tbody>
              {spells.map((s, i) => (
                <tr key={i}>
                  <td className="spell-name-col">
                    <input
                      type="text"
                      placeholder="Spell / Arcana name…"
                      value={s.name || ""}
                      onChange={(e) =>
                        setSpells((prev) =>
                          prev.map((sp, j) =>
                            j === i ? { ...sp, name: e.target.value } : sp,
                          ),
                        )
                      }
                    />
                    <textarea
                      placeholder="Notes / effect description…"
                      value={s.notes || ""}
                      onChange={(e) =>
                        setSpells((prev) =>
                          prev.map((sp, j) =>
                            j === i ? { ...sp, notes: e.target.value } : sp,
                          ),
                        )
                      }
                    />
                  </td>
                  <td className="spell-mp-col">
                    <input
                      type="number"
                      placeholder="0"
                      value={s.mp || ""}
                      onChange={(e) =>
                        setSpells((prev) =>
                          prev.map((sp, j) =>
                            j === i ? { ...sp, mp: e.target.value } : sp,
                          ),
                        )
                      }
                      style={{ minHeight: 32 }}
                    />
                  </td>
                  <td className="spell-targets-col">
                    <input
                      type="text"
                      placeholder="Target(s)…"
                      value={s.targets || ""}
                      onChange={(e) =>
                        setSpells((prev) =>
                          prev.map((sp, j) =>
                            j === i ? { ...sp, targets: e.target.value } : sp,
                          ),
                        )
                      }
                    />
                  </td>
                  <td className="spell-dur-col">
                    <input
                      type="text"
                      placeholder="Duration…"
                      value={s.duration || ""}
                      onChange={(e) =>
                        setSpells((prev) =>
                          prev.map((sp, j) =>
                            j === i ? { ...sp, duration: e.target.value } : sp,
                          ),
                        )
                      }
                    />
                  </td>
                  <td className="spell-del-col">
                    <button
                      className="spell-del-btn"
                      onClick={() =>
                        setSpells((prev) => prev.filter((_, j) => j !== i))
                      }
                    >
                      ✕
                    </button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
          <button
            className="add-btn"
            style={{ marginTop: 10 }}
            onClick={() =>
              setSpells((prev) => [
                ...prev,
                { name: "", notes: "", mp: "", targets: "", duration: "" },
              ])
            }
          >
            + Add Spell / Arcana
          </button>
        </div>

        <div className="section">
          <div className="section-title">
            <span className="icon">⊕</span> Rituals
          </div>
          <div className="disciplines-row">
            {DISCIPLINES.map((d) => (
              <div
                key={d}
                className={`disc-item${disciplines[d] ? " checked" : ""}`}
                onClick={() => toggleDisc(d)}
              >
                <div className="disc-box">{disciplines[d] ? "✓" : ""}</div>
                <span>{d}</span>
              </div>
            ))}
          </div>
          <textarea
            value={fields.ritualsNotes}
            onChange={(e) => f("ritualsNotes", e.target.value)}
            placeholder="Record ritual details, components, and notes here…"
            style={{ minHeight: 120 }}
          />
        </div>
      </div>

      {/* ── PAGE 4: MANAGE ── */}
      <div
        className={`page${activeTab === "manage" ? " active" : ""}`}
        id="page-manage"
      >
        <div className="manage-grid">
          <div className="section">
            <div className="section-title">
              <span className="icon">◈</span> Local Save
            </div>
            <p className="manage-desc">
              Save your character sheet to your browser's local storage, or load
              a previously saved sheet.
            </p>
            <div className="manage-btn-row">
              <button className="btn-save btn-lg" onClick={saveSheet}>
                ✦ Save to Browser
              </button>
              <button className="btn-load btn-lg" onClick={loadSheet}>
                ↑ Load from Browser
              </button>
            </div>
          </div>

          <div className="section">
            <div className="section-title">
              <span className="icon">⊕</span> JSON File
            </div>
            <p className="manage-desc">
              Export your character to a JSON file for backup or sharing, or
              import from a previously exported file.
            </p>
            <div className="manage-btn-row">
              <button
                className="btn-save btn-export btn-lg"
                onClick={exportSheet}
              >
                ↓ Export JSON
              </button>
              <button
                className="btn-load btn-import btn-lg"
                onClick={() => {
                  if (!importFileRef.current) return;
                  importFileRef.current.value = "";
                  importFileRef.current.click();
                }}
              >
                ↑ Import JSON
              </button>
              <input
                ref={importFileRef}
                type="file"
                accept=".json,application/json"
                style={{ display: "none" }}
                onChange={handleImportFile}
              />
            </div>
          </div>

          <div className="section col-span-2">
            <div className="section-title">
              <span className="icon">⎘</span> Share via URL
            </div>
            <p className="manage-desc">
              Encode your character's current state into a shareable link.
              Anyone who opens the link will see your character — auto-save is
              disabled for viewers.
            </p>
            <div className="manage-btn-row">
              <button
                className="btn-save btn-export btn-lg"
                onClick={copyShareURL}
              >
                ⎘ Copy URL
              </button>
              <span className={`save-status${copyStatus ? " show" : ""}`}>
                Copied!
              </span>
            </div>
          </div>
        </div>
      </div>
    </>
  );
}
