import { useEffect, useMemo, useState } from "react";
import Overlay from "./Overlay";
import { copyText } from "../lib/clipboard";
import { usePrefs } from "./PrefsProvider";

/**
 * A floating live appearance tuner. Mounted at the app root so it's reachable on every page; it
 * writes the glass CSS custom properties (index.css `:root`) inline on the document element for
 * instant, build-free feedback, and persists to localStorage.
 *
 * Opt-in via Settings → Appearance (`theme.showTuner`, off by default) — a secondary "geek toy"
 * for dialling in the surface values. "Copy CSS" exports the values.
 */
const LS_KEY = "siftlode.devGlassTuner";

type Slider = {
  k: string;
  label: string;
  min: number;
  max: number;
  step: number;
  unit: string;
  def: number;
};

// def = the BAKED baseline in index.css (:root). "Current" preset + the export diff both key off it.
// def = the default rendered baseline = the "glass over image" (backdrop-on) tier from index.css.
const SLIDERS: Slider[] = [
  { k: "--glass-blur", label: "Blur radius", min: 0, max: 40, step: 1, unit: "px", def: 10 },
  { k: "--glass-saturate", label: "Saturation", min: 1, max: 2.5, step: 0.05, unit: "", def: 1.6 },
  {
    k: "--glass-dark-bright",
    label: "Dark brightness",
    min: 1,
    max: 2,
    step: 0.05,
    unit: "",
    def: 1.2,
  },
  {
    k: "--glass-surface-alpha",
    label: "Panel opacity",
    min: 40,
    max: 100,
    step: 1,
    unit: "%",
    def: 50,
  },
  {
    k: "--glass-card-alpha",
    label: "Card opacity",
    min: 40,
    max: 100,
    step: 1,
    unit: "%",
    def: 60,
  },
  {
    k: "--glass-menu-alpha",
    label: "Menu opacity",
    min: 40,
    max: 100,
    step: 1,
    unit: "%",
    def: 66,
  },
  {
    k: "--glass-border-alpha",
    label: "Border opacity",
    min: 20,
    max: 100,
    step: 1,
    unit: "%",
    def: 80,
  },
  { k: "--glass-edge", label: "Edge-light rim", min: 0, max: 60, step: 1, unit: "%", def: 25 },
  { k: "--glass-inset", label: "Top highlight", min: 0, max: 40, step: 1, unit: "%", def: 15 },
  { k: "--glass-scrim", label: "Under-text scrim", min: 0, max: 85, step: 1, unit: "%", def: 30 },
  { k: "--ambient", label: "Ambient intensity", min: 0, max: 2, step: 0.05, unit: "", def: 1.1 },
];

const PALETTE: { k: string; label: string }[] = [
  { k: "--bg", label: "Background" },
  { k: "--surface", label: "Surface" },
  { k: "--card", label: "Card" },
  { k: "--border", label: "Border" },
  { k: "--fg", label: "Text" },
  { k: "--muted", label: "Muted" },
  { k: "--accent", label: "Accent" },
];

// Each preset resets every slider to its default, then applies these overrides.
const PRESETS: { id: string; name: string; hint: string; over: Record<string, number> }[] = [
  { id: "current", name: "Glass/image", hint: "The default (over image)", over: {} },
  {
    id: "solid",
    name: "Solid (flat)",
    hint: "As shown w/ image off",
    over: {
      "--glass-blur": 20,
      "--glass-dark-bright": 1.25,
      "--glass-surface-alpha": 94,
      "--glass-card-alpha": 94,
      "--glass-menu-alpha": 98,
      "--glass-edge": 10,
      "--glass-inset": 16,
      "--glass-scrim": 14,
    },
  },
  {
    id: "readable",
    name: "Max readable",
    hint: "Solid + strong scrim",
    over: {
      "--glass-edge": 42,
      "--glass-scrim": 45,
      "--glass-surface-alpha": 96,
      "--glass-card-alpha": 96,
    },
  },
];

const ALL = SLIDERS;
const root = () => document.documentElement;
const readVar = (k: string) => getComputedStyle(root()).getPropertyValue(k).trim();

type Saved = {
  vars?: Record<string, number>;
  palette?: Record<string, string>;
  open?: boolean;
};

function loadSaved(): Saved {
  try {
    return JSON.parse(localStorage.getItem(LS_KEY) || "{}") as Saved;
  } catch {
    return {};
  }
}

