/* Spoonity auth for the Pass Studio (Apple + Google). Brokered through the backend
   (POST /auth/login, /auth/select) which runs Spoonity's two-stage identity->vendor login and
   sets a short-lived HttpOnly session COOKIE (Secure, SameSite=Strict) on wallet-api.spoonity.com.
   Because the studio (studio.spoonity.com) and the API are the SAME site, the cookie rides
   requests automatically (credentials:'include') but is never sent from a third-party site — so
   the token is unreadable to JS (XSS-safe) with no CSRF exposure. We keep only NON-sensitive
   display info (vendor/exp) in sessionStorage for the UI. Shared by both studios. */

(function () {
  const ENDPOINT_KEY = "spoonity_design_endpoint";
  const DEFAULT_ENDPOINT = "https://wallet-api.spoonity.com";
  const META_KEY = "spoonity_session_meta"; // display info only — NEVER the token

  function b64uDecode(s) {
    s = String(s).replace(/-/g, "+").replace(/_/g, "/");
    while (s.length % 4) s += "=";
    return atob(s);
  }

  window.SpoonityAuth = {
    getEndpoint() {
      try { return (localStorage.getItem(ENDPOINT_KEY) || DEFAULT_ENDPOINT).replace(/\/+$/, ""); }
      catch (e) { return DEFAULT_ENDPOINT; }
    },
    getSession() {
      try {
        const raw = sessionStorage.getItem(META_KEY);
        if (!raw) return null;
        const s = JSON.parse(raw);
        if (!s.exp || Date.now() > s.exp) { sessionStorage.removeItem(META_KEY); return null; }
        return s;
      } catch (e) { return null; }
    },
    // The session token is an HttpOnly cookie now — deliberately not readable by JS.
    getToken() { return null; },
    activeVendorId() { const s = this.getSession(); return s && s.vendor ? s.vendor.id : null; },
    _expOf(token) {
      try { return JSON.parse(b64uDecode(String(token).split(".")[0])).exp || 0; } catch (e) { return 0; }
    },
    store(resp) {
      // Persist ONLY non-sensitive display info; the token itself stays in the HttpOnly cookie.
      const session = { exp: this._expOf(resp.sessionToken),
                        vendor: resp.vendor || null, vendors: resp.vendors || [] };
      sessionStorage.setItem(META_KEY, JSON.stringify(session));
      return session;
    },
    logout() {
      // Clear local UI state first so the app flips to logged-out immediately, then tell the
      // backend to expire the cookie (fire-and-forget).
      try { sessionStorage.removeItem(META_KEY); } catch (e) {}
      try { fetch(this.getEndpoint() + "/auth/logout", { method: "POST", credentials: "include" }); } catch (e) {}
    },
    async _post(path, body) {
      const res = await fetch(this.getEndpoint() + path, {
        method: "POST", credentials: "include",
        headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(data.error || ("Request failed (" + res.status + ")"));
      return data;
    },
    // -> { sessionToken, vendor, vendors } (single/auto) OR { vendors } (multi, must pick)
    login(email, password) { return this._post("/auth/login", { email, password }); },
    selectVendor(email, password, identity_id) { return this._post("/auth/select", { email, password, identity_id }); },
    // In-session switch to another authorized vendor (no password). Re-scopes the session (cookie)
    // and keeps the cached vendor list (names) so the badge/picker stay populated.
    async switchVendor(vendorId) {
      const res = await fetch(this.getEndpoint() + "/auth/switch", {
        method: "POST", credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ vendorId }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(data.error || ("Switch failed (" + res.status + ")"));
      const cur = this.getSession() || {};
      const vendor = (cur.vendors || []).find((v) => v.id === String(data.activeVendorId)) || { id: String(data.activeVendorId), name: "" };
      this.store({ sessionToken: data.sessionToken, vendor, vendors: cur.vendors || [] });
      return vendor;
    },
  };

  // ---- UI ----
  const ACCENT = "#3d9bfe";
  const BLUE = "linear-gradient(180deg,#4aa6ff,#2f8bef)";
  const screen = { position: "fixed", inset: 0, zIndex: 5000, display: "flex", alignItems: "center",
    justifyContent: "center", padding: 16,
    background: "radial-gradient(1200px 600px at 50% -10%, #16233b 0%, #0b1220 55%, #080d18 100%)",
    fontFamily: "var(--font-sans, system-ui, -apple-system, sans-serif)" };
  const card = { width: 420, maxWidth: "92vw", background: "#111a2b",
    border: "1px solid rgba(255,255,255,0.07)", borderRadius: 16,
    boxShadow: "0 24px 70px rgba(0,0,0,.55)", padding: "34px 30px", boxSizing: "border-box" };
  const input = { width: "100%", padding: "12px 14px", borderRadius: 10, border: "1px solid #d7deea",
    background: "#eef2f8", color: "#16233b", fontSize: 14, boxSizing: "border-box", marginTop: 6, outline: "none" };
  const btn = (bg) => ({ width: "100%", padding: "13px 0", borderRadius: 10, border: "none", color: "#fff",
    background: bg, fontSize: 15, fontWeight: 700, cursor: "pointer", marginTop: 18,
    display: "flex", alignItems: "center", justifyContent: "center", gap: 8 });
  const label = { fontSize: 11, fontWeight: 700, letterSpacing: 1, textTransform: "uppercase",
    color: "#8291ab", display: "block", marginTop: 16 };

  function LoginScreen({ onAuthed }) {
    const [email, setEmail] = React.useState("");
    const [password, setPassword] = React.useState("");
    const [vendors, setVendors] = React.useState(null); // multi-vendor picker
    const [busy, setBusy] = React.useState(false);
    const [error, setError] = React.useState("");
    const [filter, setFilter] = React.useState("");
    const filterRef = React.useRef(null);
    // Put the cursor in the filter box as soon as the vendor picker appears.
    React.useEffect(() => { if (vendors) { setFilter(""); if (filterRef.current) filterRef.current.focus(); } }, [vendors]);

    const finish = (data) => { window.SpoonityAuth.store(data); onAuthed(); };

    const submitCreds = async (e) => {
      e.preventDefault();
      if (!email.trim() || !password) { setError("Enter your email and password."); return; }
      setBusy(true); setError("");
      try {
        const data = await window.SpoonityAuth.login(email.trim(), password);
        if (data.sessionToken) finish(data);          // single vendor (auto-selected)
        else if (data.vendors && data.vendors.length) setVendors(data.vendors); // multi -> pick
        else setError("This account has no vendors.");
      } catch (err) { setError(err.message || "Sign-in failed."); }
      finally { setBusy(false); }
    };

    const pick = async (id) => {
      setBusy(true); setError("");
      try { finish(await window.SpoonityAuth.selectVendor(email.trim(), password, id)); }
      catch (err) { setError(err.message || "Selection failed."); setBusy(false); }
    };

    return (
      <div style={screen}>
        <div style={card}>
          <div style={{ textAlign: "center" }}>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 9 }}>
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={ACCENT} strokeWidth="2.2"
                strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <rect x="2" y="5" width="20" height="14" rx="3" />
                <path d="M2 10h20" />
              </svg>
              <span style={{ fontSize: 23, fontWeight: 800, color: ACCENT }}>Pass Studio</span>
            </div>
            <div style={{ color: "#6f7d95", fontSize: 11, fontWeight: 600, letterSpacing: 1.5,
              textTransform: "uppercase", marginTop: 8 }}>Wallet Pass Management</div>
          </div>

          <div style={{ display: "flex", alignItems: "center", gap: 12, margin: "22px 0 4px" }}>
            <div style={{ flex: 1, height: 1, background: "rgba(255,255,255,0.1)" }} />
            <div style={{ color: "#6f7d95", fontSize: 10.5, fontWeight: 700, letterSpacing: 1.5, textTransform: "uppercase" }}>
              {vendors ? "Choose Vendor" : "Spoonity Account"}
            </div>
            <div style={{ flex: 1, height: 1, background: "rgba(255,255,255,0.1)" }} />
          </div>

          {error && <div style={{ background: "rgba(226,74,96,.14)", color: "#ff90a2", padding: "9px 11px",
            borderRadius: 8, fontSize: 13, marginTop: 12, border: "1px solid rgba(226,74,96,.3)" }}>{error}</div>}

          {!vendors ? (
            <form onSubmit={submitCreds}>
              <label style={label}>Email address
                <input style={input} type="email" autoComplete="username" value={email}
                  onChange={(e) => setEmail(e.target.value)} disabled={busy} />
              </label>
              <label style={label}>Password
                <input style={input} type="password" autoComplete="current-password" value={password}
                  onChange={(e) => setPassword(e.target.value)} disabled={busy} />
              </label>
              <button type="submit" style={{ ...btn(BLUE), opacity: busy ? 0.7 : 1 }} disabled={busy}>
                <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.2"
                  strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                  <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" /><path d="M10 17l5-5-5-5" /><path d="M15 12H3" />
                </svg>
                {busy ? "Signing in…" : "Sign In"}
              </button>
            </form>
          ) : (
            <div style={{ marginTop: 12 }}>
              <input ref={filterRef} value={filter} onChange={(e) => setFilter(e.target.value)}
                placeholder="Filter vendors…" disabled={busy} style={{ ...input, marginTop: 0 }} />
              {(() => {
                const q = filter.trim().toLowerCase();
                const matches = vendors.filter((v) =>
                  !q || (v.name || "").toLowerCase().includes(q) || String(v.id).toLowerCase().includes(q));
                // Scrollable list — shows ~10 vendors, the rest scroll.
                return (
                  <React.Fragment>
                    <div style={{ maxHeight: 480, overflowY: "auto", marginTop: 8 }}>
                      {matches.length === 0 ? (
                        <div style={{ color: "#7e8ca6", fontSize: 13, padding: "10px 2px" }}>No matching vendors.</div>
                      ) : matches.map((v) => (
                        <button key={v.id} onClick={() => pick(v.id)} disabled={busy}
                          style={{ width: "100%", textAlign: "left", padding: "10px 12px", borderRadius: 10,
                            border: "1px solid rgba(255,255,255,0.09)", background: "rgba(255,255,255,0.04)",
                            cursor: "pointer", marginTop: 6, color: "#e6ecf5" }}>
                          <div style={{ fontWeight: 600, fontSize: 13, color: "#e6ecf5" }}>{v.name}</div>
                          <div style={{ fontSize: 11, color: "#7e8ca6" }}>Vendor {v.id}</div>
                        </button>
                      ))}
                    </div>
                    <div style={{ fontSize: 11, color: "#6f7d95", marginTop: 6 }}>
                      {matches.length} of {vendors.length} vendors
                    </div>
                  </React.Fragment>
                );
              })()}
              <button onClick={() => { setVendors(null); setError(""); }} disabled={busy}
                style={{ ...btn("transparent"), background: "transparent", color: "#8291ab",
                  border: "1px solid rgba(255,255,255,0.15)", fontWeight: 600, marginTop: 14 }}>
                Back
              </button>
            </div>
          )}
        </div>
      </div>
    );
  }

  function AuthBadge({ session, onLogout, onSwitch }) {
    const v = session.vendor;
    const canSwitch = (session.vendors || []).length > 1;
    return (
      <div style={{ position: "fixed", top: 10, right: 12, zIndex: 4000, display: "flex", alignItems: "center",
        gap: 8, background: "rgba(255,255,255,.92)", border: "1px solid #e6e6ee", borderRadius: 999,
        padding: "5px 6px 5px 13px", fontSize: 12, boxShadow: "0 2px 10px rgba(0,0,0,.08)" }}>
        <span style={{ color: "#555", maxWidth: 220, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
          {v ? `${v.name} · ${v.id}` : "Signed in"}
        </span>
        {canSwitch && <button onClick={onSwitch} title="Switch vendor"
          style={{ border: "1px solid #d7deea", background: "#fff", color: ACCENT, borderRadius: 999,
            padding: "5px 11px", fontSize: 12, fontWeight: 600, cursor: "pointer" }}>Switch</button>}
        <button onClick={onLogout} title="Sign out"
          style={{ border: "none", background: ACCENT, color: "#fff", borderRadius: 999, padding: "5px 12px",
            fontSize: 12, fontWeight: 600, cursor: "pointer" }}>Sign out</button>
      </div>
    );
  }

  // In-session vendor switcher — a filterable/scrollable picker of the operator's vendors,
  // re-scoping the session via /auth/switch (no password).
  function SwitchVendorModal({ session, onClose, onSwitched }) {
    const [filter, setFilter] = React.useState("");
    const [busy, setBusy] = React.useState(false);
    const [error, setError] = React.useState("");
    const ref = React.useRef(null);
    React.useEffect(() => { if (ref.current) ref.current.focus(); }, []);
    const vendors = session.vendors || [];
    const q = filter.trim().toLowerCase();
    const matches = vendors.filter((v) => !q || (v.name || "").toLowerCase().includes(q) || String(v.id).toLowerCase().includes(q));
    const pick = async (id) => {
      setBusy(true); setError("");
      try { await window.SpoonityAuth.switchVendor(id); onSwitched(); }
      catch (e) { setError(e.message || "Switch failed."); setBusy(false); }
    };
    return (
      <div style={{ ...screen, background: "rgba(8,13,24,.78)" }} onClick={(e) => { if (e.target === e.currentTarget && !busy) onClose(); }}>
        <div style={card}>
          <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 4 }}>
            <div style={{ flex: 1, height: 1, background: "rgba(255,255,255,0.1)" }} />
            <div style={{ color: "#6f7d95", fontSize: 10.5, fontWeight: 700, letterSpacing: 1.5, textTransform: "uppercase" }}>Switch Vendor</div>
            <div style={{ flex: 1, height: 1, background: "rgba(255,255,255,0.1)" }} />
          </div>
          {error && <div style={{ background: "rgba(226,74,96,.14)", color: "#ff90a2", padding: "9px 11px", borderRadius: 8, fontSize: 13, marginTop: 12, border: "1px solid rgba(226,74,96,.3)" }}>{error}</div>}
          <input ref={ref} value={filter} onChange={(e) => setFilter(e.target.value)} placeholder="Filter vendors…" disabled={busy} style={{ ...input, marginTop: 12 }} />
          <div style={{ maxHeight: 480, overflowY: "auto", marginTop: 8 }}>
            {matches.length === 0 ? (
              <div style={{ color: "#7e8ca6", fontSize: 13, padding: "10px 2px" }}>No matching vendors.</div>
            ) : matches.map((v) => {
              const active = v.id === (session.vendor && session.vendor.id);
              return (
                <button key={v.id} onClick={() => pick(v.id)} disabled={busy}
                  style={{ width: "100%", textAlign: "left", padding: "10px 12px", borderRadius: 10,
                    border: active ? ("1px solid " + ACCENT) : "1px solid rgba(255,255,255,0.09)",
                    background: "rgba(255,255,255,0.04)", cursor: "pointer", marginTop: 6, color: "#e6ecf5" }}>
                  <div style={{ fontWeight: 600, fontSize: 13, color: "#e6ecf5" }}>{v.name}{active ? " ✓" : ""}</div>
                  <div style={{ fontSize: 11, color: "#7e8ca6" }}>Vendor {v.id}</div>
                </button>
              );
            })}
          </div>
          <button onClick={onClose} disabled={busy}
            style={{ ...btn("transparent"), background: "transparent", color: "#8291ab", border: "1px solid rgba(255,255,255,0.15)", fontWeight: 600, marginTop: 14 }}>
            Cancel
          </button>
        </div>
      </div>
    );
  }

  window.SpoonityAuthGate = function SpoonityAuthGate({ children }) {
    const [session, setSession] = React.useState(() => window.SpoonityAuth.getSession());
    const [switching, setSwitching] = React.useState(false);
    const refresh = () => setSession(window.SpoonityAuth.getSession());
    React.useEffect(() => {
      // A 401 from a data call (e.g. /design) dispatches this to force re-login.
      const onExpire = () => { window.SpoonityAuth.logout(); refresh(); };
      window.addEventListener("spoonity-auth-expired", onExpire);
      return () => window.removeEventListener("spoonity-auth-expired", onExpire);
    }, []);
    if (!session) return <LoginScreen onAuthed={refresh} />;
    return (
      <React.Fragment>
        <AuthBadge session={session} onLogout={() => { window.SpoonityAuth.logout(); refresh(); }} onSwitch={() => setSwitching(true)} />
        {switching && <SwitchVendorModal session={session} onClose={() => setSwitching(false)} onSwitched={() => { setSwitching(false); refresh(); }} />}
        {children}
      </React.Fragment>
    );
  };
})();
