// app.jsx — router + i18n + tweaks + config + mount
const { useState: useSA, useEffect: useEA, useRef: useRA } = React;

const THEME_ACCENT = { broadcast: "#ff2e3f", telemetry: "#18e3ff", carbon: "#c8ff2e" };
const SUPPORTED = ["en", "ro", "de", "fr", "es"];

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "broadcast",
  "accent": "#ff2e3f",
  "headFont": "Saira Condensed",
  "ticker": true,
  "tower": true,
  "discordUrl": "https://discord.gg/your-invite",
  "coffeeUrl": "https://www.patreon.com/your-handle",
  "showCoffee": true,
  "discordWebhook": ""
}/*EDITMODE-END*/;

function loadAnnouncement() {
  try { const s = localStorage.getItem('igs-ann'); return s ? JSON.parse(s) : null; } catch(e) { return null; }
}

function loadTweaks() {
  try {
    const s = localStorage.getItem("igs-tweaks");
    return s ? { ...TWEAK_DEFAULTS, ...JSON.parse(s) } : TWEAK_DEFAULTS;
  } catch (e) { return TWEAK_DEFAULTS; }
}

function inkFor(hex) {
  const h = hex.replace("#", "");
  const r = parseInt(h.slice(0, 2), 16), g = parseInt(h.slice(2, 4), 16), b = parseInt(h.slice(4, 6), 16);
  return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.6 ? "#10130a" : "#ffffff";
}
function detectLang() {
  try { const s = localStorage.getItem("igs-lang"); if (s && SUPPORTED.includes(s)) return s; } catch (e) {}
  const n = (navigator.language || "en").slice(0, 2).toLowerCase();
  return SUPPORTED.includes(n) ? n : "en";
}

