// WattWatch — main app + tweaks
//
// The Hero's `price` prop is driven by a live fetch against ComEd's public
// hourly-pricing endpoint (acceptance criterion #9). The Tweaks panel's
// "currentPrice" slider acts as a demo override — moving it switches the
// Hero to demo mode; the "Use live price" button switches back.

const TWEAK_DEFAULS = /*EDITMODE-BEGIN*/{
  "palette": ["#0a0118", "#00ffe1", "#ff2bd6", "#fff700", "#39ff14"],
  "glow": 0.5,
  "background": "aurora",
  "currentPrice": 2.4,
  "fontSet": "windycity",
  "logoMark": "boltcircle",
  "useLive": true
}/*EDITMODE-END*/;

// ─── Live ComEd current-hour-average fetch ──────────────────────────────
const COMED_HOURLY_URL = "https://hourlypricing.comed.com/api?type=currenthouraverage";
const COMED_POLL_INTERVAL_MS = 60_000;

function useLiveComEdPrice() {
  const [price, setPrice] = React.useState(null);   // last successful ¢/kWh
  const [stale, setStale] = React.useState(false);  // last poll attempt failed
  const [updatedAt, setUpdatedAt] = React.useState(null);
  const mountedRef = React.useRef(true);
  const abortRef = React.useRef(null);

  const fetchOnce = React.useCallback(async () => {
    // Cancel any still-in-flight request before starting a new one.
    if (abortRef.current) abortRef.current.abort();
    const ctrl = new AbortController();
    abortRef.current = ctrl;
    try {
      const res = await fetch(COMED_HOURLY_URL, {
        cache: "no-store",
        signal: ctrl.signal,
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data = await res.json();
      if (!Array.isArray(data) || data.length === 0) throw new Error("empty");
      const cents = Number.parseFloat(data[0].price);
      if (!Number.isFinite(cents)) throw new Error("invalid price");
      if (!mountedRef.current) return;
      setPrice(cents);
      setStale(false);
      setUpdatedAt(new Date());
    } catch (err) {
      // Aborts are intentional — don't treat them as a stale-feed event.
      if (err && err.name === "AbortError") return;
      console.warn("ComEd fetch failed:", err);
      if (!mountedRef.current) return;
      setStale(true);
    }
  }, []);

  React.useEffect(() => {
    mountedRef.current = true;
    fetchOnce();
    const id = setInterval(fetchOnce, COMED_POLL_INTERVAL_MS);
    return () => {
      mountedRef.current = false;
      clearInterval(id);
      if (abortRef.current) abortRef.current.abort();
    };
  }, [fetchOnce]);

  return { price, stale, updatedAt };
}

const PALETTES = [
  ["#0a0118", "#00ffe1", "#ff2bd6", "#fff700", "#39ff14"],   // classic Vegas
  ["#0d0d1f", "#39ff14", "#00e1ff", "#ff5b00", "#39ff14"],   // toxic
  ["#1a0a2e", "#3a86ff", "#ff006e", "#ffd400", "#39ff14"],   // synthwave
  ["#08161e", "#ff2e93", "#00e1ff", "#fff700", "#39ff14"],   // tokyo
];

const FONT_SETS = {
  windycity:  { display: '"Big Shoulders Display", sans-serif', pixel: '"Space Mono", monospace',      body: '"DM Sans", system-ui, sans-serif',       displayWeight: 800 },
  modern:     { display: '"Bricolage Grotesque", sans-serif',   pixel: '"JetBrains Mono", monospace', body: '"DM Sans", system-ui, sans-serif',       displayWeight: 800 },
  clean:      { display: '"Space Grotesk", sans-serif',         pixel: '"JetBrains Mono", monospace', body: '"Space Grotesk", system-ui, sans-serif', displayWeight: 700 },
  general:    { display: '"General Sans", sans-serif',          pixel: '"DM Mono", monospace',        body: '"DM Sans", system-ui, sans-serif',       displayWeight: 700 },
  signage:    { display: '"Bungee", sans-serif',                pixel: '"Silkscreen", monospace',     body: '"Space Grotesk", system-ui, sans-serif', displayWeight: 400 },
  synthwave:  { display: '"Audiowide", sans-serif',             pixel: '"Share Tech Mono", monospace',body: '"Space Grotesk", system-ui, sans-serif', displayWeight: 400 },
  arcade:     { display: '"Russo One", sans-serif',             pixel: '"Press Start 2P", monospace', body: '"Sora", system-ui, sans-serif',          displayWeight: 400 },
  futurenoir: { display: '"Orbitron", sans-serif',              pixel: '"JetBrains Mono", monospace', body: '"Outfit", system-ui, sans-serif',        displayWeight: 700 },
  vegas:      { display: '"Monoton", sans-serif',               pixel: '"VT323", monospace',          body: '"Space Grotesk", system-ui, sans-serif', displayWeight: 400 },
};

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULS);
  const live = useLiveComEdPrice();

  // useLive default true; moving the Tweaks slider (or hitting any of the
  // demo buttons) sets it to false so the demo value takes over.
  // "Use live price" snaps back. While useLive is true and we have a live
  // reading, the Hero shows the live value; otherwise it falls back to the
  // demo override.
  const showLive = t.useLive !== false;
  const heroPrice = showLive && live.price !== null ? live.price : t.currentPrice;
  const heroStale = showLive && live.stale;
  const heroUpdatedAt = showLive ? live.updatedAt : null;
  const heroIsOverride = !showLive;

  const setDemoPrice = (v) => {
    setTweak("currentPrice", v);
    setTweak("useLive", false);
  };

  // Apply palette + glow + background + fonts to root
  React.useEffect(() => {
    const r = document.documentElement.style;
    const [bg, cyan, magenta, yellow, neg] = t.palette;
    r.setProperty("--bg", bg);
    r.setProperty("--cyan", cyan);
    r.setProperty("--magenta", magenta);
    r.setProperty("--yellow", yellow);
    r.setProperty("--neg", neg);
    r.setProperty("--glow", String(t.glow));
    document.body.setAttribute("data-bg", t.background);

    const f = FONT_SETS[t.fontSet] || FONT_SETS.signage;
    r.setProperty("--font-display", f.display);
    r.setProperty("--font-pixel", f.pixel);
    r.setProperty("--font-body", f.body);
    r.setProperty("--font-display-weight", String(f.displayWeight));
  }, [t.palette, t.glow, t.background, t.fontSet]);

  return (
    <>
      <Nav mark={t.logoMark} />
      <Hero price={heroPrice}
            stale={heroStale}
            updatedAt={heroUpdatedAt}
            isOverride={heroIsOverride} />
      <Marquee />
      <HowItWorks />
      <AlertRules />
      <FAQ />
      <Footer mark={t.logoMark} />

      <TweaksPanel title="Tweaks">
        <TweakSection label="Brand">
          <TweakSelect label="Logo mark" value={t.logoMark}
            options={[
              { value: "boltcircle", label: "Bolt in circle" },
              { value: "meter",      label: "Meter / dial" },
              { value: "waveBolt",   label: "Signal bars + bolt" },
              { value: "powerBolt",  label: "Power ring + bolt" },
              { value: "bracketW",   label: "Bracketed W" },
              { value: "spark",      label: "4‑point spark" },
              { value: "none",       label: "Wordmark only" },
            ]}
            onChange={v => setTweak("logoMark", v)} />
        </TweakSection>

        <TweakSection label="Typography">
          <TweakSelect label="Font set" value={t.fontSet}
            options={[
              { value: "windycity",  label: "Windy City — Big Shoulders" },
              { value: "modern",     label: "Modern — Bricolage Grotesque" },
              { value: "clean",      label: "Clean — Space Grotesk" },
              { value: "general",    label: "Editorial — General Sans" },
              { value: "signage",    label: "Signage — Bungee" },
              { value: "synthwave",  label: "Synthwave — Audiowide" },
              { value: "arcade",     label: "Arcade — Russo One" },
              { value: "futurenoir", label: "Future‑noir — Orbitron" },
              { value: "vegas",      label: "Vegas — Monoton" },
            ]}
            onChange={v => setTweak("fontSet", v)} />
        </TweakSection>

        <TweakSection label="Vibe">
          <TweakSlider label="Element glow" value={t.glow} min={0} max={2} step={0.1}
            onChange={v => setTweak("glow", v)} />
          <TweakRadio label="Background" value={t.background}
            options={["aurora", "grid", "static", "dots", "scanlines"]}
            onChange={v => setTweak("background", v)} />
        </TweakSection>

        <TweakSection label="Palette">
          <TweakColor label="Theme" value={t.palette} options={PALETTES}
            onChange={v => setTweak("palette", v)} />
        </TweakSection>

        <TweakSection label="Live state">
          <TweakSlider label="Demo price ¢/kWh" value={t.currentPrice} min={-3} max={8} step={0.1} unit="¢"
            onChange={setDemoPrice} />
          <TweakButton label="Force negative" onClick={() => setDemoPrice(-1.4)} />
          <TweakButton label="Use live price" onClick={() => setTweak("useLive", true)} />
          <TweakButton secondary label="Reset demo to normal" onClick={() => setDemoPrice(2.4)} />
        </TweakSection>
      </TweaksPanel>
    </>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
