// /account.html — customer order history. Visually mirrors the storefront:
// dot-pulse status indicators, octagonal CornerCuts on cards, spotlight-style
// hero strip for the account header, and the same sec-head/sec-tag rhythm
// used on the catalog & FAQ sections.

const { useState, useEffect } = React;

function AccountApp() {
  const { user, isAdmin } = window.useAuthState();
  const ready = window.ExecAuth && window.ExecAuth.isReady();

  const [orders, setOrders] = useState(null);
  const [revealed, setRevealed] = useState({});
  const [banner, setBanner] = useState(null);

  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const paid = params.get('paid');
    if (paid === 'ok' || paid === 'cancel') {
      setBanner(paid);
      params.delete('paid'); params.delete('sid');
      const qs = params.toString();
      window.history.replaceState({}, '', window.location.pathname + (qs ? '?' + qs : ''));
    }
  }, []);

  useEffect(() => {
    if (!user) { setOrders([]); return; }
    let cancelled = false;
    const load = () =>
      fetch('/api/orders', { credentials: 'same-origin' })
        .then((r) => r.ok ? r.json() : Promise.reject(r.status))
        .then((d) => { if (!cancelled) setOrders(d.orders || []); })
        .catch(() => { if (!cancelled) setOrders([]); });
    load();
    // Stripe webhook may land a beat after the redirect; second fetch catches it.
    if (banner === 'ok') {
      const t = setTimeout(load, 2500);
      return () => { cancelled = true; clearTimeout(t); };
    }
    return () => { cancelled = true; };
  }, [user, banner]);

  const reveal = async (itemId) => {
    try {
      const res = await fetch(`/api/orders/${itemId}/reveal-key`, { method: 'POST', credentials: 'same-origin' });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) { alert('Reveal failed: ' + (data.error || res.status)); return; }
      setRevealed((r) => ({ ...r, [itemId]: data.key }));
    } catch { alert('Reveal failed: network error'); }
  };

  if (!ready) {
    return <PageChrome user={null}><LoadingState/></PageChrome>;
  }

  if (!user) {
    return (
      <PageChrome user={null}>
        <LoggedOutCTA/>
      </PageChrome>
    );
  }

  const totalSpent = (orders || [])
    .filter((o) => o.status === 'paid')
    .reduce((s, o) => s + (o.amount_cents || 0), 0);
  const pendingKeys = pendingKeyCount(orders);

  return (
    <PageChrome user={user} isAdmin={isAdmin} currentTab="account">
      {banner && <InlineBanner kind={banner} onClose={() => setBanner(null)}/>}

      <HeroStrip user={user} isAdmin={isAdmin}>
        <StatColumn label="ORDERS"       value={orders === null ? '—' : String(orders.length)}/>
        <StatColumn label="TOTAL SPENT"  value={orders === null ? '—' : window.fmt(totalSpent / 100)}/>
        <StatColumn label="PENDING KEYS" value={pendingKeys === '…' ? '—' : String(pendingKeys)} accent={pendingKeys > 0}/>
      </HeroStrip>

      <div className="ap-sec-head">
        <div className="ap-sec-tag">// 01</div>
        <h2 className="ap-sec-title">ORDER HISTORY <span className="ap-sec-count">/ {orders === null ? '…' : orders.length}</span></h2>
        <div className="ap-sec-side">
          <a href="/" className="ap-sec-link">↶ STORE</a>
        </div>
      </div>

      {orders === null && <LoadingState/>}
      {orders !== null && orders.length === 0 && (
        <EmptyPanel
          art="[ ∅ ]"
          line="NO ORDERS YET"
          sub="keys delivered here after purchase"
          cta={<a href="/" className="btn primary"><window.Icon.Cart/><span>BROWSE CATALOG</span></a>}
        />
      )}
      {orders !== null && orders.length > 0 && (
        <div className="ap-orders">
          {orders.map((o) => (
            <OrderCard key={o.id} order={o} revealed={revealed} onReveal={reveal}/>
          ))}
        </div>
      )}
    </PageChrome>
  );
}