function AnnouncementBanner({ announcement, onDismiss }) {
  const { t } = useT();
  return (
    <div style={{ position: "fixed", top: 0, left: 0, right: 0, zIndex: 2000, background: "var(--accent)", color: "var(--accent-ink)", padding: "10px 20px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, fontFamily: "var(--mono)", fontSize: "13px", boxShadow: "0 2px 12px rgba(0,0,0,.4)" }}>
      <span>🏁 <b>RND {String(announcement.round).padStart(2,"0")} · {announcement.name.toUpperCase()}</b> — {t("banner.start")} {announcement.time} {announcement.tz} — {t("banner.discord")}</span>
      <button onClick={onDismiss} style={{ background: "none", border: "none", cursor: "pointer", fontSize: "20px", color: "inherit", lineHeight: 1, padding: "0 4px", flexShrink: 0 }}>×</button>
    </div>
  );
}

function App() {
  const [tw, _setTweak] = useTweaks(loadTweaks());
  const setTweak = (keyOrEdits, val) => {
    _setTweak(keyOrEdits, val);
    const edits = typeof keyOrEdits === 'object' && keyOrEdits !== null ? keyOrEdits : { [keyOrEdits]: val };
    try { const c = JSON.parse(localStorage.getItem("igs-tweaks") || "{}"); localStorage.setItem("igs-tweaks", JSON.stringify({ ...c, ...edits })); } catch (e) {}
    if (!window.sbClient) return;
    const VISUAL_KEYS = ['theme', 'accent', 'headFont', 'ticker', 'tower'];
    const LINK_KEYS = ['discordUrl', 'discordWebhook', 'coffeeUrl', 'showCoffee'];
    const visualEdits = Object.fromEntries(Object.entries(edits).filter(([k]) => VISUAL_KEYS.includes(k)));
    const linkEdits = Object.fromEntries(Object.entries(edits).filter(([k]) => LINK_KEYS.includes(k)));
    if (Object.keys(visualEdits).length) {
      window.sbClient.from('site_config').select('value').eq('id', 'tweaks').maybeSingle().then(({ data }) => {
        const current = (data && data.value) || {};
        window.sbClient.from('site_config').upsert({ id: 'tweaks', value: { ...current, ...visualEdits }, updated_at: new Date().toISOString() }, { onConflict: 'id' });
      });
    }
    if (Object.keys(linkEdits).length) {
      window.sbClient.from('site_config').select('value').eq('id', 'links').maybeSingle().then(({ data }) => {
        const current = (data && data.value) || {};
        window.sbClient.from('site_config').upsert({ id: 'links', value: { ...current, ...linkEdits }, updated_at: new Date().toISOString() }, { onConflict: 'id' });
      });
    }
  };
  const [lang, setLangState] = useSA(detectLang);
  const [page, setPage] = useSA(() => (location.hash.replace("#", "") || "home"));
  const [raceSel, setRaceSel] = useSA(null);
  const [showAdmin, setShowAdmin] = useSA(false);
  const [races, setRaces] = useSA(loadRaces);
  const [announcement, setAnnouncement] = useSA(loadAnnouncement);
  const dismissAnn = () => { localStorage.removeItem('igs-ann'); setAnnouncement(null); };

  const setLang = (l) => { setLangState(l); try { localStorage.setItem("igs-lang", l); } catch (e) {} };

  const go = (p, raceId) => {
    setRaceSel(raceId || null);
    setPage(p);
    location.hash = p;
    window.scrollTo({ top: 0, behavior: "auto" });
  };

  useEA(() => {
    const onHash = () => {
      const p = location.hash.replace("#", "");
      if (["home", "races", "clasament", "about", "signup", "admin"].includes(p)) {
        setPage(p);
        setRaces(loadRaces());
      }
    };
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);

  // detect admin keystroke (Ctrl+Shift+A)
  useEA(() => {
    const onKey = (e) => {
      if (e.ctrlKey && e.shiftKey && e.key === "A") { e.preventDefault(); go("admin"); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  useEA(() => { document.documentElement.lang = lang; }, [lang]);

  // theme + accent
  useEA(() => {
    document.documentElement.setAttribute("data-theme", tw.theme);
    const a = tw.accent, root = document.documentElement.style;
    root.setProperty("--accent", a);
    root.setProperty("--accent-2", `color-mix(in srgb, ${a} 72%, white)`);
    root.setProperty("--accent-ink", inkFor(a));
    root.setProperty("--display", `'${tw.headFont}', 'Arial Narrow', sans-serif`);
  }, [tw.theme, tw.accent, tw.headFont]);

  useEA(() => { document.body.classList.toggle("has-ticker", !!tw.ticker); }, [tw.ticker]);
  useEA(() => { window.scrollTo({ top: 0, behavior: "auto" }); }, [page]);

  // Load cloud config (races + links) — syncs across all devices
  useEA(() => {
    async function loadCloudConfig() {
      if (!window.sbClient) return;
      try {
        const { data } = await window.sbClient.from('site_config').select('id, value');
        if (!data || !data.length) return;
        const cloud = {};
        data.forEach(row => { cloud[row.id] = row.value; });

        if (Array.isArray(cloud.races) && cloud.races.length > 0) {
          // Cloud is authoritative — replace entire list, keep gridFree/status for syncCounts
          setRaces(prev => {
            const prevById = {};
            prev.forEach(r => { prevById[r.id] = r; });
            return cloud.races.map(r => {
              const { gridFree, status, ...cloudRest } = r;
              const prev = prevById[r.id] || {};
              return { ...prev, ...cloudRest };
            });
          });
          try { localStorage.setItem('igs-races', JSON.stringify(cloud.races)); } catch(e) {}
        }

        if (cloud.links) {
          const linkKeys = ['discordUrl', 'discordWebhook', 'coffeeUrl', 'showCoffee'];
          const updates = {};
          linkKeys.forEach(k => { if (cloud.links[k] !== undefined) updates[k] = cloud.links[k]; });
          if (Object.keys(updates).length) {
            _setTweak(updates);
            try { const c = JSON.parse(localStorage.getItem('igs-tweaks') || '{}'); localStorage.setItem('igs-tweaks', JSON.stringify({ ...c, ...updates })); } catch(e) {}
          }
        }

        if (cloud.images && typeof cloud.images === 'object') {
          try {
            localStorage.setItem('igs-image-slots', JSON.stringify(cloud.images));
            if (window.__imageSlotLoad) window.__imageSlotLoad(cloud.images);
          } catch(e) {}
        }

        if (cloud.tweaks && typeof cloud.tweaks === 'object') {
          const VISUAL_KEYS = ['theme', 'accent', 'headFont', 'ticker', 'tower'];
          const updates = {};
          VISUAL_KEYS.forEach(k => { if (cloud.tweaks[k] !== undefined) updates[k] = cloud.tweaks[k]; });
          if (Object.keys(updates).length) {
            _setTweak(updates);
            try { const c = JSON.parse(localStorage.getItem('igs-tweaks') || '{}'); localStorage.setItem('igs-tweaks', JSON.stringify({ ...c, ...updates })); } catch(e) {}
          }
        }

        if (cloud.text && typeof cloud.text === 'object') {
          try {
            localStorage.setItem('igs-text', JSON.stringify(cloud.text));
          } catch(e) {}
        }

        if (Array.isArray(cloud.cars) && cloud.cars.length) {
          window.CARS = cloud.cars;
        }

        if (cloud.season && typeof cloud.season === 'object') {
          if (cloud.season.year)        window.SEASON.year   = cloud.season.year;
          if (cloud.season.rounds)      window.SEASON.rounds = cloud.season.rounds;
          if (cloud.season.practiceMin != null) window.FORMAT.practiceMin = cloud.season.practiceMin;
          if (cloud.season.qualiLaps   != null) window.FORMAT.qualiLaps   = cloud.season.qualiLaps;
        }
      } catch(e) {}
    }
    loadCloudConfig();
    window._loadCloudConfig = loadCloudConfig;
    const iv = setInterval(loadCloudConfig, 30000);
    return () => { clearInterval(iv); delete window._loadCloudConfig; };
  }, []);

  // Sync real signup counts from Supabase
  useEA(() => {
    async function syncCounts() {
      if (!window.sbClient) return;
      try {
        const { data } = await window.sbClient.from('signups').select('race_id');
        if (!data) return;
        const counts = {};
        data.forEach(s => { counts[s.race_id] = (counts[s.race_id] || 0) + 1; });
        setRaces(prev => prev.map(r => {
          const signed = counts[r.id] || 0;
          const gFree = Math.max(0, r.gridTotal - signed);
          const autoStatus = r.status === 'done' ? 'done' : (gFree === 0 ? 'full' : gFree <= 8 ? 'filling' : 'open');
          return { ...r, gridFree: gFree, status: autoStatus };
        }));
      } catch(e) {}
    }
    window._syncCounts = syncCounts;
    syncCounts();
    const iv = setInterval(syncCounts, 30000);
    return () => { clearInterval(iv); delete window._syncCounts; };
  }, []);

  // Run scheduler check every minute
  useEA(() => {
    if (!window.checkAndFire) return;
    window.checkAndFire();
    const iv = setInterval(window.checkAndFire, 60000);
    return () => clearInterval(iv);
  }, []);

  // re-render when races change (from admin panel)
  const spa = races.find(r => r.featured && r.status !== "done") || races.find(r => r.status !== "done") || races[0];

  let body;
  if (page === "clasament") body = <StandingsPage races={races} />;
  else if (page === "races") body = <RacesPage go={go} races={races} />;
  else if (page === "about") body = <AboutPage go={go} races={races} />;
  else if (page === "signup") body = <SignupPage go={go} preselect={raceSel} races={races} />;
  else if (page === "admin") body = <AdminPanel onClose={() => go("home")} tw={tw} setTweak={setTweak} asPage />;
  else body = <HomePage go={go} showTower={tw.tower} races={races} spa={spa} />;

  const cfg = { discordUrl: tw.discordUrl || "#", coffeeUrl: tw.coffeeUrl || "#", showCoffee: !!tw.showCoffee, discordWebhook: tw.discordWebhook || "" };

  return (
    <LangContext.Provider value={{ lang, setLang }}>
      <ConfigContext.Provider value={cfg}>
        <div className="bg-grain" />
        {announcement && <AnnouncementBanner announcement={announcement} onDismiss={dismissAnn} />}
        <div className="app" style={announcement ? { paddingTop: 42 } : {}}>
          <Header page={page} go={go} />
          {body}
          <Footer go={go} />
        </div>
        {tw.ticker && <Ticker races={races} key={lang} />}

        <TweaksPanel>
          <TweakSection label="Visual direction" />
          <TweakRadio label="Theme" value={tw.theme} options={["broadcast", "telemetry", "carbon"]}
            onChange={(v) => setTweak({ theme: v, accent: THEME_ACCENT[v] })} />
          <TweakColor label="Accent" value={tw.accent}
            options={["#ff2e3f", "#18e3ff", "#c8ff2e", "#ff7a00", "#ffffff"]}
            onChange={(v) => setTweak("accent", v)} />
          <TweakSelect label="Headline font" value={tw.headFont}
            options={["Saira Condensed", "Oswald", "Archivo", "Saira"]}
            onChange={(v) => setTweak("headFont", v)} />

          <TweakSection label="Broadcast overlay" />
          <TweakToggle label="Live ticker (bottom)" value={tw.ticker} onChange={(v) => setTweak("ticker", v)} />
          <TweakToggle label="Timing tower (hero)" value={tw.tower} onChange={(v) => setTweak("tower", v)} />

          <TweakSection label="Links" />
          <TweakText label="Discord invite URL" value={tw.discordUrl} onChange={(v) => setTweak("discordUrl", v)} />
          <TweakToggle label="Show Patreon" value={tw.showCoffee} onChange={(v) => setTweak("showCoffee", v)} />
          <TweakText label="Patreon URL" value={tw.coffeeUrl} onChange={(v) => setTweak("coffeeUrl", v)} />

          <TweakSection label="Admin" />
          <TweakButton onClick={() => setShowAdmin(true)}>Open Admin Panel</TweakButton>
          <p style={{ margin: "8px 0 0", fontSize: "11px", color: "var(--muted)", fontFamily: "var(--mono)" }}>Or press Ctrl+Shift+A</p>
        </TweaksPanel>

        {showAdmin && <AdminPanel onClose={() => setShowAdmin(false)} tw={tw} setTweak={setTweak} />}
      </ConfigContext.Provider>
    </LangContext.Provider>
  );
}

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