/* GPS Design-Capture Admin dashboard — loaded before app.jsx; exposes window.Admin.
   Wired to the live backend via window.GPSApi: login-gated, with events (the
   captured guest designs + CSV export), a campaign Builder with the storefront
   theme designer, casinos, and kiosk device provisioning. */
(function () {
  const { useState, useEffect } = React;
  const Api = window.GPSApi;
  const cfg = window.GPS_CONFIG || {};
  const T = window.GPSTheme;
  const fmt = (n) => Number(n || 0).toLocaleString("en-US");
  const fmtDate = (s) => (s ? new Date(s).toLocaleString() : "");

  /* ---------------- small pieces ---------------- */
  function StatusBadge({ status }) {
    const s = String(status || "").toLowerCase();
    const cls = s === "active" ? "b-active" : s === "scheduled" ? "b-scheduled" : "b-draft";
    const label = s ? s.charAt(0).toUpperCase() + s.slice(1) : "—";
    return <span className={`badge ${cls}`}><span className="dt"></span>{label}</span>;
  }
  function StatePill({ state }) {
    const s = String(state || "").toLowerCase();
    const cls = s === "shared" ? "b-active" : s === "started" ? "b-scheduled" : "b-low";
    const label = s === "shared" ? "Captured" : s.charAt(0).toUpperCase() + s.slice(1);
    return <span className={`badge ${cls}`}><span className="dt"></span>{label}</span>;
  }

  /* ---------------- Storefront branding (theme designer) ---------------- */
  function ColorField({ label, hint, value, onChange }) {
    const hex = T.oklchToHex(value);
    return (
      <div className="color-field">
        <label className="cf-swatch" style={{ background: hex }}>
          <input type="color" value={hex} onChange={(e) => onChange(T.hexToOklch(e.target.value))} />
        </label>
        <div className="cf-body">
          <div className="cf-label">{label}</div>
          <div className="cf-hex num">{hex.toUpperCase()}</div>
        </div>
        {hint && <div className="cf-hint">{hint}</div>}
      </div>
    );
  }

  function FontSelect({ label, value, options, onChange }) {
    return (
      <div className="field">
        <label className="field-label">{label}</label>
        <select className="sel" value={value} onChange={(e) => { T.loadFont(e.target.value); onChange(e.target.value); }}>
          {options.map((f) => <option key={f.name} value={f.name}>{f.name}</option>)}
        </select>
        <div className="font-specimen" style={{ fontFamily: T.fontStack(value) }}>Aa — Design Your Custom Nikes</div>
      </div>
    );
  }

  function ContrastChips({ theme }) {
    const d = T.diagnostics(theme);
    const rows = [
      { k: "Body text on background", v: d.textOnBg },
      { k: "Button label on accent", v: d.buttonText },
      { k: "CTA label on button", v: d.ctaText },
      { k: "Accent on background", v: d.accentOnBg },
    ];
    const tone = (label) => (label === "AAA" || label === "AA") ? "ok" : label === "AA Large" ? "warn" : "bad";
    return (
      <div className="contrast-chips">
        {rows.map((r) => (
          <div className={`cc-chip ${tone(r.v.label)}`} key={r.k}>
            <span className="cc-dot"></span>
            <span className="cc-k">{r.k}</span>
            <span className="cc-v num">{r.v.ratio.toFixed(1)}:1 · {r.v.label}</span>
          </div>
        ))}
      </div>
    );
  }

  function ThemePreview({ theme }) {
    const vars = T.cssVars(theme);
    return (
      <div className="theme-preview">
        <div className="tp-bar">
          <span className="tp-dot"></span><span className="tp-dot"></span><span className="tp-dot"></span>
          <span className="tp-url">storefront · {theme.casinoName || "Casino"} kiosk</span>
        </div>
        <div className="tp-stage" style={vars}>
          <div className="tp-frame"></div>
          <header className="tp-top">
            <div className="tp-cobrand">
              <div className="tp-mark"><span>LOGO</span></div>
              <div className="tp-names">
                <div className="tp-name">{theme.casinoName || "Casino Name"}</div>
                <div className="tp-loc">{theme.tagline || "Design Studio"}</div>
              </div>
            </div>
            <div className="tp-tag">Players Club</div>
          </header>
          <div className="tp-hero">
            <div className="tp-eyebrow">— Design Studio —</div>
            <div className="tp-headline">Design Your Custom <em>Nikes</em></div>
            <div className="tp-sub">Create your one-of-a-kind pair.</div>
            <button className="tp-start" type="button" tabIndex={-1}>Start <span>→</span></button>
          </div>
          <div className="tp-card">
            <div className="tp-card-l">Your Design</div>
            <div className="tp-card-v">✓ Saved</div>
            <div className="tp-chip"></div>
          </div>
          <footer className="tp-foot">
            <div className="tp-by">Presented by</div>
            <div className="tp-gps"><b>GPS</b> Promotional Gifting Solutions</div>
          </footer>
        </div>
      </div>
    );
  }

  function BrandingPanel({ theme, setTheme }) {
    const patch = (p) => setTheme((t) => ({ ...t, ...p }));
    const applyPreset = (p) => {
      const next = { ...p };
      setTheme((t) => ({ ...next, casinoName: t.casinoName || next.casinoName, tagline: t.tagline || next.tagline }));
      T.ensureFonts(p);
    };
    return (
      <div className="panel panel-pad brand-panel">
        <div className="brand-head">
          <div>
            <h3 className="brand-h">Storefront Branding</h3>
            <p className="brand-sub">Skin this casino's kiosk — colors and fonts cascade through every screen, from the welcome to the check-in form.</p>
          </div>
        </div>

        <div className="brand-grid">
          <div className="brand-controls">
            <div className="field full">
              <label className="field-label">Start from a theme</label>
              <div className="preset-row">
                {T.PRESETS.map((p) => (
                  <button key={p.id} type="button" className={`preset-chip${theme.id === p.id ? " sel" : ""}`}
                    onClick={() => { patch({ id: p.id }); applyPreset(p); }}>
                    <span className="pc-swatches">
                      <i style={{ background: T.oklchToHex(p.surface) }}></i>
                      <i style={{ background: T.oklchToHex(p.accent) }}></i>
                      <i style={{ background: T.oklchToHex(p.light) }}></i>
                    </span>
                    <span className="pc-name">{p.casinoName}</span>
                  </button>
                ))}
              </div>
            </div>

            <div className="form" style={{ gap: 18 }}>
              <div className="field">
                <label className="field-label">Storefront name</label>
                <input className="inp" value={theme.casinoName} maxLength={28}
                  onChange={(e) => patch({ casinoName: e.target.value })} placeholder="Riverwind" />
              </div>
              <div className="field">
                <label className="field-label">Tagline</label>
                <input className="inp" value={theme.tagline} maxLength={32}
                  onChange={(e) => patch({ tagline: e.target.value })} placeholder="Casino · Design Studio" />
              </div>
            </div>

            <div className="field full">
              <label className="field-label">Brand colors</label>
              <div className="color-row">
                <ColorField label="Background" hint="Deep brand color" value={theme.surface} onChange={(v) => patch({ surface: v })} />
                <ColorField label="Accent" hint="Highlights & glow" value={theme.accent} onChange={(v) => patch({ accent: v })} />
                <ColorField label="Button" hint="Primary CTA" value={theme.button} onChange={(v) => patch({ button: v })} />
                <ColorField label="Text" hint="On dark surfaces" value={theme.light} onChange={(v) => patch({ light: v })} />
              </div>
              <div className="field-help">Lighter and darker shades are generated automatically to keep the kiosk's depth and glow.</div>
            </div>

            <div className="form" style={{ gap: 18 }}>
              <FontSelect label="Heading font" value={theme.heading} options={T.HEADING_FONTS} onChange={(v) => patch({ heading: v })} />
              <FontSelect label="Body font" value={theme.body} options={T.BODY_FONTS} onChange={(v) => patch({ body: v })} />
            </div>

            <div className="field full">
              <label className="field-label">Accessibility</label>
              <ContrastChips theme={theme} />
            </div>
          </div>

          <div className="brand-preview">
            <div className="bp-cap">Live preview</div>
            <ThemePreview theme={theme} />
            <div className="bp-note">Changes here re-skin the live kiosk when you save &amp; activate.</div>
          </div>
        </div>
      </div>
    );
  }

  /* ---------------- Tab 1: Campaigns (events) ---------------- */
  function CampaignsTab({ campaigns, loading, onNew, onEdit, onActivate }) {
    return (
      <>
        <div className="adm-head">
          <div>
            <div className="adm-h1">Events</div>
            <div className="adm-sub">Active and upcoming design-studio events. Only one event is live at a time across all kiosks — activating one ends the other and makes its theme dominant.</div>
          </div>
          <div className="adm-actions">
            <button className="btn btn-primary" onClick={onNew}><span className="pl">+</span> New Event</button>
          </div>
        </div>

        <div className="panel">
          <div className="tbl-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th>Casino</th><th>Event</th><th>Status</th>
                  <th>Shoes</th><th>Entries</th><th>Window</th><th>Actions</th>
                </tr>
              </thead>
              <tbody>
                {loading ? (
                  <tr><td colSpan={7} style={{ padding: 24 }}>Loading…</td></tr>
                ) : campaigns.map((c) => (
                  <tr key={c.id}>
                    <td className="casino-cell">{c.casino}<small>{c.location}</small></td>
                    <td>{c.name}</td>
                    <td><StatusBadge status={c.status} /></td>
                    <td className="num">{c.offeredShoes} shoes</td>
                    <td className="num">{fmt(c.entries)}</td>
                    <td className="num">{c.startDate || "—"} – {c.endDate || "—"}</td>
                    <td>
                      <div style={{ display: "flex", gap: 8 }}>
                        <button className="btn" style={{ padding: "6px 12px" }} onClick={() => onEdit(c.id)}>Edit</button>
                        {c.status === "active"
                          ? <span className="badge b-active"><span className="dt"></span>Live</span>
                          : <button className="btn btn-primary" style={{ padding: "6px 12px" }} onClick={() => onActivate(c)}>Activate</button>}
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </>
    );
  }

  /* ---------------- Tab 2: Campaign Builder ---------------- */
  function BuilderTab({ casinos, shoes, flash, onCreated, onSaved, onCreateCasino, editId }) {
    const [casino, setCasino] = useState("");
    const [name, setName] = useState("");
    const [picked, setPicked] = useState({});
    const [customUrls, setCustomUrls] = useState({}); // { shoeId: nikeByYouBuilderUrl }
    const [shoeImages, setShoeImages] = useState({}); // { shoeId: imageUrl override }
    const [shoeImageDefaults, setShoeImageDefaults] = useState({}); // { shoeId: catalog default }
    const [premades, setPremades] = useState({});     // { shoeId: [{ name, shareUrl, imageUrl, colors:[hex,hex,hex] }] }
    const [start, setStart] = useState("2026-06-01");
    const [end, setEnd] = useState("2026-08-31");
    const [theme, setTheme] = useState(() => T.loadTheme());
    const [saving, setSaving] = useState(false);
    const [showAddCasino, setShowAddCasino] = useState(false);
    const [ncName, setNcName] = useState("");
    const [ncLoc, setNcLoc] = useState("");
    const [addingCasino, setAddingCasino] = useState(false);

    useEffect(() => {
      if (!editId && !casino && casinos.length) setCasino(casinos[0].id);
    }, [casinos]);

    useEffect(() => {
      if (!editId) return;
      Api.adminGetCampaign(editId).then((d) => {
        setCasino(d.casinoId);
        setName(d.name || "");
        setStart((d.startDate || "").slice(0, 10));
        setEnd((d.endDate || "").slice(0, 10));
        if (d.theme) { setTheme(d.theme); T.ensureFonts(d.theme); }
        const p = {}, cu = {}, pm = {}, si = {}, sd = {};
        (d.shoes || []).forEach((s) => {
          p[s.shoeId] = true;
          cu[s.shoeId] = s.customUrl || "";
          si[s.shoeId] = s.imageUrl || "";
          sd[s.shoeId] = s.defaultImageUrl || "";
          pm[s.shoeId] = (s.premades || []).map((x) => ({ name: x.name || "", shareUrl: x.shareUrl || "", imageUrl: x.imageUrl || "", colors: (x.colors || []).slice(0, 3) }));
        });
        setPicked(p); setCustomUrls(cu); setShoeImages(si); setShoeImageDefaults(sd); setPremades(pm);
      }).catch((e) => flash(e.message));
    }, [editId]);

    const count = Object.values(picked).filter(Boolean).length;
    const toggle = (id) => setPicked((p) => ({ ...p, [id]: !p[id] }));
    const addPremade = (shoeId) => setPremades((m) => ({ ...m, [shoeId]: [...(m[shoeId] || []), { name: "", shareUrl: "", imageUrl: "", colors: ["#cccccc", "#cccccc", "#cccccc"] }] }));
    const updatePremade = (shoeId, idx, patch) => setPremades((m) => ({ ...m, [shoeId]: (m[shoeId] || []).map((r, i) => (i === idx ? { ...r, ...patch } : r)) }));
    const removePremade = (shoeId, idx) => setPremades((m) => ({ ...m, [shoeId]: (m[shoeId] || []).filter((_, i) => i !== idx) }));
    const pickCasino = (id) => {
      setCasino(id);
      const c = casinos.find((x) => x.id === id);
      if (c) setTheme((t) => ({ ...t, casinoName: c.name.replace(/ (Casino|Resort).*$/, "") }));
    };
    const addCasino = async () => {
      if (!ncName.trim()) { flash("Casino name is required."); return; }
      setAddingCasino(true);
      try {
        const c = await onCreateCasino({ name: ncName.trim(), location: ncLoc.trim() });
        setCasino(c.id);
        setTheme((t) => ({ ...t, casinoName: c.name.replace(/ (Casino|Resort).*$/, "") }));
        setShowAddCasino(false); setNcName(""); setNcLoc("");
        flash(`Added ${c.name}.`);
      } catch (e) {
        flash(e.message || "Could not add casino.");
      } finally {
        setAddingCasino(false);
      }
    };

    const buildPayload = (status) => {
      const p = {
        casinoId: casino,
        name: name.trim() || "Untitled Event",
        startDate: start,
        endDate: end,
        theme,
        shoes: Object.keys(picked).filter((id) => picked[id]).map((id) => {
          const s = shoes.find((x) => x.id === id);
          return {
            shoeId: id, name: s ? s.name : id, customUrl: (customUrls[id] || "").trim(),
            imageUrl: (shoeImages[id] || "").trim(),
            premades: (premades[id] || [])
              .filter((r) => (r.name || "").trim() && (r.shareUrl || "").trim())
              .map((r) => ({ name: r.name.trim(), shareUrl: r.shareUrl.trim(), imageUrl: (r.imageUrl || "").trim(), colors: (r.colors || []).filter(Boolean) })),
          };
        }),
      };
      if (status) p.status = status;
      return p;
    };

    const submit = async (status, label) => {
      if (!casino) { flash("Pick a casino first."); return; }
      setSaving(true);
      try {
        await Api.adminCreateCampaign(buildPayload(status));
        flash(`${label} — ${theme.casinoName} · ${count} shoes.`);
        onCreated && onCreated();
      } catch (e) { flash(e.message || "Save failed."); }
      finally { setSaving(false); }
    };
    const save = async (status, label) => {
      setSaving(true);
      try {
        await Api.adminUpdateCampaign(editId, buildPayload(status));
        flash(label);
        onSaved && onSaved();
      } catch (e) { flash(e.message || "Save failed."); }
      finally { setSaving(false); }
    };

    const inp = { padding: "6px 9px", borderRadius: 8, border: "1.5px solid rgba(120,120,120,.3)", fontSize: 13, fontFamily: "inherit" };

    return (
      <>
        <div className="adm-head">
          <div>
            <div className="adm-h1">{editId ? "Edit Event" : "Event Builder"}</div>
            <div className="adm-sub">{editId ? "Update this event's shoes, Nike By You links, dates, and branding — then save or activate." : "Configure a new design-studio event — name, casino, the silhouettes it offers, and branding."}</div>
          </div>
        </div>

        <div className="panel panel-pad">
          <div className="form">
            <div className="field">
              <label className="field-label">Event name <span className="req">*</span></label>
              <input className="inp" value={name} maxLength={48} onChange={(e) => setName(e.target.value)} placeholder="Summer Sneaker Studio" />
            </div>
            <div className="field">
              <label className="field-label">Casino <span className="req">*</span></label>
              <select className="sel" value={casino} disabled={!!editId}
                onChange={(e) => { if (e.target.value === "__add__") { setShowAddCasino(true); } else { pickCasino(e.target.value); } }}>
                {casinos.map((c) => <option key={c.id} value={c.id}>{c.name}{c.location ? ` — ${c.location}` : ""}</option>)}
                {!editId && <option value="__add__">+ Add a new casino…</option>}
              </select>
              {!editId && showAddCasino && (
                <div style={{ display: "flex", gap: 8, marginTop: 8, flexWrap: "wrap", alignItems: "center" }}>
                  <input className="inp" placeholder="Casino name" value={ncName} onChange={(e) => setNcName(e.target.value)} style={{ flex: "1 1 170px" }} />
                  <input className="inp" placeholder="Location (City, ST)" value={ncLoc} onChange={(e) => setNcLoc(e.target.value)} style={{ flex: "1 1 150px" }} />
                  <button className="btn btn-primary" type="button" disabled={addingCasino} onClick={addCasino}>{addingCasino ? "Adding…" : "Create"}</button>
                  <button className="btn" type="button" onClick={() => { setShowAddCasino(false); setNcName(""); setNcLoc(""); }}>Cancel</button>
                </div>
              )}
            </div>

            <div className="field full">
              <label className="field-label">Silhouettes offered <span className="req">*</span> <span style={{ color: "var(--faint)", fontWeight: 700 }}>· {count} selected</span></label>
              <div className="field-help" style={{ marginTop: 0, marginBottom: 10 }}>Pick which Nike By You shoes this event offers, and paste each one's "Customize Your Own" builder link (the page the kiosk opens).</div>
              <div className="checklist">
                {shoes.map((s) => (
                  <div key={s.id} className="check-item">
                    <div className={`check-row${picked[s.id] ? " on" : ""}`} onClick={() => toggle(s.id)}>
                      <div className="check-box">{picked[s.id] ? "✓" : ""}</div>
                      <div className="check-name">{s.name}</div>
                      <div className="check-meta">
                        <span className={`badge ${s.brand === "Converse" ? "b-conv" : "b-byyou"}`}>{s.brand}</span>
                      </div>
                    </div>
                    {picked[s.id] && (
                      <div className="cw-editor" style={{ padding: "10px 14px 16px 46px" }}>
                        <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", marginBottom: 10 }}>
                          <span style={{ fontSize: 12, fontWeight: 800, color: "var(--faint)", flex: "0 0 150px" }}>Customize Your Own link</span>
                          <input placeholder="https://www.nike.com/u/…  (Nike By You builder page for this shoe)" value={customUrls[s.id] || ""}
                            onChange={(e) => setCustomUrls((u) => ({ ...u, [s.id]: e.target.value }))}
                            style={{ ...inp, flex: "1 1 340px" }} />
                        </div>
                        <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", marginBottom: 14 }}>
                          <span style={{ fontSize: 12, fontWeight: 800, color: "var(--faint)", flex: "0 0 150px" }}>Card image (16:10)</span>
                          <input placeholder={`Default: /img/shoes/${s.id}.png — paste a URL to override`} value={shoeImages[s.id] || ""}
                            onChange={(e) => setShoeImages((u) => ({ ...u, [s.id]: e.target.value }))}
                            style={{ ...inp, flex: "1 1 340px" }} />
                          {(shoeImages[s.id] || shoeImageDefaults[s.id] || `/img/shoes/${s.id}.png`) &&
                            <img alt="" src={shoeImages[s.id] || shoeImageDefaults[s.id] || `/img/shoes/${s.id}.png`}
                              onError={(e) => { e.target.style.display = "none"; }}
                              style={{ width: 56, height: 35, objectFit: "cover", borderRadius: 6, border: "1px solid var(--line)" }} />}
                        </div>
                        <div style={{ fontSize: 12, fontWeight: 800, color: "var(--faint)", marginBottom: 8 }}>
                          Prebuilt designs — each needs a name + a Nike By You SHARE link (Share → Copy Link) ({(premades[s.id] || []).filter((r) => (r.name || "").trim() && (r.shareUrl || "").trim()).length})
                        </div>
                        {(premades[s.id] || []).map((row, idx) => (
                          <div key={idx} style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 6, flexWrap: "wrap" }}>
                            <input placeholder="Design name" value={row.name}
                              onChange={(e) => updatePremade(s.id, idx, { name: e.target.value })} style={{ ...inp, flex: "0 0 150px" }} />
                            <input placeholder="https://www.nike.com/u/…?mid=…  (Share → Copy Link)" value={row.shareUrl}
                              onChange={(e) => updatePremade(s.id, idx, { shareUrl: e.target.value })} style={{ ...inp, flex: "1 1 220px" }} />
                            <input placeholder="Image URL (16:10, optional)" value={row.imageUrl || ""}
                              onChange={(e) => updatePremade(s.id, idx, { imageUrl: e.target.value })} style={{ ...inp, flex: "1 1 160px" }} />
                            {[0, 1, 2].map((ci) => (
                              <input key={ci} type="color" title={`Swatch ${ci + 1}`} value={(row.colors && row.colors[ci]) || "#cccccc"}
                                onChange={(e) => { const c = [...(row.colors || [])]; c[ci] = e.target.value; updatePremade(s.id, idx, { colors: c }); }}
                                style={{ width: 30, height: 30, padding: 0, border: "none", background: "none", cursor: "pointer" }} />
                            ))}
                            <button type="button" title="Remove" onClick={() => removePremade(s.id, idx)}
                              style={{ border: "none", background: "none", cursor: "pointer", fontSize: 18, color: "var(--faint)", fontWeight: 800 }}>×</button>
                          </div>
                        ))}
                        <button type="button" onClick={() => addPremade(s.id)}
                          style={{ marginTop: 6, padding: "5px 12px", borderRadius: 999, border: "1.5px solid var(--accent)", background: "var(--accent-soft)", cursor: "pointer", fontSize: 12, fontWeight: 800, color: "var(--accent)" }}>+ Add prebuilt design</button>
                      </div>
                    )}
                  </div>
                ))}
              </div>
            </div>

            <div className="field">
              <label className="field-label">Start Date</label>
              <input className="inp" type="date" value={start} onChange={(e) => setStart(e.target.value)} />
            </div>
            <div className="field">
              <label className="field-label">End Date</label>
              <input className="inp" type="date" value={end} onChange={(e) => setEnd(e.target.value)} />
            </div>
          </div>
        </div>

        <BrandingPanel theme={theme} setTheme={setTheme} />

        <div className="form-foot">
          {editId ? (
            <>
              <button className="btn" disabled={saving} onClick={() => save(undefined, "Changes saved.")}>Save Changes</button>
              <button className="btn btn-primary" disabled={saving} onClick={() => save("active", "Saved & activated — reload the kiosk to see it.")}>Save &amp; Activate</button>
            </>
          ) : (
            <>
              <button className="btn" disabled={saving} onClick={() => submit("draft", "Draft saved with branding")}>Save Draft</button>
              <button className="btn btn-primary" disabled={saving} onClick={() => submit("scheduled", "Event created (scheduled)")}>Create Event</button>
            </>
          )}
        </div>
      </>
    );
  }

  /* ---------------- Tab 3: Entries (captured designs + CSV) ---------------- */
  function EntriesTab({ campaigns, flash }) {
    const [filters, setFilters] = useState({ campaignId: "", state: "", range: "" });
    const [data, setData] = useState({ entries: [], summary: null });
    const [loading, setLoading] = useState(true);

    const load = () => {
      setLoading(true);
      Api.adminEntries(filters).then(setData).catch((e) => flash(e.message)).finally(() => setLoading(false));
    };
    useEffect(() => { load(); }, [filters.campaignId, filters.state, filters.range]);
    const setF = (k) => (e) => setFilters((s) => ({ ...s, [k]: e.target.value }));
    const s = data.summary || {};

    return (
      <>
        <div className="adm-head">
          <div>
            <div className="adm-h1">Entries</div>
            <div className="adm-sub">Every guest check-in and the Nike By You design they shared. Download the CSV after the event.</div>
          </div>
          <div className="adm-actions">
            <button className="btn btn-primary" onClick={() => Api.downloadEntriesCsv(filters).then(() => flash("CSV exported.")).catch((e) => flash(e.message))}>
              <span className="pl">↧</span> Download CSV
            </button>
          </div>
        </div>

        <div className="kpi-grid">
          <div className="kpi"><div className="k">Check-ins</div><div className="v num">{fmt(s.total)}</div><div className="d flat">guests started</div></div>
          <div className="kpi"><div className="k">Designs captured</div><div className="v num">{fmt(s.shared)}</div><div className="d flat">share links saved</div></div>
          <div className="kpi"><div className="k">Capture rate</div><div className="v num">{s.shareRatePct || 0}%</div><div className="d flat">of check-ins</div></div>
        </div>

        <div className="adm-filters" style={{ display: "flex", gap: 10, margin: "18px 0", flexWrap: "wrap" }}>
          <select className="sel" style={{ maxWidth: 280 }} value={filters.campaignId} onChange={setF("campaignId")}>
            <option value="">All events</option>
            {campaigns.map((c) => <option key={c.id} value={c.id}>{c.casino} — {c.name}</option>)}
          </select>
          <select className="sel" style={{ maxWidth: 180 }} value={filters.state} onChange={setF("state")}>
            <option value="">Any status</option>
            <option value="shared">Captured</option>
            <option value="started">Started</option>
            <option value="abandoned">Abandoned</option>
          </select>
          <select className="sel" style={{ maxWidth: 180 }} value={filters.range} onChange={setF("range")}>
            <option value="">All time</option>
            <option value="7d">Last 7 days</option>
            <option value="30d">Last 30 days</option>
            <option value="90d">Last 90 days</option>
          </select>
        </div>

        <div className="panel">
          <div className="tbl-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th>When</th><th>Guest</th><th>Player ID</th><th>Fit / Size</th>
                  <th>Shoe</th><th>Status</th><th>Design (mid)</th><th>Link</th>
                </tr>
              </thead>
              <tbody>
                {loading ? (
                  <tr><td colSpan={8} style={{ padding: 24 }}>Loading…</td></tr>
                ) : data.entries.length === 0 ? (
                  <tr><td colSpan={8} style={{ padding: 24, color: "var(--faint)" }}>No entries yet.</td></tr>
                ) : data.entries.map((e) => (
                  <tr key={e.id}>
                    <td>{fmtDate(e.createdAt)}</td>
                    <td className="casino-cell">{e.guestName}</td>
                    <td className="num">{e.playerId}</td>
                    <td>{(e.shoeGender === "womens" ? "Women's" : "Men's")} · {e.shoeSize}</td>
                    <td>{e.shoeName || e.shoeId || "—"}</td>
                    <td><StatePill state={e.state} /></td>
                    <td className="num">{e.designMid || "—"}</td>
                    <td>{e.shareUrl ? <a href={e.shareUrl} target="_blank" rel="noopener">open ↗</a> : "—"}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </>
    );
  }

  /* ---------------- Tab 4: Devices ---------------- */
  function DevicesTab({ casinos, flash }) {
    const [devices, setDevices] = useState([]);
    const [form, setForm] = useState({ casinoId: "", label: "" });
    const [newToken, setNewToken] = useState(null);
    const load = () => Api.adminDevices().then((d) => setDevices(d.devices || [])).catch((e) => flash(e.message));
    useEffect(() => { load(); }, []);
    const create = async (e) => {
      e.preventDefault();
      if (!form.casinoId || !form.label) { flash("Pick a casino and label."); return; }
      try { const r = await Api.adminCreateDevice(form); setNewToken(r.token); setForm({ casinoId: "", label: "" }); load(); }
      catch (ex) { flash(ex.message); }
    };
    const revoke = async (id) => { if (!window.confirm("Revoke this device token?")) return; try { await Api.adminRevokeDevice(id); load(); } catch (ex) { flash(ex.message); } };
    return (
      <>
        <div className="adm-head">
          <div>
            <div className="adm-h1">Kiosk devices</div>
            <div className="adm-sub">Each physical kiosk carries a device token bound to one casino. The token is shown once at creation.</div>
          </div>
        </div>
        <div className="panel panel-pad" style={{ marginBottom: 18 }}>
          <form className="form" onSubmit={create} style={{ gridTemplateColumns: "1fr 1fr auto", alignItems: "end" }}>
            <div className="field">
              <label className="field-label">Casino</label>
              <select className="sel" value={form.casinoId} onChange={(e) => setForm((s) => ({ ...s, casinoId: e.target.value }))}>
                <option value="">Casino…</option>
                {casinos.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
              </select>
            </div>
            <div className="field">
              <label className="field-label">Label</label>
              <input className="inp" placeholder="Floor #1" value={form.label} onChange={(e) => setForm((s) => ({ ...s, label: e.target.value }))} />
            </div>
            <div className="field"><button className="btn btn-primary" type="submit">Provision</button></div>
          </form>
          {newToken && (
            <div style={{ marginTop: 14, padding: "12px 16px", background: "var(--ok-soft)", border: "1px solid var(--ok)", borderRadius: 10, wordBreak: "break-all" }}>
              <b>Device token (shown once): </b><span className="num">{newToken}</span>
            </div>
          )}
        </div>
        <div className="panel">
          <div className="tbl-wrap">
            <table className="tbl">
              <thead><tr><th>Casino</th><th>Label</th><th>Last seen</th><th>Status</th><th></th></tr></thead>
              <tbody>
                {devices.map((d) => (
                  <tr key={d.id}>
                    <td>{d.casino_id}</td><td>{d.label}</td><td>{fmtDate(d.last_seen_at)}</td>
                    <td>{d.revoked_at ? <span className="badge b-low"><span className="dt"></span>Revoked</span> : <span className="badge b-active"><span className="dt"></span>Active</span>}</td>
                    <td>{d.revoked_at ? null : <button className="btn" style={{ padding: "6px 12px" }} onClick={() => revoke(d.id)}>Revoke</button>}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </>
    );
  }

  /* ---------------- Login gate ---------------- */
  function LoginGate({ onAuthed }) {
    const [email, setEmail] = useState(cfg.ADMIN_EMAIL || "");
    const [password, setPassword] = useState("");
    const [err, setErr] = useState(null);
    const [busy, setBusy] = useState(false);
    const submit = async (e) => {
      e.preventDefault();
      setBusy(true); setErr(null);
      try { const user = await Api.adminLogin(email, password); onAuthed(user); }
      catch (ex) { setErr(ex.message || "Sign-in failed."); }
      finally { setBusy(false); }
    };
    return (
      <div className="admin" data-screen-label="Admin Login">
        <div className="adm-top">
          <div className="adm-logo">
            <span className="mk">GPS</span>
            <span>Admin <span className="sub">· Design Studio</span></span>
          </div>
        </div>
        <div className="adm-body" style={{ display: "block" }}>
          <main className="adm-main" style={{ maxWidth: 420, margin: "8vh auto" }}>
            <div className="panel panel-pad">
              <div className="adm-h1" style={{ marginBottom: 4 }}>Sign in</div>
              <div className="adm-sub" style={{ marginBottom: 16 }}>Operations dashboard</div>
              <form className="form" onSubmit={submit}>
                <div className="field full">
                  <label className="field-label">Email</label>
                  <input className="inp" type="email" value={email} onChange={(e) => setEmail(e.target.value)} autoComplete="username" />
                </div>
                <div className="field full">
                  <label className="field-label">Password</label>
                  <input className="inp" type="password" value={password} onChange={(e) => setPassword(e.target.value)} autoComplete="current-password" placeholder="sandbox-admin-pw" />
                </div>
                {err && <div className="field full" style={{ color: "var(--danger)" }}>{err}</div>}
                <div className="field full">
                  <button className="btn btn-primary" type="submit" disabled={busy}>{busy ? "Signing in…" : "Sign in"}</button>
                </div>
              </form>
            </div>
          </main>
        </div>
      </div>
    );
  }

  /* ---------------- shell ---------------- */
  function Dashboard({ onExit, user }) {
    const [tab, setTab] = useState("campaigns");
    const [editId, setEditId] = useState(null);
    const [toast, setToast] = useState(null);
    const [campaigns, setCampaigns] = useState([]);
    const [casinos, setCasinos] = useState([]);
    const [shoes, setShoes] = useState([]);
    const [loading, setLoading] = useState({});

    const flash = (msg) => {
      setToast(msg);
      clearTimeout(window.__admT);
      window.__admT = setTimeout(() => setToast(null), 2800);
    };

    const loadCampaigns = () => { setLoading((l) => ({ ...l, campaigns: true })); Api.adminCampaigns().then((d) => setCampaigns(d.campaigns || [])).catch((e) => flash(e.message)).finally(() => setLoading((l) => ({ ...l, campaigns: false }))); };
    const loadCasinos = () => Api.adminCasinos().then((d) => setCasinos(d.casinos || [])).catch((e) => flash(e.message));
    const createCasino = async (payload) => { const r = await Api.adminCreateCasino(payload); await loadCasinos(); return r.casino; };

    const startNew = () => { setEditId(null); setTab("builder"); };
    const startEdit = (id) => { setEditId(id); setTab("builder"); };
    const activate = (c) => {
      if (!window.confirm(`Make "${c.name}" the live event? Only one event is live at a time — it becomes the dominant event (and theme) on every kiosk, and any other live event is ended.`)) return;
      Api.adminUpdateCampaign(c.id, { status: "active" })
        .then(() => { flash("Activated — reload the kiosk to see it."); loadCampaigns(); })
        .catch((e) => flash(e.message));
    };

    useEffect(() => {
      loadCampaigns();
      loadCasinos();
      Api.adminShoes().then((d) => setShoes(d.shoes || [])).catch((e) => flash(e.message));
    }, []);

    const TABS = [
      { id: "campaigns", label: "Events", glyph: "▤", count: campaigns.length },
      { id: "builder", label: "Event Builder", glyph: "✎" },
      { id: "entries", label: "Entries", glyph: "▦" },
      { id: "devices", label: "Kiosk Devices", glyph: "▥" },
    ];

    return (
      <div className="admin" data-screen-label="Admin Dashboard">
        <div className="adm-top">
          <div className="adm-logo">
            <span className="mk">GPS</span>
            <span>Admin <span className="sub">· Design Studio</span></span>
            <span className="adm-env">Live · sandbox</span>
          </div>
          <div className="spacer"></div>
          <div className="adm-user"><span className="av">{(user.name || "?").split(" ").map((s) => s[0]).join("").slice(0, 2)}</span> {user.name} · {user.role}</div>
          <button className="adm-exit" onClick={onExit}>Exit to Kiosk</button>
        </div>

        <div className="adm-body">
          <nav className="adm-side">
            <div className="sec-cap">Operations</div>
            {TABS.map((t) => (
              <button key={t.id} className={`nav-item${tab === t.id ? " active" : ""}`} onClick={() => { if (t.id === "builder") setEditId(null); setTab(t.id); }}>
                <span className="ico"><span>{t.glyph}</span></span>
                {t.label}
                {t.count != null && <span className="badge-count num">{t.count}</span>}
              </button>
            ))}
          </nav>

          <main className="adm-main">
            {tab === "campaigns" && <CampaignsTab campaigns={campaigns} loading={loading.campaigns} onNew={startNew} onEdit={startEdit} onActivate={activate} />}
            {tab === "builder" && <BuilderTab key={editId || "new"} editId={editId} casinos={casinos} shoes={shoes} flash={flash} onCreateCasino={createCasino} onCreated={() => { loadCampaigns(); setTab("campaigns"); }} onSaved={() => { loadCampaigns(); setEditId(null); setTab("campaigns"); }} />}
            {tab === "entries" && <EntriesTab campaigns={campaigns} flash={flash} />}
            {tab === "devices" && <DevicesTab casinos={casinos} flash={flash} />}
          </main>
        </div>

        {toast && <div className="adm-toast"><span className="i">●</span> {toast}</div>}
      </div>
    );
  }

  function Admin({ onExit }) {
    const [user, setUser] = useState(null);
    useEffect(() => {
      if (!user && Api.isAdminAuthed()) {
        Api.adminMe().then((d) => setUser(d.user)).catch(() => Api.adminLogout());
      }
    }, []);
    if (!user && !Api.isAdminAuthed()) return <LoginGate onAuthed={setUser} />;
    if (!user) return <div className="admin" data-screen-label="Admin"><div className="adm-top"><div className="adm-logo"><span className="mk">GPS</span><span>Admin</span></div></div><main className="adm-main" style={{ padding: 24 }}>Loading…</main></div>;
    return <Dashboard onExit={onExit} user={user} />;
  }

  window.Admin = Admin;
})();
