// PeptidePal app root

const { useState: useStateApp, useEffect: useEffectApp } = React;

const TIPS = [
  { tag: "HRV spike 📈", expression: "surprised", text: "Your HRV jumped 8ms in two weeks. That's a real signal — probably the Mots-C + sleep stack working. Worth a pat on the back. 🫧" },
  { tag: "Gentle nudge", expression: "thinking", text: "You've injected into your left abdomen 4 times in a row. Rotate to your right thigh tonight — your skin will thank us both." },
  { tag: "Cabinet check 🫙", expression: "reading", text: "Mots-C supply is at 22% — about 6 days left. Want me to queue your reorder for Wednesday?" },
  { tag: "Study drop", expression: "happy", text: "A new Retatrutide paper landed yesterday — 36-week data. I summarized it into a 4-min read in your Reader." },
];

// Mark a user as paid in Supabase profiles. Best-effort — failures don't
// block the UI; localStorage already says they're paid.
const markPaid = async (userId) => {
  if (!userId) return;
  const sb = window.getSupabase && window.getSupabase();
  if (!sb) return;
  try {
    await sb.from("profiles").update({ paid: true }).eq("id", userId);
  } catch (e) {
    console.warn("markPaid failed", e);
  }
};

const PeptideDrawer = ({ peptide, onClose }) => {
  if (!peptide) return null;
  return (
    <>
      <div className="drawer-scrim" onClick={onClose} />
      <aside className="drawer">
        <div className="drawer-header">
          <span className="caps" style={{ color: "var(--muted)" }}>Peptide file</span>
          <button className="btn" onClick={onClose}>Close ×</button>
        </div>
        <div className="drawer-content">
          <div
            className="drawer-hero"
            style={{
              position: "relative",
              aspectRatio: "10 / 7",
              background: "var(--cream-2)",
            }}
          >
            <div style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}>
              <PeptideArt id={peptide.id} />
            </div>
          </div>
          <span className="pill green">{peptide.tag}</span>
          <h2>{peptide.name}</h2>
          <p className="tagline">{peptide.tagline}</p>

          <div className="spec-grid">
            <div className="spec"><span className="k">Molecule</span>{peptide.molecule}</div>
            <div className="spec"><span className="k">Half-life</span>{peptide.halfLife}</div>
            <div className="spec"><span className="k">Route</span>{peptide.route}</div>
            <div className="spec"><span className="k">Timing</span>{peptide.timing}</div>
          </div>

          <h4>What it does</h4>
          <p>{peptide.about}</p>

          <h4>Reported benefits</h4>
          <ul>{peptide.benefits.map((b, i) => <li key={i}>{b}</li>)}</ul>

          <h4>Known side effects</h4>
          <ul>{peptide.sideEffects.map((b, i) => <li key={i}>{b}</li>)}</ul>

          <div className="splat-note">
            <SplatBadge size={44} expression="wink" />
            <div>
              <div className="from">Splat's note</div>
              <p>{peptide.splat}</p>
            </div>
          </div>

          <p style={{ marginTop: 18, fontSize: 11, fontFamily: "var(--mono)", color: "var(--muted)" }}>
            ※ Educational only — not medical advice. Consult your healthcare provider.
          </p>
        </div>
      </aside>
    </>
  );
};