export default function GlassTuner() {
  // Opt-in via Settings → Appearance (theme.showTuner, off by default); the flag lives in PrefsProvider.
  const enabled = usePrefs().theme.showTuner;
  const initial = useMemo(loadSaved, []);
  const [vars, setVars] = useState<Record<string, number>>(() => {
    const base: Record<string, number> = {};
    ALL.forEach((s) => (base[s.k] = s.def));
    return { ...base, ...(initial.vars || {}) };
  });
  const [palette, setPalette] = useState<Record<string, string>>(() => initial.palette || {});
  const [open, setOpen] = useState<boolean>(() => initial.open ?? true);
  const [tab, setTab] = useState<"glass" | "palette">("glass");
  const [copied, setCopied] = useState(false);
  const [themeTick, setThemeTick] = useState(0);
  // Seed the colour pickers from the live scheme for display (without forcing them onto <html>).
  const [swatches, setSwatches] = useState<Record<string, string>>({});

  // Apply saved var overrides (and any saved palette edits) once on mount.
  useEffect(() => {
    Object.entries(initial.vars || {}).forEach(([k, v]) => {
      const unit = ALL.find((s) => s.k === k)?.unit ?? "";
      root().style.setProperty(k, v + unit);
    });
    Object.entries(initial.palette || {}).forEach(([k, v]) => root().style.setProperty(k, v));
    const sw: Record<string, string> = {};
    PALETTE.forEach((p) => {
      const v = (initial.palette?.[p.k] || readVar(p.k)).trim();
      sw[p.k] = /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#000000";
    });
    setSwatches(sw);
  }, [initial]);

  function persist(next: Partial<Saved>) {
    const cur = loadSaved();
    localStorage.setItem(LS_KEY, JSON.stringify({ ...cur, ...next }));
  }

  function setVar(k: string, value: number) {
    const unit = ALL.find((s) => s.k === k)?.unit ?? "";
    root().style.setProperty(k, value + unit);
    setVars((prev) => {
      const next = { ...prev, [k]: value };
      persist({ vars: next });
      return next;
    });
  }

  function setColor(k: string, hex: string) {
    root().style.setProperty(k, hex);
    setSwatches((s) => ({ ...s, [k]: hex }));
    setPalette((prev) => {
      const next = { ...prev, [k]: hex };
      persist({ palette: next });
      return next;
    });
  }

  function applyPreset(over: Record<string, number>) {
    const next: Record<string, number> = { ...vars };
    SLIDERS.forEach((s) => {
      const v = over[s.k] ?? s.def;
      next[s.k] = v;
      root().style.setProperty(s.k, v + s.unit);
    });
    setVars(next);
    persist({ vars: next });
  }

  function reset() {
    [...ALL, ...PALETTE].forEach((s) => root().style.removeProperty(s.k));
    const base: Record<string, number> = {};
    ALL.forEach((s) => (base[s.k] = s.def));
    setVars(base);
    setPalette({});
    localStorage.removeItem(LS_KEY);
    const sw: Record<string, string> = {};
    PALETTE.forEach((p) => {
      const v = readVar(p.k);
      sw[p.k] = /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#000000";
    });
    setSwatches(sw);
  }

  // Re-read the LIVE, theme-driven glass values into the sliders (clearing inline overrides first),
  // so the panel reflects whatever theme is active — switch to light, hit this, and the opacity
  // sliders show the light-theme values (index.css `html[data-theme="light"]`) ready to tune.
  function syncToTheme() {
    ALL.forEach((s) => root().style.removeProperty(s.k));
    const next: Record<string, number> = { ...vars };
    ALL.forEach((s) => {
      const n = parseFloat(readVar(s.k));
      if (!Number.isNaN(n)) next[s.k] = n;
    });
    setVars(next);
    persist({ vars: {} });
    setThemeTick((t) => t + 1);
  }

  // Export the values that differ from the baseline, headered for the active theme's scope so a
  // light-theme tune pastes into the right block.
  const cssOut = useMemo(() => {
    void themeTick; // re-run when the active theme is re-synced
    const lines: string[] = [];
    ALL.forEach((s) => {
      if (vars[s.k] !== s.def) lines.push(`  ${s.k}: ${vars[s.k]}${s.unit};`);
    });
    PALETTE.forEach((p) => {
      if (palette[p.k]) lines.push(`  ${p.k}: ${palette[p.k]};`);
    });
    const scope = root().dataset.theme === "light" ? 'html[data-theme="light"]' : ":root";
    return lines.length ? `${scope} {\n${lines.join("\n")}\n}` : "/* all values at defaults */";
  }, [vars, palette, themeTick]);

  async function copy() {
    if (await copyText(cssOut)) {
      setCopied(true);
      setTimeout(() => setCopied(false), 1200);
    }
  }

  function persistOpen(v: boolean) {
    setOpen(v);
    persist({ open: v });
  }

  if (!enabled) return null;

  return (
    <Overlay>
      <div
        className="fixed right-0 top-24 z-tuner flex items-start"
        style={{ pointerEvents: "none" }}
      >
        {!open && (
          <button
            onClick={() => persistOpen(true)}
            style={{ pointerEvents: "auto" }}
            className="glass-menu glass-hover rounded-l-xl rounded-r-none px-2 py-3 text-xs font-semibold tracking-wide text-fg"
            title="Open the glass tuner"
          >
            <span style={{ writingMode: "vertical-rl" }}>◐ GLASS</span>
          </button>
        )}
        {open && (
          <div
            style={{ pointerEvents: "auto" }}
            className="glass-menu rounded-l-2xl rounded-r-none w-[300px] max-h-[82vh] flex flex-col overflow-hidden shadow-2xl"
          >
            <div className="flex items-center gap-2 px-3 py-2 border-b border-border/60">
              <span className="text-sm font-semibold text-fg">◐ Glass Tuner</span>
              <span className="text-[10px] uppercase tracking-wider text-muted">lab</span>
              <button
                onClick={() => persistOpen(false)}
                className="ml-auto text-muted hover:text-fg text-lg leading-none px-1"
                title="Collapse"
              >
                ×
              </button>
            </div>

            <div className="overflow-y-auto px-3 py-3 flex flex-col gap-4 text-sm">
              {/* Treatment presets */}
              <div className="flex flex-col gap-1.5">
                <div className="text-[10px] uppercase tracking-wider text-muted font-semibold">
                  Dark treatment
                </div>
                <div className="grid grid-cols-2 gap-1.5">
                  {PRESETS.map((p) => (
                    <button
                      key={p.id}
                      onClick={() => applyPreset(p.over)}
                      title={p.hint}
                      className="glass-card glass-hover rounded-lg px-2 py-1.5 text-left text-xs text-fg"
                    >
                      <b className="block font-semibold">{p.name}</b>
                      <span className="text-muted text-[10px]">{p.hint}</span>
                    </button>
                  ))}
                </div>
              </div>

              {/* Tab switch */}
              <div className="flex gap-1.5">
                {(["glass", "palette"] as const).map((t) => (
                  <button
                    key={t}
                    onClick={() => setTab(t)}
                    className={`flex-1 rounded-lg px-2 py-1 text-xs capitalize ${
                      tab === t
                        ? "bg-accent text-[color:var(--accent-fg)] font-semibold"
                        : "glass-card text-muted"
                    }`}
                  >
                    {t}
                  </button>
                ))}
              </div>

              {tab === "glass" && (
                <div className="flex flex-col gap-2.5">
                  {SLIDERS.map((s) => (
                    <Ctl
                      key={s.k}
                      s={s}
                      value={vars[s.k] ?? s.def}
                      onChange={(v) => setVar(s.k, v)}
                    />
                  ))}
                </div>
              )}

              {tab === "palette" && (
                <div className="flex flex-col gap-2">
                  <p className="text-[10px] text-muted leading-relaxed">
                    Overrides the live scheme (inline on &lt;html&gt;). Reset clears these back to
                    the scheme.
                  </p>
                  {PALETTE.map((p) => (
                    <label key={p.k} className="flex items-center gap-2 text-xs text-fg">
                      <input
                        type="color"
                        value={swatches[p.k] || "#000000"}
                        onChange={(e) => setColor(p.k, e.target.value)}
                        className="w-8 h-6 rounded border border-border bg-transparent cursor-pointer"
                      />
                      <span className="flex-1">{p.label}</span>
                      <code className="text-[10px] text-muted">{swatches[p.k]}</code>
                    </label>
                  ))}
                </div>
              )}

              {/* Export */}
              <div className="flex flex-col gap-1.5 pt-1 border-t border-border/50">
                <div className="text-[10px] uppercase tracking-wider text-muted font-semibold">
                  Export → bake in
                </div>
                <button
                  onClick={syncToTheme}
                  title="Load the values of the theme you're currently viewing (light or dark) into the sliders"
                  className="glass-card glass-hover rounded-lg px-2 py-1.5 text-xs text-fg text-left"
                >
                  ⟳ Sync to current theme
                </button>
                <div className="flex gap-1.5">
                  <button
                    onClick={copy}
                    className="flex-1 rounded-lg px-2 py-1.5 text-xs font-semibold bg-accent text-[color:var(--accent-fg)]"
                  >
                    {copied ? "Copied ✓" : "Copy CSS"}
                  </button>
                  <button
                    onClick={reset}
                    className="glass-card glass-hover rounded-lg px-3 py-1.5 text-xs text-fg"
                  >
                    Reset
                  </button>
                </div>
                <pre className="text-[9.5px] leading-relaxed bg-[color:var(--bg)] border border-border/60 rounded-lg p-2 overflow-x-auto text-muted whitespace-pre">
                  {cssOut}
                </pre>
              </div>
            </div>
          </div>
        )}
      </div>
    </Overlay>
  );
}

function Ctl({ s, value, onChange }: { s: Slider; value: number; onChange: (v: number) => void }) {
  return (
    <div className="grid grid-cols-[1fr_auto] items-center gap-x-2 gap-y-1">
      <label className="text-xs text-fg">{s.label}</label>
      <input
        type="number"
        min={s.min}
        max={s.max}
        step={s.step}
        value={value}
        onChange={(e) => onChange(Number(e.target.value))}
        className="w-16 text-right text-[11px] tabular-nums rounded bg-card border border-border px-1.5 py-0.5 text-accent"
      />
      <input
        type="range"
        min={s.min}
        max={s.max}
        step={s.step}
        value={value}
        onChange={(e) => onChange(Number(e.target.value))}
        className="col-span-2 w-full accent-accent"
      />
    </div>
  );
}
