/* app.jsx — root: router, account/session state, tweaks, language. */
const { useState: useApp, useEffect: useAppEffect } = React;

const ACCENTS = {
  "#6d4ee6": { a: "oklch(0.55 0.2 285)", a50: "oklch(0.96 0.03 285)", a100: "oklch(0.92 0.06 285)" },
  "#2a6fdb": { a: "oklch(0.55 0.16 250)", a50: "oklch(0.965 0.025 250)", a100: "oklch(0.92 0.06 250)" },
  "#1f8a5b": { a: "oklch(0.56 0.13 158)", a50: "oklch(0.965 0.03 158)", a100: "oklch(0.92 0.06 158)" },
  "#c2410c": { a: "oklch(0.58 0.16 45)", a50: "oklch(0.97 0.03 45)", a100: "oklch(0.93 0.07 45)" },
};
const FONTS = { "Space Grotesk": '"Space Grotesk"', "Outfit": '"Outfit"', "Sora": '"Sora"' };
const TWEAK_DEFAULTS = { accent: "#6d4ee6", headingFont: "Space Grotesk", density: "regular" };

function getGameType(s) {
  if (s.gameType) return s.gameType;
  if (s.game === "wuerfelpoker" || s.game === "wp") return "wp";
  if (s.game === "tischfussball" || s.game === "tfb") return "tfb";
  const cat = window.CATALOG?.byId(s.game);
  return cat?.type || s.game;
}

function useHashRoute() {
  const [hash, setHash] = useApp(window.location.hash || "#/");
  useAppEffect(() => {
    const on = () => setHash(window.location.hash || "#/");
    window.addEventListener("hashchange", on);
    window.addEventListener("tallynavigate", on);
    return () => { window.removeEventListener("hashchange", on); window.removeEventListener("tallynavigate", on); };
  }, []);
  return hash;
}
function nav(h) {
  if ((window.location.hash || "#/") === h) {
    // Same hash — force re-render via custom event
    window.dispatchEvent(new Event("tallynavigate"));
  } else {
    window.location.hash = h;
  }
}