const App = () => {
  const [paid, setPaid] = useStateApp(() => localStorage.getItem("pp_paid") === "1");
  // Land on home by default. EXCEPT: if we're returning from a Supabase
  // auth callback (signup confirmation, magic link, password recovery),
  // route the user straight to the dashboard.
  const [view, setView] = useStateApp(() => {
    if (typeof window === "undefined") return "landing";
    const hash = window.location.hash || "";
    const search = window.location.search || "";
    const isAuthCallback =
      hash.includes("access_token") ||
      hash.includes("type=signup") ||
      hash.includes("type=recovery") ||
      hash.includes("type=magiclink") ||
      hash.includes("confirmed") ||
      search.includes("type=signup") ||
      search.includes("code=") ||
      search.includes("dashboard=1");
    return isAuthCallback ? "dashboard" : "landing";
  });
  const [selected, setSelected] = useStateApp(null);
  const [articleId, setArticleId] = useStateApp(() => localStorage.getItem("pp_article") || null);
  const [chatOpen, setChatOpen] = useStateApp(false);
  const [tipIndex, setTipIndex] = useStateApp(0);
  const [fabTipIdx, setFabTipIdx] = useStateApp(0);
  const [fabTipVisible, setFabTipVisible] = useStateApp(true);

  // Supabase session — null when signed out, object when signed in.
  const [session, setSession] = useStateApp(null);

  // While true, render a loader instead of paywall/dashboard so we don't
  // flash the signup form while Supabase processes an auth-callback URL.
  const [authProcessing, setAuthProcessing] = useStateApp(() => {
    if (typeof window === "undefined") return false;
    const hash = window.location.hash || "";
    const search = window.location.search || "";
    return (
      hash.includes("access_token") ||
      hash.includes("type=") ||
      hash.includes("confirmed") ||
      search.includes("code=") ||
      search.includes("type=") ||
      search.includes("dashboard=1")
    );
  });

  useEffectApp(() => {
    const sb = window.getSupabase && window.getSupabase();
    if (!sb) {
      setAuthProcessing(false);
      return;
    }
    const cleanUrl = () => {
      try {
        if (typeof window === "undefined") return;
        const hadAuthParams =
          window.location.hash || window.location.search.includes("code=") ||
          window.location.search.includes("dashboard=") || window.location.search.includes("login=");
        if (hadAuthParams) {
          window.history.replaceState({}, document.title, window.location.pathname);
        }
      } catch (_) {}
    };
    sb.auth.getSession().then(({ data }) => {
      setSession(data.session);
      setAuthProcessing(false);
      cleanUrl();
    });
    const { data: sub } = sb.auth.onAuthStateChange((_e, s) => {
      setSession(s);
      setAuthProcessing(false);
      cleanUrl();
    });
    // Hard timeout so we never stick on the loader if something goes wrong
    const t = setTimeout(() => { setAuthProcessing(false); cleanUrl(); }, 4000);
    return () => { sub.subscription.unsubscribe(); clearTimeout(t); };
  }, []);

  // When the user signs in, sync `paid` between their profile row and
  // local state. If localStorage says they paid (e.g. they entered a
  // card during signup before confirming their email), push that to the
  // profile so it persists across devices.
  useEffectApp(() => {
    if (!session) return;
    const sb = window.getSupabase && window.getSupabase();
    if (!sb) return;
    sb.from("profiles")
      .select("paid, name")
      .eq("id", session.user.id)
      .single()
      .then(({ data }) => {
        if (!data) return;
        const localPaid = (() => {
          try { return localStorage.getItem("pp_paid") === "1"; } catch (_) { return false; }
        })();
        if (data.paid) {
          setPaid(true);
          try { localStorage.setItem("pp_paid", "1"); } catch (_) {}
        } else if (localPaid) {
          // Local card-entry happened pre-confirmation — sync up to the profile.
          setPaid(true);
          markPaid(session.user.id);
        }
        if (data.name) {
          try { localStorage.setItem("pp_name", data.name); } catch (_) {}
        }
      })
      .catch(() => {});
  }, [session]);

  useEffectApp(() => {
    if (articleId) localStorage.setItem("pp_article", articleId);
    else localStorage.removeItem("pp_article");
  }, [articleId]);

  const openArticle = (id) => {
    setArticleId(id);
    setView("article");
  };

  // auto-cycle the floating FAB tip
  useEffectApp(() => {
    if (view !== "dashboard") return;
    const t = setInterval(() => {
      setFabTipIdx(i => (i + 1) % TIPS.length);
    }, 9000);
    return () => clearInterval(t);
  }, [view]);

  const cycleTip = () => setTipIndex(i => (i + 1) % TIPS.length);

  const handleUnlockPaid = () => {
    setPaid(true);
    try { localStorage.setItem("pp_paid", "1"); } catch (_) {}
    if (session && session.user) {
      markPaid(session.user.id);
      // Also sync the name they typed on the paywall to their profile.
      const nm = (typeof localStorage !== "undefined" && localStorage.getItem("pp_name")) || "";
      if (nm) {
        const sb = window.getSupabase && window.getSupabase();
        if (sb) {
          sb.from("profiles").update({ name: nm }).eq("id", session.user.id).then(() => {}, () => {});
        }
      }
    }
  };

  const signOut = async () => {
    const sb = window.getSupabase && window.getSupabase();
    if (sb) {
      try { await sb.auth.signOut(); } catch (_) {}
    }
    setSession(null);
    setPaid(false);
    setChatOpen(false);    // close Splat's chat panel if open
    setFabTipVisible(false); // hide any lingering tip bubble
    try {
      localStorage.removeItem("pp_paid");
      localStorage.removeItem("pp_name");
    } catch (_) {}
    // Stay on the dashboard route — signed-out + unpaid = the merged
    // signup/signin page (Paywall in signup mode).
    setView("dashboard");
  };

  const isDashboard = view === "dashboard";
  // Single combined Paywall handles signup + payment when signed out, or
  // just payment when already signed in. We still need to gate the
  // dashboard on paid status. While Supabase is still processing an
  // auth-callback URL, suppress both views and show a loader.
  // Dashboard renders only when the user is BOTH signed in AND paid.
  // Otherwise we fall through to the Paywall — it handles signup, log-in,
  // and pay-only states. This also covers the edge case where pp_paid=1
  // is cached locally but there's no active session (cleared cookies,
  // logged out elsewhere, expired token) — the user lands on the login
  // tab and re-authenticates instead of staring at a blank page.
  const showDashboard = isDashboard && session && paid && !authProcessing;
  const showPaywall = isDashboard && !showDashboard && !authProcessing;
  const showAuthLoader = isDashboard && authProcessing;

  return (
    <div className="app-shell">
      <Nav view={view} onNav={setView} />
      {view === "landing" && (
        <Landing onNav={setView} onSelectPeptide={setSelected} />
      )}
      {view === "index" && (
        <IndexPage onSelect={setSelected} onNav={setView} />
      )}
      {view === "learn" && (
        <LearnPage onNav={setView} onOpenArticle={openArticle} />
      )}
      {view === "article" && (
        <ArticlePage
          article={ARTICLES.find(a => a.id === articleId)}
          onBack={() => setView("learn")}
          onNav={(v, id) => v === "article" && id ? openArticle(id) : setView(v)}
        />
      )}

      {showAuthLoader && (
        <div style={{
          minHeight: "calc(100vh - 80px)",
          display: "flex",
          flexDirection: "column",
          alignItems: "center",
          justifyContent: "center",
          gap: 28,
          background: "var(--cream)",
          color: "var(--ink)",
          textAlign: "center",
          padding: "60px 20px",
        }}>
          <span className="loader3 lg" aria-hidden="true"><b /><b /><b /></span>
          <h1 style={{
            fontFamily: "var(--serif)",
            fontWeight: 400,
            fontSize: 44,
            lineHeight: 1.05,
            letterSpacing: "-0.02em",
            margin: 0,
          }}>
            Confirming your email…
          </h1>
          <p style={{
            fontFamily: "var(--mono)",
            fontSize: 12,
            letterSpacing: "0.18em",
            textTransform: "uppercase",
            color: "var(--muted)",
            margin: 0,
          }}>
            Just a sec — Splat is letting you in.
          </p>
        </div>
      )}

      {showPaywall && (
        <Paywall
          onUnlock={handleUnlockPaid}
          onBack={() => setView("landing")}
          session={session}
        />
      )}

      {showDashboard && (
        <Dashboard
          onOpenChat={() => setChatOpen(true)}
          onSelectPeptide={setSelected}
          onNav={(v, id) => v === "article" && id ? openArticle(id) : setView(v)}
          tipIndex={tipIndex}
          tips={TIPS}
          cycleTip={cycleTip}
          session={session}
          onSignOut={signOut}
        />
      )}

      {selected && <PeptideDrawer peptide={selected} onClose={() => setSelected(null)} />}

      {showDashboard && !chatOpen && (
        <SplatFab
          onOpen={() => { setChatOpen(true); setFabTipVisible(false); }}
          tip={fabTipVisible ? TIPS[fabTipIdx].text : null}
          onDismissTip={() => setFabTipVisible(false)}
        />
      )}
      {showDashboard && chatOpen && <ChatWindow onClose={() => setChatOpen(false)} />}
    </div>
  );
};