// ---------- Order card ----------
function OrderCard({ order, revealed, onReveal }) {
  const dotClass = order.status === 'paid' ? 'pulse' : '';
  return (
    <article className={window.cls('ap-order', 'ap-order--' + order.status)}>
      <window.CornerCuts size={10}/>
      <header className="ap-order-head">
        <span className={window.cls('ap-status', 'ap-status--' + order.status)}>
          <span className={window.cls('dot', dotClass)}></span>
          {order.status.toUpperCase()}
        </span>
        <span className="ap-order-id">ORDER · #{String(order.id).padStart(4, '0')}</span>
        <span className="ap-order-date">{fmtDate(order.created_at)}</span>
        <span className="ap-order-total">{window.fmt((order.amount_cents || 0) / 100)}</span>
      </header>
      <ul className="ap-items">
        {order.items.map((it) => (
          <li key={it.id} className="ap-item">
            <div className="ap-item-meta">
              <span className="ap-item-cat">{it.product_category}</span>
              <span className="ap-item-name">{it.product_name}</span>
              <span className="ap-item-tier">· {String(it.tier).toUpperCase()}</span>
            </div>
            <div className="ap-item-action">
              {it.has_key ? (
                revealed[it.id] ? (
                  <code
                    className="ap-key"
                    title="Click to copy"
                    onClick={(e) => {
                      navigator.clipboard.writeText(revealed[it.id]);
                      e.currentTarget.classList.add('ap-key--copied');
                      setTimeout(() => e.currentTarget && e.currentTarget.classList.remove('ap-key--copied'), 1200);
                    }}
                  >{revealed[it.id]}</code>
                ) : (
                  <button className="ap-reveal" onClick={() => onReveal(it.id)}>
                    REVEAL KEY <span className="ap-reveal-arrow">→</span>
                  </button>
                )
              ) : (
                <span className="ap-item-await">
                  {order.status === 'paid' ? 'AWAITING DELIVERY' : 'PENDING PAYMENT'}
                </span>
              )}
              {it.has_loader && order.status === 'paid' && (
                <a
                  className="ap-loader-btn"
                  href={`/api/orders/${it.id}/loader`}
                  target="_blank"
                  rel="noopener noreferrer"
                  title="Download the loader for this product"
                >
                  <span className="ap-loader-arrow">↓</span> DOWNLOAD LOADER
                </a>
              )}
            </div>
          </li>
        ))}
      </ul>
    </article>
  );
}

// ---------- Hero strip (spotlight-style) ----------
function HeroStrip({ user, isAdmin, children }) {
  // Some admin sub-views (e.g. WEBSITE tab) reuse the stat slot without a
  // logged-in user prop. Guard so passing null doesn't crash the entire
  // panel; the identity block just renders as anonymous.
  const name = user ? (user.global_name || user.username || '—') : 'ADMIN';
  return (
    <section className="ap-hero">
      <window.CornerCuts size={14}/>
      <div className="ap-hero-label">
        <span className="dot live"></span>
        <span>ACCOUNT // AUTHENTICATED</span>
      </div>
      <div className="ap-hero-body">
        {user ? (
          <div className="ap-hero-id">
            {user.avatar_url ? (
              <div className="ap-hero-avatar-frame">
                <window.CornerCuts size={8}/>
                <img src={user.avatar_url} alt="" className="ap-hero-avatar"/>
              </div>
            ) : (
              <div className="ap-hero-avatar-frame ap-hero-avatar--placeholder">{(name[0] || '?').toUpperCase()}</div>
            )}
            <div className="ap-hero-text">
              <div className="ap-hero-name">{name}</div>
              <div className="ap-hero-handle">
                @{user.username}
                {isAdmin && <span className="ap-admin-pill">ADMIN</span>}
              </div>
              <div className="ap-hero-meta">DISCORD · {user.discord_id}</div>
            </div>
          </div>
        ) : (
          <div className="ap-hero-id ap-hero-id--anon">
            <div className="ap-hero-text">
              <div className="ap-hero-name">{name}</div>
              <div className="ap-hero-handle">{isAdmin && <span className="ap-admin-pill">ADMIN</span>}</div>
            </div>
          </div>
        )}
        <div className="ap-hero-stats">
          {children}
        </div>
      </div>
    </section>
  );
}

function StatColumn({ label, value, accent }) {
  return (
    <div className={window.cls('ap-stat', accent && 'ap-stat--accent')}>
      <div className="ap-stat-label">{label}</div>
      <div className="ap-stat-value">{value}</div>
    </div>
  );
}