function App() {
  const [t_, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const hash = useHashRoute();
  const [account, setAccount]   = useApp(null);
  const [sessions, setSessions] = useApp([]);
  const [authLoading, setAuthLoading] = useApp(() => !!window.sb);

  /* ── Supabase auth state listener ────────────────────────── */
  useAppEffect(() => {
    if (!window.sb) {
      // localStorage-only mode: init from Store directly
      setAccount(Store.getAccount());
      return;
    }
    const { data: { subscription } } = window.sb.auth.onAuthStateChange(
      async (event, session) => {
        if (session?.user) {
          window.__supabaseUser = session.user;
          await Store.loadProfile(session.user.id).catch(() => {});
          setAccount(Store.getAccount());
          if (window.__authRedirectRoute) {
            const target = window.__authRedirectRoute;
            window.__authRedirectRoute = null;
            nav(target);
          }
        } else {
          window.__supabaseUser    = null;
          window.__supabaseProfile = null;
          setAccount(null);
        }
        setAuthLoading(false);
      }
    );
    return () => subscription.unsubscribe();
  }, []);

  useAppEffect(() => {
    if (!account) return;
    Promise.resolve(Store.getSessions(account.id)).then(s => setSessions(Array.isArray(s) ? s : []));
  }, [account]);

  const [lang, setLangState] = useApp(() => I18N.getLang());
  I18N.setLang(lang);
  function setLang(l) { I18N.setLang(l); setLangState(l); }

  function reload() {
    if (!account) return;
    Promise.resolve(Store.getSessions(account.id)).then(s => setSessions(Array.isArray(s) ? s : []));
  }

  async function reloadAccount() {
    if (window.sb && window.__supabaseUser) {
      await Store.loadProfile(window.__supabaseUser.id).catch(() => {});
      setAccount(Store.getAccount());
    }
  }

  useAppEffect(() => {
    const root = document.documentElement;
    const ac = ACCENTS[t_.accent] || ACCENTS["#6d4ee6"];
    root.style.setProperty("--accent", ac.a);
    root.style.setProperty("--accent-50", ac.a50);
    root.style.setProperty("--accent-100", ac.a100);
    root.style.setProperty("--font-display", `${FONTS[t_.headingFont] || '"Space Grotesk"'}, ui-sans-serif, system-ui, sans-serif`);
    root.setAttribute("data-density", t_.density);
  }, [t_.accent, t_.headingFont, t_.density]);

  const parts = hash.replace(/^#\/?/, "").split("/");
  let screen;

  const sharedNav = { nav, lang, setLang };
  const signOutFn = () => { Store.signOut(); setAccount(null); setSessions([]); nav("#/"); };

  // Show spinner while Supabase restores the session
  if (authLoading) {
    return (
      <div className="app" style={{ display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh" }}>
        <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 16 }}>
          <div style={{ width: 36, height: 36, border: "3px solid var(--accent)", borderTopColor: "transparent",
            borderRadius: "50%", animation: "spin 0.7s linear infinite" }} />
          <span className="muted" style={{ fontSize: 14 }}>Laden…</span>
        </div>
      </div>
    );
  }

  // Billing success — accessible regardless of auth state
  if (parts[0] === "billing" && parts[1] === "success") {
    screen = <BillingSuccess {...sharedNav} onRefresh={reloadAccount} onGoHome={() => { reloadAccount().then(() => nav("#/")); }} />;

  } else if (parts[0].startsWith("reset-password") && account) {
    screen = <ResetPassword {...sharedNav} />;

  } else if (!account) {
    const onSignedIn = (acc) => { setAccount(acc); Promise.resolve(Store.getSessions(acc.id)).then(s => { setSessions(Array.isArray(s) ? s : []); nav("#/"); }); };
    screen = parts[0] === "signup"
      ? <SignUp {...sharedNav} onCreate={(d) => Store.createAccount(d)} onFinish={onSignedIn} />
      : parts[0] === "signin"
      ? <SignIn {...sharedNav} onSignIn={onSignedIn} />
      : parts[0] === "forgot"
      ? <ForgotPassword {...sharedNav} />
      : parts[0].startsWith("reset-password")
      ? <ForgotPassword {...sharedNav} />
      : <Landing {...sharedNav} />;

  } else if (!Store.isPro()) {
    // Logged in but no active subscription → billing gate
    screen = <BillingGate account={account} {...sharedNav} onSignOut={signOutFn} onRefresh={reloadAccount} />;

  } else if (parts[0] === "new" && parts[1]) {
    const gameId = parts[1];
    screen = <Setup account={account} {...sharedNav} gameId={gameId}
      onCreate={(cfg) => {
        const s = Store.createSession({ ...cfg, owner: account.id });
        setSessions(prev => [s, ...prev]);
        nav("#/game/" + s.id);
      }} />;

  } else if (parts[0] === "new") {
    nav("#/new/wp"); screen = null;

  } else if (parts[0] === "game" && parts[1]) {
    const sess = sessions.find((s) => s.id === parts[1]) || Store.getSessionLocal(parts[1]);
    if (!sess) {
      screen = <NotFound nav={nav} account={account} />;
    } else {
      const gt = getGameType(sess);
      const gameProps = { sess, account, nav, lang, setLang, onChange: (next) => {
        setSessions(prev => {
          const i = prev.findIndex(s => s.id === next.id);
          if (i >= 0) { const cp = prev.slice(); cp[i] = next; return cp; }
          return [next, ...prev];
        });
        Store.saveSession(next);
      } };
      if (gt === "wp")       screen = <Game {...gameProps} density={t_.density} />;
      else if (gt === "tfb")    screen = <TischfussballGame {...gameProps} />;
      else if (gt === "racket") screen = <RacketGame {...gameProps} />;
      else if (gt === "dart")   screen = <DartGame {...gameProps} />;
      else if (gt === "kniffel") screen = <KniffelGame {...gameProps} />;
      else if (gt === "bowling") screen = <BowlingGame {...gameProps} />;
      else                      screen = <RoundGame {...gameProps} />;
    }

  } else if (parts[0] === "stats") {
    screen = <Stats account={account} sessions={sessions} nav={nav} lang={lang} setLang={setLang}
      onSignOut={signOutFn} />;
  } else {
    screen = <Dashboard account={account} sessions={sessions} {...sharedNav}
      onSignOut={signOutFn}
      onDelete={(id) => {
        setSessions(prev => prev.filter(s => s.id !== id));
        Store.deleteSession(id);
      }} />;
  }

  return (
    <React.Fragment>
      {screen}
      <TweaksPanel>
        <TweakSection label={t("tw.theme")} />
        <TweakColor label={t("tw.accent")} value={t_.accent} options={["#6d4ee6", "#2a6fdb", "#1f8a5b", "#c2410c"]} onChange={(v) => setTweak("accent", v)} />
        <TweakRadio label={t("tw.font")} value={t_.headingFont} options={["Space Grotesk", "Outfit", "Sora"]} onChange={(v) => setTweak("headingFont", v)} />
        <TweakSection label={t("tw.sheet")} />
        <TweakRadio label={t("tw.density")} value={t_.density}
          options={[{ value: "compact", label: t("tw.compact") }, { value: "regular", label: t("tw.regular") }, { value: "comfy", label: t("tw.comfy") }]}
          onChange={(v) => setTweak("density", v)} />
      </TweaksPanel>
    </React.Fragment>
  );
}

/* ── Billing Success ────────────────────────────────────────── */
function BillingSuccess({ nav, lang, setLang, onRefresh, onGoHome }) {
  const [busy, setBusy] = useApp(false);
  async function handle() {
    setBusy(true);
    await (onRefresh ? onRefresh() : Promise.resolve());
    setBusy(false);
    nav("#/");
  }
  return (
    <div className="app" style={{ background: "var(--bg)" }}>
      <header className="topbar">
        <div className="wrap topbar-inner">
          <div className="brand" onClick={() => nav("#/")}><Logo /> Scoby</div>
          <span className="spacer"></span>
          {setLang && <LangToggle lang={lang} onChange={setLang} />}
        </div>
      </header>
      <main className="wrap" style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "60px 24px" }}>
        <div className="card" style={{ width: "100%", maxWidth: 460, padding: 40, textAlign: "center" }}>
          <div style={{ fontSize: 56, marginBottom: 16 }}>🎉</div>
          <h2 style={{ fontSize: 26, marginBottom: 10 }}>{t("billing.success.title")}</h2>
          <p className="muted" style={{ fontSize: 15, marginBottom: 28 }}>{t("billing.success.sub")}</p>
          <button className="btn primary lg block" disabled={busy} onClick={handle}>
            {busy ? "…" : t("billing.success.cta")}<Icon name="arrow" />
          </button>
        </div>
      </main>
    </div>
  );
}

