// /admin.html — admin orders panel. Customer-grouped by default with search +
// sort, flat list for per-order sorting. Shares page chrome with account.jsx
// (loaded headlessly before this file) via window.PageChrome/HeroStrip/etc.

const { useState, useEffect, useMemo, useCallback } = React;

const CUSTOMER_SORTS = [
  { id: 'last_order',  label: 'LAST ORDER (NEWEST)' },
  { id: 'first_order', label: 'LAST ORDER (OLDEST)' },
  { id: 'spent_desc',  label: 'SPENT ↓' },
  { id: 'spent_asc',   label: 'SPENT ↑' },
  { id: 'orders_desc', label: 'ORDER COUNT ↓' },
  { id: 'name_asc',    label: 'NAME A→Z' },
];
const FLAT_SORTS = [
  { id: 'newest',     label: 'NEWEST FIRST' },
  { id: 'oldest',     label: 'OLDEST FIRST' },
  { id: 'price_desc', label: 'PRICE ↓' },
  { id: 'price_asc',  label: 'PRICE ↑' },
];
const STATUS_FILTERS = [
  { id: 'all',     label: 'ALL' },
  { id: 'pending', label: 'PENDING' },
  { id: 'paid',    label: 'PAID' },
];

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

  // ORDERS | CATALOG — picked up from ?tab= so a refresh lands you where
  // you were. The legacy WEBSITE tab was merged into CATALOG (status now
  // lives on each product row + editor), so ?tab=website redirects here.
  // Mods land on CATALOG by default (no ORDERS visible). Admins default
  // to ORDERS.
  const initialTab = (() => {
    try {
      const t = new URL(location.href).searchParams.get('tab');
      if (t === 'catalog') return 'catalog';
      if (t === 'status' || t === 'website') return 'status';
      if (t === 'orders' && isAdmin) return 'orders';
    } catch {}
    return isAdmin ? 'orders' : 'catalog';
  })();
  const [tab, setTabState] = useState(initialTab);
  const setTab = (next) => {
    if (next === 'orders' && !isAdmin) return;   // mod safety net
    setTabState(next);
    try {
      const url = new URL(location.href);
      if (next === 'orders') url.searchParams.delete('tab');
      else url.searchParams.set('tab', next);
      history.replaceState({}, '', url.toString());
    } catch {}
  };

  const [orders, setOrders] = useState(null);
  const [search, setSearch] = useState('');
  const [statusFilter, setStatusFilter] = useState('all');
  const [grouped, setGrouped] = useState(true);
  const [customerSort, setCustomerSort] = useState('last_order');
  const [flatSort, setFlatSort] = useState('newest');

  const refresh = useCallback(async () => {
    try {
      const res = await fetch('/api/admin/orders', { credentials: 'same-origin' });
      if (!res.ok) throw new Error(res.status);
      const data = await res.json();
      setOrders(data.orders || []);
    } catch { setOrders([]); }
  }, []);

  useEffect(() => { if (isAdmin && tab === 'orders') refresh(); }, [isAdmin, refresh, tab]);

  const setKey = async (itemId, key, orderStatus) => {
    if (orderStatus !== 'paid') {
      if (!window.confirm(
        'This order has not been paid yet.\n\n' +
        'Are you sure you want to deliver a key now?\n\n' +
        'The customer will see "REVEAL KEY" even though their payment is incomplete.'
      )) return false;
    }
    try {
      const res = await fetch(`/api/admin/orders/${itemId}/key`, {
        method: 'POST', credentials: 'same-origin',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ key }),
      });
      if (!res.ok) { alert('Save failed: ' + res.status); return false; }
      await refresh();
      return true;
    } catch { alert('Save failed: network error'); return false; }
  };

  // ---------- Derived data ----------
  // IMPORTANT: every hook call MUST appear before the early-return gating
  // below. Calling useMemo (or any hook) only after a conditional return
  // makes the hook count differ between renders → "Rendered more hooks
  // than during the previous render" crash. Filters here are safe with
  // null/empty orders, so we compute unconditionally.
  const filteredOrders = useMemo(() => {
    let arr = orders || [];
    if (statusFilter !== 'all') arr = arr.filter((o) => o.status === statusFilter);
    if (search.trim()) {
      const q = search.trim().toLowerCase();
      arr = arr.filter((o) => {
        const fields = [o.username, o.global_name, o.discord_id].filter(Boolean).join(' ').toLowerCase();
        return fields.includes(q);
      });
    }
    return arr;
  }, [orders, statusFilter, search]);
  const customers = useMemo(() => groupByCustomer(filteredOrders, customerSort), [filteredOrders, customerSort]);
  const flatSorted = useMemo(() => sortFlat(filteredOrders, flatSort), [filteredOrders, flatSort]);

  const totalRevenue = (orders || [])
    .filter((o) => o.status === 'paid')
    .reduce((s, o) => s + (o.amount_cents || 0), 0);
  const pendingKeys = (orders || []).reduce((n, o) => {
    if (o.status !== 'paid') return n;
    return n + o.items.filter((it) => !it.product_key).length;
  }, 0);
  const uniqueCustomers = new Set((orders || []).map((o) => o.discord_id)).size;

  // ---------- Gating (all hooks above this line) ----------
  if (!ready) return <window.PageChrome user={null}><window.LoadingState/></window.PageChrome>;
  if (!user) return <window.PageChrome user={null}><window.LoggedOutCTA/></window.PageChrome>;
  if (!isAdmin && !isMod) {
    return (
      <window.PageChrome user={user} currentTab="admin">
        <div className="ap-locked">
          <window.CornerCuts size={12}/>
          <div className="ap-locked-art">✕</div>
          <div className="ap-locked-line">ACCESS DENIED</div>
          <div className="ap-locked-sub">Your account is not authorized for the admin panel.</div>
          <a href="/account.html" className="btn ghost"><span>↶ MY ACCOUNT</span></a>
        </div>
      </window.PageChrome>
    );
  }

  return (
    <window.PageChrome user={user} isAdmin={isAdmin} currentTab="admin">
      <div className="adm-subnav" role="tablist">
        {isAdmin && (
          <button
            className={window.cls('adm-subnav-btn', tab === 'orders'  && 'on')}
            onClick={() => setTab('orders')}
          >ORDERS + KEYS</button>
        )}
        <button
          className={window.cls('adm-subnav-btn', tab === 'catalog' && 'on')}
          onClick={() => setTab('catalog')}
        >CATALOG</button>
        <button
          className={window.cls('adm-subnav-btn', tab === 'status' && 'on')}
          onClick={() => setTab('status')}
        >STATUS</button>
        {!isAdmin && isMod && (
          <span className="adm-subnav-role">MODERATOR</span>
        )}
      </div>

      {tab === 'catalog' && <window.AdminCatalogApp/>}
      {tab === 'status' && <window.AdminSiteApp/>}
      {tab === 'orders' && <>
      <window.HeroStrip user={user} isAdmin={isAdmin}>
        <window.StatColumn label="ORDERS"        value={orders === null ? '—' : String(orders.length)}/>
        <window.StatColumn label="CUSTOMERS"     value={orders === null ? '—' : String(uniqueCustomers)}/>
        <window.StatColumn label="REVENUE"       value={orders === null ? '—' : window.fmt(totalRevenue / 100)}/>
        <window.StatColumn label="PENDING KEYS"  value={String(pendingKeys)} accent={pendingKeys > 0}/>
      </window.HeroStrip>

      <div className="ap-sec-head">
        <div className="ap-sec-tag">// ADMIN</div>
        <h2 className="ap-sec-title">ORDERS <span className="ap-sec-count">/ {filteredOrders.length}</span></h2>
        <div className="ap-sec-side">
          <button className="ap-icon-btn" onClick={refresh} title="Refresh">↻</button>
        </div>
      </div>

      <div className="adm-toolbar">
        <window.CornerCuts size={8}/>
        <label className="adm-search-wrap">
          <span className="adm-search-icon">⌕</span>
          <input
            className="adm-search"
            type="search"
            placeholder="search customer · name, handle, discord id"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
          />
        </label>
        <div className="adm-filter">
          <span className="adm-filter-label">STATUS</span>
          <select className="adm-select" value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
            {STATUS_FILTERS.map((s) => <option key={s.id} value={s.id}>{s.label}</option>)}
          </select>
        </div>
        <div className="adm-filter">
          <span className="adm-filter-label">SORT</span>
          <select
            className="adm-select"
            value={grouped ? customerSort : flatSort}
            onChange={(e) => grouped ? setCustomerSort(e.target.value) : setFlatSort(e.target.value)}
          >
            {(grouped ? CUSTOMER_SORTS : FLAT_SORTS).map((s) => <option key={s.id} value={s.id}>{s.label}</option>)}
          </select>
        </div>
        <div className="adm-view-toggle" role="tablist">
          <button className={window.cls('adm-view-btn', grouped && 'on')} onClick={() => setGrouped(true)}>BY CUSTOMER</button>
          <button className={window.cls('adm-view-btn', !grouped && 'on')} onClick={() => setGrouped(false)}>FLAT</button>
        </div>
      </div>

      {orders === null && <window.LoadingState/>}
      {orders !== null && filteredOrders.length === 0 && (
        <window.EmptyPanel art="[ ∅ ]" line="NO MATCHES" sub="try a different search or status filter"/>
      )}

      {orders !== null && filteredOrders.length > 0 && grouped && (
        <div className="adm-customers">
          {customers.map((c) => <CustomerCard key={c.discord_id} customer={c} onSetKey={setKey}/>)}
        </div>
      )}
      {orders !== null && filteredOrders.length > 0 && !grouped && (
        <div className="ap-orders">
          {flatSorted.map((o) => <FlatOrder key={o.id} order={o} onSetKey={setKey}/>)}
        </div>
      )}
      </>}
    </window.PageChrome>
  );
}