const SitePasswordGate = ({ children }) => {
  const [unlocked, setUnlocked] = useStateApp(() => {
    try { return localStorage.getItem("pp_site_unlocked") === "1"; } catch (_) { return false; }
  });
  const [value, setValue] = useStateApp("");
  const [shake, setShake] = useStateApp(false);
  const [error, setError] = useStateApp(false);

  if (unlocked) return children;

  const submit = (e) => {
    if (e && e.preventDefault) e.preventDefault();
    if (value.trim().toLowerCase() === "bullish") {
      try { localStorage.setItem("pp_site_unlocked", "1"); } catch (_) {}
      setUnlocked(true);
    } else {
      setError(true);
      setShake(true);
      setTimeout(() => setShake(false), 480);
    }
  };

  return (
    <div style={{
      minHeight: "100vh",
      display: "flex",
      flexDirection: "column",
      alignItems: "center",
      justifyContent: "center",
      gap: 28,
      background: "var(--cream)",
      color: "var(--ink)",
      textAlign: "center",
      padding: "60px 20px",
      fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif",
    }}>
      <div style={{
        display: "inline-block",
        width: 72,
        height: 72,
        background: "#C7ECBF",
        border: "1.5px solid #0F1F2C",
        borderRadius: "50%",
        lineHeight: "72px",
        fontSize: 36,
        textAlign: "center",
      }}>🫧</div>
      <div>
        <div style={{
          fontFamily: "var(--mono)",
          fontSize: 11,
          letterSpacing: "0.22em",
          textTransform: "uppercase",
          color: "var(--muted)",
          marginBottom: 14,
        }}>PeptidePal · Members</div>
        <h1 style={{
          fontFamily: "var(--serif)",
          fontWeight: 400,
          fontSize: 52,
          lineHeight: 1.05,
          letterSpacing: "-0.02em",
          margin: 0,
        }}>Enter the password.</h1>
        <p style={{
          marginTop: 14,
          fontSize: 15,
          lineHeight: 1.6,
          color: "var(--muted)",
          maxWidth: 360,
          marginLeft: "auto",
          marginRight: "auto",
        }}>This site is private while we're in stealth. Drop the password to come in.</p>
      </div>
      <form onSubmit={submit} className={shake ? "pp-pw-shake" : ""} style={{
        display: "flex",
        flexDirection: "column",
        gap: 12,
        width: "100%",
        maxWidth: 320,
      }}>
        <input
          type="password"
          autoFocus
          value={value}
          onChange={(e) => { setValue(e.target.value); if (error) setError(false); }}
          placeholder="password"
          style={{
            width: "100%",
            padding: "14px 18px",
            border: `1.5px solid ${error ? "#C0392B" : "#0F1F2C"}`,
            borderRadius: 999,
            background: "#FFFDF6",
            fontSize: 16,
            fontFamily: "inherit",
            color: "var(--ink)",
            outline: "none",
            textAlign: "center",
            letterSpacing: "0.08em",
            boxSizing: "border-box",
          }}
        />
        <button type="submit" style={{
          padding: "14px 28px",
          background: "#5FBF5F",
          border: "1.5px solid #0F1F2C",
          borderRadius: 999,
          boxShadow: "0 2px 0 #0F1F2C",
          color: "#FFFFFF",
          fontSize: 15,
          fontWeight: 600,
          letterSpacing: "0.01em",
          cursor: "pointer",
          fontFamily: "inherit",
        }}>Unlock →</button>
        {error && (
          <div style={{
            fontFamily: "var(--mono)",
            fontSize: 11,
            letterSpacing: "0.12em",
            textTransform: "uppercase",
            color: "#C0392B",
            marginTop: 2,
          }}>Wrong password. Try again.</div>
        )}
      </form>
      <style>{`
        @keyframes pp-pw-shake {
          0%, 100% { transform: translateX(0); }
          20% { transform: translateX(-8px); }
          40% { transform: translateX(8px); }
          60% { transform: translateX(-5px); }
          80% { transform: translateX(5px); }
        }
        .pp-pw-shake { animation: pp-pw-shake 0.45s ease-in-out; }
      `}</style>
    </div>
  );
};

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

// Hide the initial full-page loader once React has mounted
requestAnimationFrame(() => {
  const pl = document.getElementById("page-loader");
  if (pl) {
    pl.classList.add("hidden");
    setTimeout(() => pl.remove(), 500);
  }
});