// ---------- Page chrome (nav + main) ----------
function PageChrome({ user, isAdmin, currentTab, children }) {
  const [navOpen, setNavOpen] = useState(false);
  return (
    <div className="root ap-root">
      <header className="nav ap-nav">
        <a href="/" className="nav-logo" style={{ textDecoration: 'none' }}>
          <span className="nl-b">[EXE]</span><span className="nl-r">cutables</span>
          <span className="nl-v">v26.5</span>
        </a>
        <nav id="primary-navigation" className={window.cls('nav-links', 'ap-nav-links', navOpen && 'is-open')}>
          <a href="/" onClick={() => setNavOpen(false)} className={window.cls(currentTab === 'store' && 'ap-nav-current')}>STORE</a>
          <a href="/account.html" onClick={() => setNavOpen(false)} className={window.cls(currentTab === 'account' && 'ap-nav-current')}>ACCOUNT</a>
          {isAdmin && (
            <a href="/admin.html" onClick={() => setNavOpen(false)} className={window.cls('ap-nav-admin', currentTab === 'admin' && 'ap-nav-current')}>ADMIN</a>
          )}
        </nav>
        <div className="nav-right">
          <button className="nav-mobile-toggle" type="button" aria-label={navOpen ? 'Close navigation menu' : 'Open navigation menu'} aria-expanded={navOpen} aria-controls="primary-navigation" onClick={() => setNavOpen(!navOpen)}>
            {navOpen ? <window.Icon.X/> : <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"/></svg>}
          </button>
          {user && (
            <button
              className="ap-logout"
              onClick={() => window.ExecAuth.logout().then(() => (location.href = '/'))}
              title="Log out"
            >
              <window.Icon.X/><span>LOG OUT</span>
            </button>
          )}
        </div>
      </header>
      <main className="ap-main">{children}</main>
    </div>
  );
}

function LoadingState() {
  return (
    <div className="ap-loading">
      <span className="dot pulse"></span>
      <span>LOADING…</span>
    </div>
  );
}

function EmptyPanel({ art, line, sub, cta }) {
  return (
    <div className="ap-empty">
      <window.CornerCuts size={10}/>
      <div className="ap-empty-art">{art}</div>
      <div className="ap-empty-line">{line}</div>
      {sub && <div className="ap-empty-sub">{sub}</div>}
      {cta && <div className="ap-empty-cta">{cta}</div>}
    </div>
  );
}

function LoggedOutCTA() {
  return (
    <div className="ap-locked">
      <window.CornerCuts size={12}/>
      <div className="ap-locked-art">⚿</div>
      <div className="ap-locked-line">LOGIN REQUIRED</div>
      <div className="ap-locked-sub">Authenticate with Discord to view your orders.</div>
      <button className="btn primary big" onClick={() => window.ExecAuth.login()}>
        <window.Icon.Lock/><span>LOG IN WITH DISCORD</span>
      </button>
    </div>
  );
}

function InlineBanner({ kind, onClose }) {
  useEffect(() => {
    if (kind !== 'ok') return;
    const t = setTimeout(onClose, 8000);
    return () => clearTimeout(t);
  }, [kind, onClose]);
  return (
    <div className={window.cls('ap-banner', 'ap-banner--' + kind)} onClick={onClose} role="status">
      <window.CornerCuts size={8}/>
      <span className={window.cls('dot', kind === 'ok' ? 'pulse' : '')}></span>
      <span className="ap-banner-text">
        {kind === 'ok'
          ? 'PAYMENT RECEIVED — your order is below'
          : 'CHECKOUT CANCELLED — no charge was made'}
      </span>
      <button className="ap-banner-x" onClick={onClose} aria-label="Dismiss">×</button>
    </div>
  );
}

// ---------- helpers ----------
function fmtDate(epoch) {
  if (!epoch) return '—';
  const d = new Date(epoch * 1000);
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }).toUpperCase();
}
function pendingKeyCount(orders) {
  if (!orders) return 0;
  let n = 0;
  for (const o of orders) {
    if (o.status !== 'paid') continue;
    for (const it of o.items) if (!it.has_key) n++;
  }
  return n;
}

// Expose shared shell pieces so admin.jsx can reuse them.
window.PageChrome = PageChrome;
window.HeroStrip  = HeroStrip;
window.StatColumn = StatColumn;
window.LoadingState = LoadingState;
window.EmptyPanel   = EmptyPanel;
window.LoggedOutCTA = LoggedOutCTA;
window.InlineBanner = InlineBanner;
window.fmtDate      = fmtDate;

const __root = document.getElementById('account-root');
if (__root) ReactDOM.createRoot(__root).render(<AccountApp/>);