// ---------- Customer card ----------
function CustomerCard({ customer, onSetKey }) {
  const [open, setOpen] = useState(false);
  return (
    <section className={window.cls('adm-customer', open && 'adm-customer--open')}>
      <window.CornerCuts size={10}/>
      <button className="adm-customer-head" onClick={() => setOpen(!open)} aria-expanded={open}>
        <div className="adm-customer-avatar-frame">
          {customer.avatar_url
            ? <img className="adm-customer-avatar" src={customer.avatar_url} alt=""/>
            : <span className="adm-customer-avatar adm-customer-avatar--ph">{(customer.display_name[0] || '?').toUpperCase()}</span>}
        </div>
        <div className="adm-customer-id">
          <div className="adm-customer-name">{customer.display_name}</div>
          <div className="adm-customer-handle">@{customer.username || customer.discord_id}</div>
        </div>
        <div className="adm-customer-stats">
          <Pair label="ORDERS"     value={customer.orders.length}/>
          <Pair label="SPENT"      value={window.fmt(customer.total_spent / 100)}/>
          <Pair label="LAST ORDER" value={window.fmtDate(customer.last_order_at)}/>
          {customer.pending_key_count > 0 && (
            <span className="adm-customer-flag" title="Paid orders awaiting key delivery">
              <span className="dot pulse"></span>
              {customer.pending_key_count} PENDING KEY{customer.pending_key_count === 1 ? '' : 'S'}
            </span>
          )}
        </div>
        <span className={window.cls('adm-chev', open && 'adm-chev--open')} aria-hidden>▾</span>
      </button>
      {open && (
        <div className="adm-customer-body">
          {customer.orders.map((o) => <FlatOrder key={o.id} order={o} onSetKey={onSetKey} hideCustomer/>)}
        </div>
      )}
    </section>
  );
}