/* ── Billing Gate (logged in but no active sub) ─────────────── */
function BillingGate({ account, nav, lang, setLang, onSignOut, onRefresh }) {
  const [busy, setBusy] = useApp("");
  const [err, setErr]   = useApp("");

  async function subscribe(interval) {
    setBusy(interval); setErr("");
    try { await Store.createCheckoutSession({ interval }); }
    catch (e) { setErr(e.message || "Fehler — bitte erneut versuchen."); }
    finally { setBusy(""); }
  }

  async function manage() {
    setBusy("portal"); setErr("");
    try { await Store.openCustomerPortal(); }
    catch (e) { setErr(e.message || "Fehler — bitte erneut versuchen."); }
    finally { setBusy(""); }
  }

  async function refresh() {
    setBusy("refresh");
    await (onRefresh ? onRefresh() : Promise.resolve());
    setBusy("");
  }

  return (
    <div className="app" style={{ background: "var(--bg)" }}>
      <header className="topbar">
        <div className="wrap topbar-inner">
          <div className="brand" onClick={() => nav("#/")}><Logo /> Scoby</div>
          <span className="spacer"></span>
          {setLang && <LangToggle lang={lang} onChange={setLang} />}
        </div>
      </header>
      <main className="wrap" style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "60px 24px" }}>
        <div className="card" style={{ width: "100%", maxWidth: 480, padding: 36 }}>
          <div style={{ fontSize: 44, marginBottom: 14, textAlign: "center" }}>🔒</div>
          <h2 style={{ fontSize: 22, marginBottom: 8, textAlign: "center" }}>{t("billing.gate.title")}</h2>
          <p className="muted" style={{ fontSize: 14.5, marginBottom: 28, textAlign: "center" }}>{t("billing.gate.sub")}</p>
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <button className="btn primary lg block" disabled={!!busy} onClick={() => subscribe("month")}>
              {busy === "month" ? "…" : t("billing.subscribe.monthly")}<Icon name="arrow" />
            </button>
            <button className="btn lg block" disabled={!!busy} onClick={() => subscribe("year")}>
              {busy === "year" ? "…" : t("billing.subscribe.yearly")}<Icon name="arrow" />
            </button>
            <div style={{ borderTop: "1px solid var(--line)", margin: "6px 0" }} />
            <button className="btn ghost block" disabled={!!busy} onClick={manage}>
              {busy === "portal" ? "…" : t("billing.manage")}
            </button>
            <button className="btn ghost block" disabled={!!busy} onClick={refresh}>
              {busy === "refresh" ? "…" : t("billing.refresh")}
            </button>
            <button className="btn ghost block" onClick={onSignOut}>{t("nav.signout")}</button>
          </div>
          {err && (
            <div style={{ color: "var(--strike)", fontSize: 14, padding: "10px 14px", marginTop: 10,
              background: "oklch(0.97 0.04 25)", borderRadius: "var(--r)", border: "1px solid oklch(0.88 0.07 25)" }}>
              {err}
            </div>
          )}
          <p className="faint" style={{ fontSize: 12, textAlign: "center", marginTop: 18 }}>
            {lang === "de" ? "Angemeldet als " : "Signed in as "}<strong>{account?.email}</strong>
          </p>
        </div>
      </main>
    </div>
  );
}

function NotFound({ nav, account }) {
  return (
    <div className="app">
      <main className="wrap" style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", minHeight: "70vh" }}>
        <div style={{ textAlign: "center" }}>
          <h2 style={{ fontSize: 26 }}>{t("nf.title")}</h2>
          <p className="muted" style={{ margin: "8px 0 18px" }}>{t("nf.sub")}</p>
          <button className="btn primary" onClick={() => nav("#/")}>{t("nf.home")}</button>
        </div>
      </main>
    </div>
  );
}

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