function Pair({ label, value }) {
  return (
    <span className="adm-pair">
      <span className="adm-pair-l">{label}</span>
      <span className="adm-pair-v">{value}</span>
    </span>
  );
}

// ---------- Flat order row (used in customer body AND flat view) ----------
function FlatOrder({ order, onSetKey, hideCustomer }) {
  const dotClass = order.status === 'paid' ? 'pulse' : '';
  return (
    <article className={window.cls('ap-order', 'ap-order--' + order.status, 'adm-order-card')}>
      <window.CornerCuts size={8}/>
      <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">#{String(order.id).padStart(4, '0')}</span>
        {!hideCustomer && (
          <span className="adm-order-customer">
            {order.global_name || order.username || order.discord_id}
          </span>
        )}
        <span className="ap-order-date">{window.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) => <KeyEditor key={it.id} item={it} orderStatus={order.status} onSetKey={onSetKey}/>)}
      </ul>
    </article>
  );
}

// ---------- Key editor (set, edit, with confirms) ----------
function KeyEditor({ item, orderStatus, onSetKey }) {
  const [editing, setEditing] = useState(false);
  const [val, setVal] = useState('');
  const delivered = !!item.product_key;

  const begin = () => { setVal(item.product_key || ''); setEditing(true); };
  const cancel = () => { setEditing(false); setVal(''); };
  const save = async () => {
    const key = val.trim();
    if (!key) return;
    if (delivered && key === item.product_key) { setEditing(false); return; }
    if (delivered) {
      if (!window.confirm(
        'OVERWRITE an already-delivered key?\n\n' +
        `Current: ${item.product_key}\n` +
        `New:     ${key}\n\n` +
        'The customer will see only the new key.'
      )) return;
    }
    const ok = await onSetKey(item.id, key, orderStatus);
    if (ok) { setEditing(false); setVal(''); }
  };

  return (
    <li className="ap-item">
      <div className="ap-item-meta">
        <span className="ap-item-cat">{item.product_category}</span>
        <span className="ap-item-name">{item.product_name}</span>
        <span className="ap-item-tier">· {String(item.tier).toUpperCase()}</span>
      </div>
      <div className="ap-item-action">
        {!editing && delivered && (
          <div className="adm-key-row">
            <code className="ap-key ap-key--delivered" title={`Delivered ${window.fmtDate(item.key_delivered_at)}`}>{item.product_key}</code>
            <button className="adm-mini-btn" onClick={begin}>EDIT</button>
          </div>
        )}
        {!editing && !delivered && (
          <button
            className={window.cls('adm-set-btn', orderStatus !== 'paid' && 'adm-set-btn--warn')}
            onClick={begin}
          >
            {orderStatus === 'paid' ? '＋ SET KEY' : '＋ SET KEY (PENDING)'}
          </button>
        )}
        {editing && (
          <div className="adm-key-edit">
            <input
              type="text"
              className="adm-key-input"
              placeholder="paste key…"
              value={val}
              autoFocus
              onChange={(e) => setVal(e.target.value)}
              onKeyDown={(e) => { if (e.key === 'Enter') save(); if (e.key === 'Escape') cancel(); }}
            />
            <button className="adm-mini-btn adm-mini-btn--primary" onClick={save} disabled={!val.trim()}>SAVE</button>
            <button className="adm-mini-btn" onClick={cancel}>CANCEL</button>
          </div>
        )}
      </div>
    </li>
  );
}

// ---------- helpers ----------
function groupByCustomer(orders, sortId) {
  const map = new Map();
  for (const o of orders) {
    const id = o.discord_id;
    let c = map.get(id);
    if (!c) {
      c = {
        discord_id: id,
        username: o.username,
        display_name: o.global_name || o.username || id,
        avatar_url: o.avatar_url,
        orders: [],
        total_spent: 0,
        last_order_at: 0,
        first_order_at: Infinity,
        pending_key_count: 0,
      };
      map.set(id, c);
    }
    c.orders.push(o);
    if (o.status === 'paid') c.total_spent += (o.amount_cents || 0);
    c.last_order_at  = Math.max(c.last_order_at,  o.created_at || 0);
    c.first_order_at = Math.min(c.first_order_at, o.created_at || Infinity);
    if (o.status === 'paid') for (const it of o.items) if (!it.product_key) c.pending_key_count++;
  }
  const arr = Array.from(map.values());
  for (const c of arr) c.orders.sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
  const cmp = {
    last_order:  (a, b) => b.last_order_at - a.last_order_at,
    first_order: (a, b) => a.last_order_at - b.last_order_at,
    spent_desc:  (a, b) => b.total_spent - a.total_spent,
    spent_asc:   (a, b) => a.total_spent - b.total_spent,
    orders_desc: (a, b) => b.orders.length - a.orders.length,
    name_asc:    (a, b) => (a.display_name || '').localeCompare(b.display_name || ''),
  }[sortId] || ((a, b) => b.last_order_at - a.last_order_at);
  arr.sort(cmp);
  return arr;
}

function sortFlat(orders, sortId) {
  const arr = orders.slice();
  const cmp = {
    newest:     (a, b) => (b.created_at || 0) - (a.created_at || 0),
    oldest:     (a, b) => (a.created_at || 0) - (b.created_at || 0),
    price_desc: (a, b) => (b.amount_cents || 0) - (a.amount_cents || 0),
    price_asc:  (a, b) => (a.amount_cents || 0) - (b.amount_cents || 0),
  }[sortId] || ((a, b) => (b.created_at || 0) - (a.created_at || 0));
  arr.sort(cmp);
  return arr;
}

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