// admin.jsx — in-app admin dashboard.
// Gated by ADMIN_EMAILS in app.jsx. All writes go through the admin* callables
// in functions/index.js (also enforced by firestore.rules / storage.rules).
//
// Window mounts <AdminContent> with seven top-tab panels:
//   orders     → AdminOrdersPanel       — list + fulfillment update
//   customers  → AdminCustomersPanel    — list + per-customer drawer
//   products   → AdminProductEditPanel  — catalog + details editor
//   order      → AdminDisplayOrderPanel — drag-to-reorder + smart sort presets
//   coupons    → AdminCouponsPanel      — promo-code CRUD (Firestore coupons/)
//   stats      → AdminAnalyticsPanel    — revenue / AOV / top products / chart
//   ai         → AdminGeneratorPanel    — AI product design + draft inbox

// ─── Shared primitives ────────────────────────────────────────────────────
const ADMIN_TAB_COLOR = {
  orders: 'var(--ap-cyan)',
  customers: 'var(--ap-violet)',
  products: 'var(--ap-pink)',
  order: 'var(--ap-orange)',
  stats: 'var(--ap-yellow)',
  ai: 'var(--ap-mint)',
  coupons: '#c8ff5f',
};

function AdminTopTabs({ active, onChange }) {
  const tabs = [
    { id: 'orders',    label: '▣ Orders' },
    { id: 'customers', label: '◆ Users'  },
    { id: 'products',  label: '✎ Edit'   },
    { id: 'order',     label: '⇅ Order'  },
    { id: 'coupons',   label: '% Codes'  },
    { id: 'stats',     label: '▦ Stats'  },
    { id: 'ai',        label: '✦ AI'     },
  ];
  return (
    <div style={{
      display: 'flex', gap: 0, padding: '0 2px',
      borderBottom: '1px solid #000', background: 'var(--w95-bg)',
    }}>
      {tabs.map((t) => {
        const isActive = t.id === active;
        return (
          <button key={t.id} onClick={() => onChange(t.id)} style={{
            flex: 1, minWidth: 0,
            border: '1px solid', borderColor: isActive
              ? '#fff #000 transparent #fff'
              : '#fff #000 #000 #fff',
            borderBottom: isActive ? 'none' : '1px solid #000',
            background: isActive ? 'var(--w95-bg)' : 'var(--w95-bg-light)',
            padding: '4px 2px', marginRight: 1,
            marginBottom: isActive ? -1 : 0,
            fontFamily: 'inherit', fontSize: 9, fontWeight: isActive ? 'bold' : 'normal',
            cursor: 'pointer', color: '#000', whiteSpace: 'nowrap',
            overflow: 'hidden', textOverflow: 'ellipsis',
          }}>{t.label}</button>
        );
      })}
    </div>
  );
}

function AdminPanelHeader({ tabId, title, sub, right }) {
  return (
    <div style={{
      background: ADMIN_TAB_COLOR[tabId] || 'var(--ap-pink)',
      border: '1px solid #000', padding: '3px 6px',
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      marginBottom: 6,
    }}>
      <div>
        <div style={{ fontWeight: 'bold', fontSize: 11, color: '#000' }}>{title}</div>
        {sub && <div style={{ fontSize: 9, color: '#222' }}>{sub}</div>}
      </div>
      {right}
    </div>
  );
}

function AdminStatTile({ label, value, sub, color }) {
  return (
    <div className="w95-inset" style={{
      flex: 1, minWidth: 0, padding: '4px 6px', background: '#fff',
      display: 'flex', flexDirection: 'column',
    }}>
      <div style={{ fontSize: 8, color: '#444', textTransform: 'uppercase', letterSpacing: '0.05em' }}>{label}</div>
      <div style={{ fontSize: 14, fontWeight: 'bold', color: color || '#000', lineHeight: 1, marginTop: 2 }}>{value}</div>
      {sub && <div style={{ fontSize: 8, color: '#666', marginTop: 1 }}>{sub}</div>}
    </div>
  );
}

function AdminEmpty({ children }) {
  return (
    <div className="w95-inset" style={{
      background: '#fff', padding: 12, textAlign: 'center',
      fontSize: 10, color: '#666', marginTop: 8,
    }}>{children}</div>
  );
}

function AdminLoader({ label = 'loading…' }) {
  return (
    <div style={{ padding: 12, textAlign: 'center', fontSize: 10, color: '#444' }}>
      <Hourglass size={20} /> <div style={{ marginTop: 4 }}>{label}</div>
    </div>
  );
}

function fmtINR(n) {
  if (typeof n !== 'number' || !isFinite(n)) return '₹0';
  return '₹' + n.toLocaleString('en-IN');
}
function fmtDate(ms) {
  if (!ms) return '—';
  const d = new Date(ms);
  return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short' }) +
    ' ' + d.toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit' });
}
function fmtDay(ms) {
  if (!ms) return '—';
  return new Date(ms).toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: '2-digit' });
}

function adminTier(spend) {
  if (spend >= 5000) return { label: 'Platinum', color: '#6a2bd9' };
  if (spend >= 2000) return { label: 'Gold',     color: '#ff7a18' };
  if (spend >= 500)  return { label: 'Silver',   color: '#888'    };
  return { label: 'New', color: '#444' };
}

// ─── AdminContent — shell ─────────────────────────────────────────────────
function AdminContent({ user, onToast, onView }) {
  const [tab, setTab] = React.useState('orders');
  const [savedAt, setSavedAt] = React.useState(null);
  const flash = (msg) => { setSavedAt(Date.now()); onToast && onToast(msg); };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', padding: 0, margin: -8 /* offset .w95-content padding */ }}>
      <div style={{ padding: '6px 6px 0' }}>
        <AdminTopTabs active={tab} onChange={setTab} />
      </div>
      <div style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: 6 }} className="w95-scroll">
        {tab === 'orders'    && <AdminOrdersPanel onSaved={flash} />}
        {tab === 'customers' && <AdminCustomersPanel onSaved={flash} />}
        {tab === 'products'  && <AdminProductEditPanel onSaved={flash} onView={onView} />}
        {tab === 'order'     && <AdminDisplayOrderPanel onSaved={flash} />}
        {tab === 'coupons'   && <AdminCouponsPanel onSaved={flash} />}
        {tab === 'stats'     && <AdminAnalyticsPanel />}
        {tab === 'ai'        && <AdminGeneratorPanel onSaved={flash} />}
      </div>
      <div style={{
        borderTop: '1px solid var(--w95-highlight)',
        boxShadow: 'inset 0 1px 0 var(--w95-shadow)',
        padding: '3px 6px', display: 'flex', gap: 6,
        fontSize: 9, fontFamily: 'monospace', background: 'var(--w95-bg)',
        whiteSpace: 'nowrap', overflow: 'hidden',
      }}>
        <span style={{ color: '#a00', fontWeight: 'bold' }}>ADMIN</span>
        <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', color: '#000' }}>
          {(user && user.email) || '—'}
        </span>
        <span style={{ color: '#444' }}>
          {savedAt ? `saved ${Math.max(0, Math.floor((Date.now() - savedAt) / 1000))}s ago` : 'idle'}
        </span>
      </div>
    </div>
  );
}

// ─── Panel 1 — Orders ─────────────────────────────────────────────────────
const ORDER_STATUSES = ['all', 'created', 'paid', 'failed', 'refunded'];
const ORDER_FULFILLMENTS = ['all', 'pending', 'processing', 'shipped', 'delivered', 'cancelled'];

function AdminOrdersPanel({ onSaved }) {
  const [status, setStatus] = React.useState('all');
  const [fulfillment, setFulfillment] = React.useState('all');
  const [search, setSearch] = React.useState('');
  const [orders, setOrders] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');
  const [active, setActive] = React.useState(null);

  const load = React.useCallback(() => {
    setLoading(true); setError('');
    window.adminListOrders({ status, fulfillment, search, limit: 50 })
      .then((res) => { setOrders(res.orders || []); setLoading(false); })
      .catch((e) => { setError(e.message || 'Failed to load orders.'); setLoading(false); });
  }, [status, fulfillment, search]);

  React.useEffect(() => { load(); }, [load]);

  return (
    <>
      <AdminPanelHeader tabId="orders" title="Orders dashboard"
        sub={`${orders.length} match${orders.length === 1 ? '' : 'es'}`}
        right={<button className="w95-btn" style={{ minWidth: 0, padding: '1px 6px', fontSize: 9 }}
          onClick={load}>↻</button>} />
      <div style={{ display: 'flex', gap: 4, marginBottom: 6, flexWrap: 'wrap' }}>
        <select className="w95-input" value={status} onChange={(e) => setStatus(e.target.value)}
          style={{ fontSize: 10, padding: 1 }}>
          {ORDER_STATUSES.map((s) => <option key={s} value={s}>status: {s}</option>)}
        </select>
        <select className="w95-input" value={fulfillment} onChange={(e) => setFulfillment(e.target.value)}
          style={{ fontSize: 10, padding: 1 }}>
          {ORDER_FULFILLMENTS.map((s) => <option key={s} value={s}>fill: {s}</option>)}
        </select>
        <input className="w95-input" value={search} placeholder="🔍 receipt / email"
          onChange={(e) => setSearch(e.target.value)}
          style={{ flex: '1 1 100px', fontSize: 10 }} />
      </div>
      {error && <AdminEmpty>{error}</AdminEmpty>}
      {loading ? <AdminLoader label="fetching orders…" /> : (
        orders.length === 0 ? <AdminEmpty>no orders match these filters</AdminEmpty> : (
          <div className="w95-inset" style={{ background: '#fff', maxHeight: 'none' }}>
            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 9 }}>
              <thead>
                <tr style={{ background: 'var(--w95-title)', color: '#fff' }}>
                  <th style={thStyle}>Receipt</th>
                  <th style={thStyle}>Date</th>
                  <th style={thStyle}>Customer</th>
                  <th style={{ ...thStyle, textAlign: 'right' }}>Total</th>
                  <th style={thStyle}>Status</th>
                </tr>
              </thead>
              <tbody>
                {orders.map((o, i) => (
                  <tr key={o.id} onClick={() => setActive(o)} style={{
                    background: i % 2 ? '#f0f0f0' : '#fff', cursor: 'pointer',
                  }}>
                    <td style={tdStyle}>{o.receipt || o.id.slice(-8)}</td>
                    <td style={tdStyle}>{fmtDay(o.createdAt)}</td>
                    <td style={tdStyle}>
                      {o.customer && o.customer.name ? o.customer.name.split(' ')[0] : '—'}
                    </td>
                    <td style={{ ...tdStyle, textAlign: 'right' }}>{fmtINR(o.total)}</td>
                    <td style={tdStyle}>
                      <StatusPill status={o.status} fulfillment={o.fulfillment} />
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )
      )}
      {active && (
        <AdminOrderDetail order={active}
          onClose={() => setActive(null)}
          onSaved={(msg) => { onSaved(msg); load(); setActive(null); }} />
      )}
    </>
  );
}

const thStyle = { padding: '3px 4px', textAlign: 'left', fontSize: 9, fontWeight: 'bold', borderBottom: '1px solid #000' };
const tdStyle = { padding: '3px 4px', fontSize: 9, borderBottom: '1px solid #ddd' };

function StatusPill({ status, fulfillment }) {
  const c = status === 'paid' ? '#0a8a0a'
          : status === 'failed' ? '#a00'
          : status === 'refunded' ? '#888'
          : '#bb6a00';
  const fc = fulfillment === 'delivered' ? '#0a8a0a'
           : fulfillment === 'shipped'   ? '#0066c0'
           : fulfillment === 'cancelled' ? '#a00'
           : '#444';
  return (
    <span style={{ display: 'inline-flex', flexDirection: 'column', gap: 1, lineHeight: 1.1 }}>
      <span style={{ fontWeight: 'bold', color: '#fff', background: c, padding: '0 3px', fontSize: 8 }}>
        {status || '—'}
      </span>
      <span style={{ color: fc, fontSize: 8 }}>{fulfillment || '—'}</span>
    </span>
  );
}

function AdminOrderDetail({ order, onClose, onSaved }) {
  const [fulfillment, setFulfillment] = React.useState(order.fulfillment || 'pending');
  const [tracking, setTracking] = React.useState(order.trackingNumber || '');
  const [note, setNote] = React.useState(order.adminNote || '');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');

  const save = async () => {
    setBusy(true); setErr('');
    try {
      await window.adminUpdateOrder({
        orderId: order.id,
        fulfillment,
        trackingNumber: tracking,
        adminNote: note,
      });
      onSaved(`Order ${order.receipt || ''} updated`);
    } catch (e) {
      setErr(e.message || 'Failed.');
      setBusy(false);
    }
  };

  return (
    <div style={{
      position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.5)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center', zIndex: 50,
    }} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', maxHeight: '85%', overflow: 'auto',
        background: 'var(--w95-bg)', border: '2px solid', borderColor: '#fff #000 #000 #fff',
        padding: 8, fontSize: 10,
      }} className="w95-scroll">
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
          <div style={{ fontWeight: 'bold', fontSize: 12 }}>{order.receipt || order.id}</div>
          <button className="w95-btn" onClick={onClose}
            style={{ minWidth: 0, padding: '1px 6px' }}>✕</button>
        </div>
        <div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
          <AdminStatTile label="Total"  value={fmtINR(order.total)} color="#ff3eb5" />
          <AdminStatTile label="Items"  value={order.items.reduce((s, l) => s + l.qty, 0)} />
          <AdminStatTile label="Status" value={order.status} />
        </div>
        <div className="w95-inset" style={{ background: '#fff', padding: 6, marginBottom: 6 }}>
          <div style={{ fontWeight: 'bold', marginBottom: 2 }}>Customer</div>
          {order.customer ? (
            <>
              <div>{order.customer.name} · {order.customer.phone || '—'}</div>
              <div style={{ color: '#444' }}>{order.customer.email}</div>
              <div style={{ color: '#444' }}>
                {[order.customer.address, order.customer.city, order.customer.pincode].filter(Boolean).join(', ')}
              </div>
            </>
          ) : <div>—</div>}
        </div>
        <div className="w95-inset" style={{ background: '#fff', padding: 6, marginBottom: 6 }}>
          <div style={{ fontWeight: 'bold', marginBottom: 2 }}>Items</div>
          {order.items.map((l, i) => (
            <div key={i} style={{ display: 'flex', justifyContent: 'space-between', borderBottom: '1px dotted #ddd', padding: '2px 0' }}>
              <span>{l.qty}× {l.name}</span>
              <span>{fmtINR(l.price * l.qty)}</span>
            </div>
          ))}
          {order.coupon && (
            <div style={{ color: '#0a8a0a', marginTop: 3 }}>
              Coupon {order.coupon.code} −{fmtINR(order.discount)}
            </div>
          )}
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 3 }}>
            <span>Shipping</span><span>{fmtINR(order.shipping)}</span>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', fontWeight: 'bold' }}>
            <span>Total</span><span>{fmtINR(order.total)}</span>
          </div>
        </div>

        <div style={{ fontWeight: 'bold', fontSize: 11, marginBottom: 4 }}>Update fulfillment</div>
        <div style={{ display: 'flex', gap: 4, alignItems: 'center', marginBottom: 4 }}>
          <span style={{ width: 60, fontSize: 9 }}>Status</span>
          <select className="w95-input" value={fulfillment} onChange={(e) => setFulfillment(e.target.value)}
            style={{ flex: 1, fontSize: 10 }}>
            {ORDER_FULFILLMENTS.filter((s) => s !== 'all').map((s) => (
              <option key={s} value={s}>{s}</option>
            ))}
          </select>
        </div>
        <div style={{ display: 'flex', gap: 4, alignItems: 'center', marginBottom: 4 }}>
          <span style={{ width: 60, fontSize: 9 }}>Tracking</span>
          <input className="w95-input" value={tracking} onChange={(e) => setTracking(e.target.value)}
            placeholder="AWB / tracking no."
            style={{ flex: 1, fontSize: 10 }} />
        </div>
        <div style={{ display: 'flex', gap: 4, alignItems: 'flex-start', marginBottom: 6 }}>
          <span style={{ width: 60, fontSize: 9, paddingTop: 4 }}>Note</span>
          <textarea className="w95-input" value={note} onChange={(e) => setNote(e.target.value)}
            rows={2} style={{ flex: 1, fontSize: 10, fontFamily: 'inherit' }} />
        </div>
        {err && <div style={{ color: '#a00', fontSize: 9, marginBottom: 4 }}>{err}</div>}
        <div style={{ display: 'flex', gap: 4 }}>
          <button className="w95-btn" disabled={busy} onClick={save} style={{
            flex: 1, background: 'var(--ap-pink-deep)', color: '#fff', fontWeight: 'bold',
          }}>{busy ? 'Saving…' : 'Save'}</button>
          <button className="w95-btn" disabled={busy} onClick={onClose}>Cancel</button>
        </div>
      </div>
    </div>
  );
}

// ─── Panel 2 — Customers ─────────────────────────────────────────────────
function AdminCustomersPanel({ onSaved }) {
  const [sort, setSort] = React.useState('lifetimeSpend');
  const [data, setData] = React.useState({ customers: [], stats: {} });
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');
  const [active, setActive] = React.useState(null);

  React.useEffect(() => {
    setLoading(true); setError('');
    window.adminListCustomers({ sort, limit: 100 })
      .then((res) => { setData(res); setLoading(false); })
      .catch((e) => { setError(e.message || 'Failed.'); setLoading(false); });
  }, [sort]);

  const stats = data.stats || {};
  return (
    <>
      <AdminPanelHeader tabId="customers" title="Customers"
        sub={`${stats.total || 0} accounts · ${stats.paying || 0} paying`} />
      <div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
        <AdminStatTile label="Customers" value={stats.total || 0} />
        <AdminStatTile label="Paying"    value={stats.paying || 0} color="#0a8a0a" />
        <AdminStatTile label="Repeat"    value={`${stats.repeatRate || 0}%`} color="#ff3eb5" />
      </div>
      <div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
        <span style={{ fontSize: 9, alignSelf: 'center' }}>Sort by</span>
        <select className="w95-input" value={sort} onChange={(e) => setSort(e.target.value)}
          style={{ fontSize: 10, flex: 1 }}>
          <option value="lifetimeSpend">Lifetime spend</option>
          <option value="orderCount">Order count</option>
          <option value="updatedAt">Most recent</option>
        </select>
      </div>
      {error && <AdminEmpty>{error}</AdminEmpty>}
      {loading ? <AdminLoader label="loading customers…" /> : (
        data.customers.length === 0 ? <AdminEmpty>no customers yet</AdminEmpty> : (
          <div className="w95-inset" style={{ background: '#fff' }}>
            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 9 }}>
              <thead>
                <tr style={{ background: 'var(--w95-title)', color: '#fff' }}>
                  <th style={thStyle}>Customer</th>
                  <th style={thStyle}>Tier</th>
                  <th style={{ ...thStyle, textAlign: 'right' }}>Orders</th>
                  <th style={{ ...thStyle, textAlign: 'right' }}>Spend</th>
                </tr>
              </thead>
              <tbody>
                {data.customers.map((c, i) => {
                  const tier = adminTier(c.lifetimeSpend);
                  return (
                    <tr key={c.uid} onClick={() => setActive(c)} style={{
                      background: i % 2 ? '#f0f0f0' : '#fff', cursor: 'pointer',
                    }}>
                      <td style={tdStyle}>
                        <div>{c.name || c.email || c.uid.slice(0, 10) + '…'}</div>
                        {c.email && c.name && (
                          <div style={{ color: '#666', fontSize: 8 }}>{c.email}</div>
                        )}
                      </td>
                      <td style={tdStyle}>
                        <span style={{
                          color: '#fff', background: tier.color,
                          padding: '0 4px', fontSize: 8, fontWeight: 'bold',
                        }}>{tier.label}</span>
                      </td>
                      <td style={{ ...tdStyle, textAlign: 'right' }}>{c.orderCount}</td>
                      <td style={{ ...tdStyle, textAlign: 'right' }}>{fmtINR(c.lifetimeSpend)}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )
      )}
      {active && <AdminCustomerDetail uid={active.uid}
        seed={active} onClose={() => setActive(null)} />}
    </>
  );
}

function AdminCustomerDetail({ uid, seed, onClose }) {
  const [data, setData] = React.useState(null);
  const [error, setError] = React.useState('');
  React.useEffect(() => {
    window.adminGetCustomer(uid)
      .then(setData)
      .catch((e) => setError(e.message || 'Failed.'));
  }, [uid]);

  return (
    <div style={{
      position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.5)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center', zIndex: 50,
    }} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', maxHeight: '85%', overflow: 'auto',
        background: 'var(--w95-bg)', border: '2px solid', borderColor: '#fff #000 #000 #fff',
        padding: 8, fontSize: 10,
      }} className="w95-scroll">
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
          <div style={{ fontWeight: 'bold', fontSize: 12, overflow: 'hidden', textOverflow: 'ellipsis' }}>
            {(data && data.user && (seed.name || seed.email)) || (seed.name || seed.email || uid.slice(0, 12))}
          </div>
          <button className="w95-btn" onClick={onClose}
            style={{ minWidth: 0, padding: '1px 6px' }}>✕</button>
        </div>
        {error ? <AdminEmpty>{error}</AdminEmpty> :
         !data ? <AdminLoader /> : (
          <>
            <div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
              <AdminStatTile label="Spend"  value={fmtINR(data.user.lifetimeSpend)} color="#ff3eb5" />
              <AdminStatTile label="Orders" value={data.user.orderCount} />
              <AdminStatTile label="Points" value={data.user.points} color="#ff7a18" />
            </div>
            <div className="w95-inset" style={{ background: '#fff', padding: 6, marginBottom: 6 }}>
              <div style={{ fontSize: 9 }}>
                <div>UID: <span style={{ fontFamily: 'monospace' }}>{uid}</span></div>
                <div>Email: {seed.email || '—'}</div>
                <div>Wishlist: {data.user.wishlist.length} items</div>
                <div>Notif: {data.user.notif
                  ? Object.entries(data.user.notif).filter(([, v]) => v).map(([k]) => k).join(', ') || 'none'
                  : '—'}</div>
              </div>
            </div>
            <div style={{ fontWeight: 'bold', fontSize: 11, marginBottom: 4 }}>
              Order history ({data.orders.length})
            </div>
            {data.orders.length === 0 ? <AdminEmpty>no orders yet</AdminEmpty> : (
              <div className="w95-inset" style={{ background: '#fff' }}>
                <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 9 }}>
                  <thead>
                    <tr style={{ background: 'var(--w95-title)', color: '#fff' }}>
                      <th style={thStyle}>Receipt</th>
                      <th style={thStyle}>Date</th>
                      <th style={{ ...thStyle, textAlign: 'right' }}>Total</th>
                      <th style={thStyle}>Status</th>
                    </tr>
                  </thead>
                  <tbody>
                    {data.orders.map((o, i) => (
                      <tr key={o.id} style={{ background: i % 2 ? '#f0f0f0' : '#fff' }}>
                        <td style={tdStyle}>{o.receipt || o.id.slice(-8)}</td>
                        <td style={tdStyle}>{fmtDay(o.createdAt)}</td>
                        <td style={{ ...tdStyle, textAlign: 'right' }}>{fmtINR(o.total)}</td>
                        <td style={tdStyle}>
                          <StatusPill status={o.status} fulfillment={o.fulfillment} />
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </>
        )}
      </div>
    </div>
  );
}

// ─── Panel 3 — Product details editor ────────────────────────────────────
function AdminProductEditPanel({ onSaved, onView }) {
  // Admin sees active + archived together so hidden items can be unhidden.
  // `tick` forces a re-read of the globals after any mutation refreshes them.
  const [tick, setTick] = React.useState(0);
  const allProducts = React.useMemo(
    () => [...(window.PRODUCTS || []), ...(window.ARCHIVED_PRODUCTS || [])],
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [tick]
  );
  const collections = window.COLLECTIONS || [];
  const [q, setQ] = React.useState('');
  const [statusFilter, setStatusFilter] = React.useState('all'); // all | active | archived
  const [selectedSlugs, setSelectedSlugs] = React.useState(() => new Set());
  const [slug, setSlug] = React.useState(null);
  const [editTab, setEditTab] = React.useState('catalog');
  const [bulkBusy, setBulkBusy] = React.useState(false);

  const refresh = React.useCallback(async () => {
    try {
      if (typeof window.loadCatalog === 'function') await window.loadCatalog();
    } catch (e) {
      console.warn('[admin] refresh failed', e);
    }
    setTick((n) => n + 1);
  }, []);

  const query = q.trim().toLowerCase();
  const filtered = allProducts.filter((p) => {
    const isArchived = p.status === 'archived';
    if (statusFilter === 'active' && isArchived) return false;
    if (statusFilter === 'archived' && !isArchived) return false;
    if (!query) return true;
    return (p.name || '').toLowerCase().includes(query)
      || (p.slug || '').toLowerCase().includes(query);
  });

  const toggleSelect = (s) => {
    setSelectedSlugs((prev) => {
      const next = new Set(prev);
      if (next.has(s)) next.delete(s); else next.add(s);
      return next;
    });
  };
  const selectAllVisible = () => {
    setSelectedSlugs(new Set(filtered.map((p) => p.slug)));
  };
  const clearSelection = () => setSelectedSlugs(new Set());
  const allVisibleSelected = filtered.length > 0
    && filtered.every((p) => selectedSlugs.has(p.slug));

  const runBulk = async (action) => {
    const slugs = Array.from(selectedSlugs);
    if (!slugs.length) return;
    if (action === 'delete') {
      if (!window.confirm(`Permanently delete ${slugs.length} product(s)? This cannot be undone.`)) return;
    }
    setBulkBusy(true);
    let ok = 0, failed = 0;
    for (const s of slugs) {
      try {
        if (action === 'hide')   await window.adminUpdateProduct(s, { status: 'archived' });
        if (action === 'show')   await window.adminUpdateProduct(s, { status: 'active' });
        if (action === 'delete') await window.adminDeleteProduct(s);
        ok += 1;
      } catch (e) {
        console.warn('[admin bulk]', s, e);
        failed += 1;
      }
    }
    await refresh();
    clearSelection();
    setBulkBusy(false);
    const verb = action === 'hide' ? 'hidden' : action === 'show' ? 'shown' : 'deleted';
    const msg = failed
      ? `${ok} ${verb}, ${failed} failed`
      : `${ok} product${ok === 1 ? '' : 's'} ${verb}`;
    onSaved(msg);
  };

  if (slug) {
    const product = allProducts.find((p) => p.slug === slug);
    if (!product) {
      return <AdminEmpty>Product gone — refresh.</AdminEmpty>;
    }
    return (
      <>
        <AdminPanelHeader tabId="products" title={product.name} sub={product.slug}
          right={<button className="w95-btn" style={{ minWidth: 0, padding: '1px 6px', fontSize: 9 }}
            onClick={() => setSlug(null)}>← back</button>} />
        <div style={{ display: 'flex', gap: 0, borderBottom: '1px solid #888', marginBottom: 6 }}>
          {[['catalog', 'Catalog row'], ['details', 'Details doc'], ['preview', 'Preview']].map(([k, l]) => (
            <button key={k} onClick={() => setEditTab(k)} style={{
              flex: 1, padding: '3px 2px', fontSize: 9, cursor: 'pointer',
              background: editTab === k ? '#fff' : 'var(--w95-bg)',
              border: '1px solid', borderColor: editTab === k ? '#fff #888 #fff #fff' : '#fff #888 #888 #fff',
              borderBottom: editTab === k ? 'none' : '1px solid #888',
              fontFamily: 'inherit', fontWeight: editTab === k ? 'bold' : 'normal',
            }}>{l}</button>
          ))}
        </div>
        {editTab === 'catalog' && (
          <AdminCatalogEditor product={product} collections={collections}
            onSaved={onSaved}
            onDeleted={async () => { await refresh(); setSlug(null); }}
            onStatusChanged={refresh} />
        )}
        {editTab === 'details' && <AdminDetailsEditor product={product} onSaved={onSaved} />}
        {editTab === 'preview' && (
          <div style={{ background: 'var(--w95-bg)' }}>
            <ProductDetailContent product={product} onAdd={() => {}} wishlist={[]} />
            {onView && (
              <button className="w95-btn" onClick={() => onView(product)}
                style={{ width: '100%', marginTop: 6 }}>
                Open in product window
              </button>
            )}
          </div>
        )}
      </>
    );
  }

  const activeCount = allProducts.filter((p) => p.status !== 'archived').length;
  const archivedCount = allProducts.filter((p) => p.status === 'archived').length;

  const chip = (id, label) => (
    <button key={id} onClick={() => setStatusFilter(id)} style={{
      padding: '2px 8px', fontSize: 9, cursor: 'pointer',
      border: '1px solid', borderColor: statusFilter === id ? '#000 #fff #fff #000' : '#fff #888 #888 #fff',
      background: statusFilter === id ? '#fff' : 'var(--w95-bg)',
      fontWeight: statusFilter === id ? 'bold' : 'normal',
      fontFamily: 'inherit',
    }}>{label}</button>
  );

  return (
    <>
      <AdminPanelHeader tabId="products" title="Product editor"
        sub={`${activeCount} active · ${archivedCount} archived`} />

      <div style={{ display: 'flex', gap: 3, marginBottom: 4 }}>
        {chip('all', `All (${allProducts.length})`)}
        {chip('active', `Active (${activeCount})`)}
        {chip('archived', `Archived (${archivedCount})`)}
      </div>

      <div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
        <input className="w95-input" value={q} placeholder="🔍 search products"
          onChange={(e) => setQ(e.target.value)} style={{ flex: 1, fontSize: 10 }} />
        {q && <button className="w95-btn" style={{ minWidth: 0, padding: '1px 6px' }}
          onClick={() => setQ('')}>×</button>}
      </div>

      {selectedSlugs.size > 0 && (
        <div style={{
          display: 'flex', gap: 4, alignItems: 'center', padding: 4, marginBottom: 4,
          background: '#fff8c4', border: '1px solid #888',
        }}>
          <span style={{ fontSize: 9, fontWeight: 'bold' }}>
            {selectedSlugs.size} selected
          </span>
          <button className="w95-btn" disabled={bulkBusy}
            onClick={() => runBulk('hide')}
            style={{ fontSize: 9, padding: '1px 6px' }}>
            Hide selected
          </button>
          <button className="w95-btn" disabled={bulkBusy}
            onClick={() => runBulk('show')}
            style={{ fontSize: 9, padding: '1px 6px' }}>
            Show selected
          </button>
          <button className="w95-btn" disabled={bulkBusy}
            onClick={() => runBulk('delete')}
            style={{ fontSize: 9, padding: '1px 6px', background: '#a00', color: '#fff', fontWeight: 'bold' }}>
            Delete selected
          </button>
          <button className="w95-btn" disabled={bulkBusy} onClick={clearSelection}
            style={{ fontSize: 9, padding: '1px 6px', marginLeft: 'auto' }}>×</button>
        </div>
      )}

      {filtered.length === 0 ? <AdminEmpty>no matches</AdminEmpty> : (
        <>
          <div style={{ display: 'flex', alignItems: 'center', gap: 4, padding: '2px 4px', fontSize: 9, color: '#444' }}>
            <input type="checkbox" checked={allVisibleSelected}
              onChange={(e) => e.target.checked ? selectAllVisible() : clearSelection()} />
            <span>select all ({filtered.length})</span>
          </div>
          <div className="w95-inset w95-scroll" style={{ background: '#fff', maxHeight: 360, overflow: 'auto' }}>
            {filtered.map((p) => {
              const isArchived = p.status === 'archived';
              const checked = selectedSlugs.has(p.slug);
              return (
                <div key={p.slug} onClick={() => setSlug(p.slug)} style={{
                  display: 'flex', gap: 6, alignItems: 'center', padding: 4,
                  borderBottom: '1px solid #ddd', cursor: 'pointer',
                  background: checked ? '#e0f0ff' : (isArchived ? '#f5f0f0' : '#fff'),
                  opacity: isArchived ? 0.75 : 1,
                }}>
                  <input type="checkbox" checked={checked}
                    onClick={(e) => e.stopPropagation()}
                    onChange={(e) => { e.stopPropagation(); toggleSelect(p.slug); }} />
                  <div style={{ width: 28, height: 28, background: '#fff', border: '1px solid #888', overflow: 'hidden', flexShrink: 0 }}>
                    {p.image && <img src={p.image} alt={p.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />}
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 10, fontWeight: 'bold', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 4 }}>
                      <span style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>{p.name}</span>
                      {isArchived && (
                        <span style={{
                          fontSize: 8, padding: '0 4px', background: '#a00', color: '#fff',
                          fontWeight: 'bold', flexShrink: 0,
                        }}>HIDDEN</span>
                      )}
                    </div>
                    <div style={{ fontSize: 9, color: '#666' }}>{p.slug}</div>
                  </div>
                  <div style={{ fontSize: 10, color: '#ff3eb5', fontWeight: 'bold' }}>{fmtINR(p.price)}</div>
                </div>
              );
            })}
          </div>
        </>
      )}
    </>
  );
}

function AdminCatalogEditor({ product, collections, onSaved, onDeleted, onStatusChanged }) {
  const [name, setName] = React.useState(product.name || '');
  const [price, setPrice] = React.useState(String(product.price || ''));
  const [kind, setKind] = React.useState(product.kind || '');
  const [cols, setCols] = React.useState(product.cols || []);
  // Normalize null/undefined/'active' all to 'active' in the form; persist
  // 'archived' explicitly to flip the public-visibility bit.
  const [status, setStatus] = React.useState(product.status === 'archived' ? 'archived' : 'active');
  const [busy, setBusy] = React.useState(false);
  const [deleting, setDeleting] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [uploading, setUploading] = React.useState(false);
  const [imgVer, setImgVer] = React.useState(0); // bust img cache after upload
  const fileRef = React.useRef(null);

  const initialStatus = product.status === 'archived' ? 'archived' : 'active';
  const statusChanged = status !== initialStatus;

  const toggleCol = (id) => {
    setCols((prev) => prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id]);
  };

  const save = async () => {
    setBusy(true); setErr('');
    try {
      const patch = {
        name: name.trim(),
        price: Number(price),
        kind: kind.trim(),
        cols,
        status,
      };
      await window.adminUpdateProduct(product.slug, patch);
      // Mutate in-place so the rest of the app reflects the change without
      // a reload (matches loadCatalog's mutate-in-place pattern).
      Object.assign(product, patch);
      onSaved(`Saved: ${name}`);
      // A status flip moves the product between PRODUCTS and ARCHIVED_PRODUCTS,
      // so re-run loadCatalog to keep the panel list in sync.
      if (statusChanged && typeof onStatusChanged === 'function') {
        await onStatusChanged();
      }
    } catch (e) {
      setErr(e.message || 'Failed.');
    } finally {
      setBusy(false);
    }
  };

  const remove = async () => {
    if (!window.confirm(`Permanently delete "${product.name}" (${product.slug})? This deletes the product and its details doc. Storage images are kept.`)) return;
    setDeleting(true); setErr('');
    try {
      await window.adminDeleteProduct(product.slug);
      onSaved(`Deleted: ${product.name}`);
      if (typeof onDeleted === 'function') await onDeleted();
    } catch (e) {
      setErr(e.message || 'Delete failed.');
      setDeleting(false);
    }
  };

  const uploadImage = async (file, index) => {
    if (!file) return;
    setUploading(true); setErr('');
    try {
      const { path, url } = await window.adminUploadImage(file, product.slug, index);
      const patch = index === 1
        ? { image: path }
        : { images: [...(product.images || []), path] };
      await window.adminUpdateProduct(product.slug, patch);
      // Mutate in place — store the URL form so the runtime <img> works.
      if (index === 1) product.image = url;
      else product.images = [...(product.images || []), url];
      setImgVer((v) => v + 1);
      onSaved(`Image uploaded`);
    } catch (e) {
      setErr(e.message || 'Upload failed.');
    } finally {
      setUploading(false);
      if (fileRef.current) fileRef.current.value = '';
    }
  };

  return (
    <div style={{ fontSize: 10 }}>
      <div style={{ display: 'flex', gap: 6, marginBottom: 6 }}>
        <div style={{
          width: 70, height: 70, background: '#fff', border: '1px solid #000',
          overflow: 'hidden', flexShrink: 0, position: 'relative',
        }} key={imgVer}>
          {product.image && <img src={product.image} alt={product.name || 'product'} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />}
        </div>
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 3 }}>
          <button className="w95-btn" disabled={uploading}
            onClick={() => fileRef.current && fileRef.current.click()}
            style={{ fontSize: 9, padding: '2px 4px' }}>
            {uploading ? 'Uploading…' : 'Replace primary image'}
          </button>
          <input ref={fileRef} type="file" accept="image/*" style={{ display: 'none' }}
            onChange={(e) => uploadImage(e.target.files && e.target.files[0], 1)} />
          <div style={{ fontSize: 8, color: '#666' }}>
            ≤ 8MB. Replaces <span style={{ fontFamily: 'monospace' }}>products/{product.slug}/1.&lt;ext&gt;</span>
          </div>
        </div>
      </div>

      <FieldRow label="Name">
        <input className="w95-input" value={name} onChange={(e) => setName(e.target.value)}
          style={{ flex: 1, fontSize: 10 }} />
      </FieldRow>
      <FieldRow label="Price ₹">
        <input className="w95-input" type="number" value={price} onChange={(e) => setPrice(e.target.value)}
          style={{ flex: 1, fontSize: 10 }} />
      </FieldRow>
      <FieldRow label="Kind">
        <input className="w95-input" value={kind} onChange={(e) => setKind(e.target.value)}
          placeholder="tshirt | shoe | hoodie | pants"
          style={{ flex: 1, fontSize: 10 }} />
      </FieldRow>
      <FieldRow label="Visibility">
        <select className="w95-input" value={status}
          onChange={(e) => setStatus(e.target.value)}
          style={{ flex: 1, fontSize: 10 }}>
          <option value="active">Active — shown in catalogue</option>
          <option value="archived">Archived — hidden from catalogue</option>
        </select>
      </FieldRow>
      {status === 'archived' && (
        <div style={{
          fontSize: 9, padding: '3px 4px', marginBottom: 4,
          background: '#fff8c4', border: '1px solid #888', color: '#444',
        }}>
          This product is hidden from the shop grid, file manager, and related-products
          rails. Direct slug URLs still resolve.
        </div>
      )}

      <div style={{ marginBottom: 4, fontSize: 9, fontWeight: 'bold' }}>Collections</div>
      <div className="w95-inset" style={{ background: '#fff', padding: 4, marginBottom: 6 }}>
        {collections.map((c) => (
          <label key={c.id} style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 9, cursor: 'pointer' }}>
            <input type="checkbox" checked={cols.includes(c.id)} onChange={() => toggleCol(c.id)} />
            <span>{c.name}</span>
            <span style={{ marginLeft: 'auto', color: '#888', fontSize: 8 }}>{c.id}</span>
          </label>
        ))}
      </div>

      {err && <div style={{ color: '#a00', fontSize: 9, marginBottom: 4 }}>{err}</div>}
      <button className="w95-btn" disabled={busy || deleting} onClick={save} style={{
        width: '100%', background: 'var(--ap-pink-deep)', color: '#fff', fontWeight: 'bold',
      }}>{busy ? 'Saving…' : 'Save catalog row'}</button>
      <button className="w95-btn" disabled={busy || deleting} onClick={remove} style={{
        width: '100%', marginTop: 4, background: '#a00', color: '#fff', fontWeight: 'bold',
      }}>{deleting ? 'Deleting…' : 'Delete product'}</button>
    </div>
  );
}

function FieldRow({ label, children }) {
  return (
    <div style={{ display: 'flex', gap: 4, alignItems: 'center', marginBottom: 4 }}>
      <span style={{ width: 60, fontSize: 9 }}>{label}</span>
      {children}
    </div>
  );
}

function AdminDetailsEditor({ product, onSaved }) {
  const initial = (window.PRODUCT_DETAILS && window.PRODUCT_DETAILS[product.slug]) || {};
  const [desc, setDesc] = React.useState(initial.desc || '');
  const [options, setOptions] = React.useState(() => JSON.parse(JSON.stringify(initial.options || [])));
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');

  const setOpt = (i, partial) => setOptions((opts) => opts.map((o, j) => j === i ? { ...o, ...partial } : o));
  const setChoice = (oi, ci, partial) => setOptions((opts) =>
    opts.map((o, j) => j !== oi ? o : { ...o, choices: o.choices.map((c, k) => k === ci ? { ...c, ...partial } : c) }));
  const addOption = () => setOptions((opts) => [...opts, { name: 'New option', type: 'select', choices: [{ label: 'Choice A' }] }]);
  const removeOption = (i) => setOptions((opts) => opts.filter((_, j) => j !== i));
  const addChoice = (oi) => setOptions((opts) => opts.map((o, j) => j !== oi ? o : {
    ...o, choices: [...o.choices, o.type === 'color' ? { label: 'New', hex: '#ffffff' } : { label: 'New' }],
  }));
  const removeChoice = (oi, ci) => setOptions((opts) =>
    opts.map((o, j) => j !== oi ? o : { ...o, choices: o.choices.filter((_, k) => k !== ci) }));

  const save = async () => {
    setBusy(true); setErr('');
    try {
      await window.adminUpdateProductDetails(product.slug, { desc, options });
      // Mutate in-place so subsequent product-view renders see the change.
      window.PRODUCT_DETAILS = window.PRODUCT_DETAILS || {};
      window.PRODUCT_DETAILS[product.slug] = {
        ...(window.PRODUCT_DETAILS[product.slug] || {}),
        desc, options,
      };
      onSaved('Details saved');
    } catch (e) {
      setErr(e.message || 'Failed.');
    } finally {
      setBusy(false);
    }
  };

  return (
    <div style={{ fontSize: 10 }}>
      <div style={{ marginBottom: 4, fontSize: 9, fontWeight: 'bold' }}>Description</div>
      <textarea className="w95-input" value={desc} onChange={(e) => setDesc(e.target.value)}
        rows={5} style={{ width: '100%', fontSize: 10, fontFamily: 'inherit', marginBottom: 6 }} />

      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
        <div style={{ fontSize: 9, fontWeight: 'bold' }}>Options ({options.length})</div>
        <button className="w95-btn" onClick={addOption}
          style={{ minWidth: 0, padding: '1px 6px', fontSize: 9 }}>+ option</button>
      </div>

      {options.map((opt, oi) => (
        <div key={oi} className="w95-inset" style={{ background: '#fff', padding: 4, marginBottom: 4 }}>
          <div style={{ display: 'flex', gap: 4, marginBottom: 3 }}>
            <input className="w95-input" value={opt.name} onChange={(e) => setOpt(oi, { name: e.target.value })}
              style={{ flex: 1, fontSize: 10 }} />
            <select className="w95-input" value={opt.type} onChange={(e) => setOpt(oi, { type: e.target.value })}
              style={{ fontSize: 10 }}>
              <option value="select">select</option>
              <option value="color">color</option>
            </select>
            <button className="w95-btn" onClick={() => removeOption(oi)}
              style={{ minWidth: 0, padding: '1px 4px', fontSize: 9 }}>×</button>
          </div>
          {opt.choices.map((c, ci) => (
            <div key={ci} style={{ display: 'flex', gap: 3, marginBottom: 2, alignItems: 'center' }}>
              <input className="w95-input" value={c.label}
                onChange={(e) => setChoice(oi, ci, { label: e.target.value })}
                style={{ flex: 1, fontSize: 10 }} />
              {opt.type === 'color' && (
                <input type="color" value={c.hex || '#ffffff'}
                  onChange={(e) => setChoice(oi, ci, { hex: e.target.value })}
                  style={{ width: 28, height: 22, padding: 0, border: '1px solid #888' }} />
              )}
              <button className="w95-btn" onClick={() => removeChoice(oi, ci)}
                style={{ minWidth: 0, padding: '1px 4px', fontSize: 9 }}>×</button>
            </div>
          ))}
          <button className="w95-btn" onClick={() => addChoice(oi)}
            style={{ minWidth: 0, padding: '1px 6px', fontSize: 9, marginTop: 2 }}>+ choice</button>
        </div>
      ))}

      {err && <div style={{ color: '#a00', fontSize: 9, marginBottom: 4 }}>{err}</div>}
      <button className="w95-btn" disabled={busy} onClick={save} style={{
        width: '100%', marginTop: 4, background: 'var(--ap-pink-deep)', color: '#fff', fontWeight: 'bold',
      }}>{busy ? 'Saving…' : 'Save details'}</button>
    </div>
  );
}

// ─── Panel 4 — Display order ─────────────────────────────────────────────
const ORDER_PRESETS = [
  { id: 'manual',    label: 'Manual (current)' },
  { id: 'price-asc', label: 'Price ↑'  },
  { id: 'price-dsc', label: 'Price ↓'  },
  { id: 'a-z',       label: 'A → Z'    },
  { id: 'z-a',       label: 'Z → A'    },
  { id: 'best',      label: 'Best sellers (uses last 30d)' },
];

function AdminDisplayOrderPanel({ onSaved }) {
  const [list, setList] = React.useState(() => (window.PRODUCTS || []).map((p) => p.slug));
  const [preset, setPreset] = React.useState('manual');
  const [pinned, setPinned] = React.useState(new Set());
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [bestRanking, setBestRanking] = React.useState(null); // slug -> units
  const dragSrc = React.useRef(null);

  const products = window.PRODUCTS || [];
  const idx = React.useMemo(() => Object.fromEntries(products.map((p) => [p.slug, p])), [products]);

  const applyPreset = async (id) => {
    setPreset(id);
    if (id === 'manual') return;
    if (id === 'best' && !bestRanking) {
      try {
        const a = await window.adminGetAnalytics({ days: 30 });
        const r = {};
        (a.topProducts || []).forEach((p, i) => { r[p.slug] = a.topProducts.length - i; });
        setBestRanking(r);
        setList(reorderBy(list, idx, id, r));
        return;
      } catch (e) {
        setErr('Could not load best-seller data.');
        return;
      }
    }
    setList(reorderBy(list, idx, id, bestRanking));
  };

  const togglePin = (slug) => {
    setPinned((p) => {
      const n = new Set(p);
      if (n.has(slug)) n.delete(slug); else n.add(slug);
      return n;
    });
  };

  const move = (from, to) => {
    if (from === to || from < 0 || to < 0 || from >= list.length || to >= list.length) return;
    setList((l) => {
      const next = l.slice();
      const [v] = next.splice(from, 1);
      next.splice(to, 0, v);
      return next;
    });
    setPreset('manual');
  };

  const save = async () => {
    // Pinned items go to the top in current order.
    const pin = list.filter((s) => pinned.has(s));
    const rest = list.filter((s) => !pinned.has(s));
    const final = [...pin, ...rest];
    setBusy(true); setErr('');
    try {
      await window.adminReorderProducts(final);
      // Mutate PRODUCTS in place so the shop grid reorders without reload.
      const slugIndex = Object.fromEntries(final.map((s, i) => [s, i]));
      products.forEach((p) => { p.order = slugIndex[p.slug] ?? p.order; });
      products.sort((a, b) => (a.order ?? 9999) - (b.order ?? 9999));
      onSaved(`Reordered ${final.length} products`);
    } catch (e) {
      setErr(e.message || 'Failed.');
    } finally {
      setBusy(false);
    }
  };

  return (
    <>
      <AdminPanelHeader tabId="order" title="Display order"
        sub={`${list.length} products · drag to reorder`} />
      <FieldRow label="Smart sort">
        <select className="w95-input" value={preset} onChange={(e) => applyPreset(e.target.value)}
          style={{ flex: 1, fontSize: 10 }}>
          {ORDER_PRESETS.map((p) => <option key={p.id} value={p.id}>{p.label}</option>)}
        </select>
      </FieldRow>

      {/* Live preview strip */}
      <div style={{ fontSize: 9, color: '#444', margin: '6px 0 3px' }}>Preview (top 6)</div>
      <div className="w95-inset" style={{ background: '#fff', padding: 4, marginBottom: 6 }}>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 3 }}>
          {[...list.filter((s) => pinned.has(s)), ...list.filter((s) => !pinned.has(s))].slice(0, 6).map((slug) => {
            const p = idx[slug];
            if (!p) return null;
            return (
              <div key={slug} style={{ aspectRatio: '1/1', background: '#fff', border: '1px solid #888', overflow: 'hidden', position: 'relative' }}>
                {p.image && <img src={p.image} alt={p.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />}
                {pinned.has(slug) && (
                  <div style={{ position: 'absolute', top: 0, right: 0, background: '#ffe14d', padding: '0 2px', fontSize: 7, fontWeight: 'bold' }}>★</div>
                )}
              </div>
            );
          })}
        </div>
      </div>

      <div className="w95-inset w95-scroll" style={{ background: '#fff', maxHeight: 280, overflow: 'auto' }}>
        {list.map((slug, i) => {
          const p = idx[slug];
          if (!p) return null;
          const isPinned = pinned.has(slug);
          return (
            <div key={slug}
              draggable
              onDragStart={() => { dragSrc.current = i; }}
              onDragOver={(e) => e.preventDefault()}
              onDrop={() => { if (dragSrc.current != null) { move(dragSrc.current, i); dragSrc.current = null; } }}
              style={{
                display: 'flex', gap: 4, alignItems: 'center', padding: 3,
                borderBottom: '1px solid #eee', background: isPinned ? '#fff8c4' : '#fff',
                cursor: 'grab',
              }}>
              <span style={{ width: 16, fontSize: 12, color: '#888', textAlign: 'center', userSelect: 'none' }}>≡</span>
              <span style={{ width: 18, fontSize: 8, color: '#888', textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{i + 1}</span>
              <div style={{ width: 22, height: 22, background: '#fff', border: '1px solid #888', overflow: 'hidden', flexShrink: 0 }}>
                {p.image && <img src={p.image} alt={p.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />}
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 9, fontWeight: 'bold', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  {p.name}
                </div>
                <div style={{ fontSize: 8, color: '#888' }}>{fmtINR(p.price)}</div>
              </div>
              <button onClick={() => move(i, i - 1)} title="Up"
                style={{ ...iconBtn, opacity: i === 0 ? 0.3 : 1 }}>↑</button>
              <button onClick={() => move(i, i + 1)} title="Down"
                style={{ ...iconBtn, opacity: i === list.length - 1 ? 0.3 : 1 }}>↓</button>
              <button onClick={() => togglePin(slug)} title="Pin to top"
                style={{ ...iconBtn, color: isPinned ? '#ff7a18' : '#888' }}>★</button>
            </div>
          );
        })}
      </div>

      {err && <div style={{ color: '#a00', fontSize: 9, marginTop: 4 }}>{err}</div>}
      <div style={{ display: 'flex', gap: 4, marginTop: 6 }}>
        <button className="w95-btn" disabled={busy} onClick={save} style={{
          flex: 1, background: 'var(--ap-pink-deep)', color: '#fff', fontWeight: 'bold',
        }}>{busy ? 'Saving…' : 'Apply order'}</button>
        <button className="w95-btn" disabled={busy}
          onClick={() => { setList((window.PRODUCTS || []).map((p) => p.slug)); setPreset('manual'); setPinned(new Set()); }}>
          Reset
        </button>
      </div>
    </>
  );
}

const iconBtn = {
  width: 18, height: 18, padding: 0, fontSize: 11, lineHeight: 1,
  border: '1px solid', borderColor: '#fff #000 #000 #fff',
  background: 'var(--w95-bg)', cursor: 'pointer', fontFamily: 'inherit',
};

function reorderBy(slugs, idx, preset, bestRanking) {
  const arr = slugs.slice();
  const get = (s) => idx[s];
  const cmp = {
    'price-asc': (a, b) => (get(a)?.price || 0) - (get(b)?.price || 0),
    'price-dsc': (a, b) => (get(b)?.price || 0) - (get(a)?.price || 0),
    'a-z':       (a, b) => (get(a)?.name || '').localeCompare(get(b)?.name || ''),
    'z-a':       (a, b) => (get(b)?.name || '').localeCompare(get(a)?.name || ''),
    'best':      (a, b) => ((bestRanking && bestRanking[b]) || 0) - ((bestRanking && bestRanking[a]) || 0),
  }[preset];
  if (cmp) arr.sort(cmp);
  return arr;
}

// ─── Panel 5 — Coupons ───────────────────────────────────────────────────
// CRUD over Firestore `coupons/{CODE}`. Source of truth for priceCart in
// functions/index.js — every change here ripples to checkout immediately.
// New codes get source: 'manual'; Wix-imported codes keep source: 'wix-import'.

const KIND_LABEL = { percent: '% off', flat: '₹ off', free_shipping: 'free ship' };
const SCOPE_LABEL = { stores: 'store', bookings: 'bookings' };

function fmtCouponValue(c) {
  if (c.kind === 'percent') return `${c.value}%`;
  if (c.kind === 'flat')    return `₹${c.value}`;
  if (c.kind === 'free_shipping') return 'free ship';
  return String(c.value);
}

function couponStatusChip(c) {
  const now = Date.now();
  if (c.active === false) return { label: 'inactive', color: '#888' };
  if (c.expirationTime && c.expirationTime < now) return { label: 'expired', color: '#a00' };
  if (c.startTime && c.startTime > now) return { label: 'scheduled', color: '#0066aa' };
  return { label: 'live', color: '#0a8030' };
}

function AdminCouponsPanel({ onSaved }) {
  const [coupons, setCoupons] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');
  const [editing, setEditing] = React.useState(null); // null | 'new' | <coupon object>
  const [filter, setFilter] = React.useState('');

  const load = React.useCallback(() => {
    setLoading(true); setError('');
    window.adminListCoupons()
      .then((res) => { setCoupons(res.coupons || []); setLoading(false); })
      .catch((e) => { setError(e.message || 'Failed to load coupons.'); setLoading(false); });
  }, []);
  React.useEffect(() => { load(); }, [load]);

  const deleteCoupon = async (c) => {
    if (!window.confirm(`Delete coupon "${c.code}"? This can't be undone.`)) return;
    try {
      await window.adminDeleteCoupon(c.code);
      onSaved && onSaved(`✗ ${c.code} deleted`);
      load();
    } catch (e) {
      setError(e.message || 'Delete failed.');
    }
  };

  if (editing) {
    return (
      <AdminCouponEditor
        coupon={editing === 'new' ? null : editing}
        onClose={() => setEditing(null)}
        onSaved={(saved, created) => {
          onSaved && onSaved(`${created ? '✦ Created' : '✓ Saved'} ${saved.code}`);
          setEditing(null);
          load();
        }}
      />
    );
  }

  const q = filter.trim().toLowerCase();
  const visible = q
    ? coupons.filter((c) => c.code.toLowerCase().includes(q) || (c.label || '').toLowerCase().includes(q))
    : coupons;

  return (
    <>
      <AdminPanelHeader tabId="coupons" title="Promo codes"
        sub={`${visible.length} of ${coupons.length}`}
        right={
          <button className="w95-btn" style={{ minWidth: 0, padding: '1px 8px', fontSize: 9 }}
            onClick={() => setEditing('new')}>+ New</button>
        }
      />
      <div style={{ display: 'flex', gap: 6, marginBottom: 6 }}>
        <input className="w95-input" placeholder="Search code or label…" value={filter}
          onChange={(e) => setFilter(e.target.value)}
          style={{ flex: 1, fontSize: 10, padding: '2px 4px' }} />
        <button className="w95-btn" style={{ fontSize: 9, padding: '1px 8px' }} onClick={load}>↻</button>
      </div>
      {error && <div className="w95-inset" style={{ background: '#ffe5e5', padding: 6, fontSize: 10, color: '#a00', marginBottom: 6 }}>{error}</div>}
      {loading ? <AdminLoader label="loading coupons…" /> :
        visible.length === 0 ? <AdminEmpty>No coupons. Hit <b>+ New</b> to create one.</AdminEmpty> : (
        <div className="w95-inset" style={{ background: '#fff', padding: 0 }}>
          {visible.map((c) => {
            const chip = couponStatusChip(c);
            return (
              <div key={c.code} style={{
                display: 'flex', alignItems: 'center', gap: 6,
                padding: '5px 6px', borderBottom: '1px dotted #ccc', fontSize: 10,
              }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: 'monospace', fontWeight: 'bold', fontSize: 11 }}>
                    {c.code}{' '}
                    <span style={{
                      display: 'inline-block', padding: '0 4px', fontSize: 8,
                      background: chip.color, color: '#fff', marginLeft: 4,
                      verticalAlign: 'middle',
                    }}>{chip.label}</span>
                    {c.source === 'wix-import' && (
                      <span style={{ marginLeft: 4, fontSize: 8, color: '#666' }}>· wix</span>
                    )}
                    {c.firstOrderOnly && (
                      <span style={{ marginLeft: 4, fontSize: 8, color: '#0066aa' }}>· first-order</span>
                    )}
                  </div>
                  <div style={{ fontSize: 9, color: '#444' }}>
                    {c.label} · <b>{fmtCouponValue(c)}</b> · {SCOPE_LABEL[c.scope] || c.scope}
                    {c.usageLimit != null && ` · cap ${c.usageLimit}`}
                    {c.limitPerCustomer != null && ` · ${c.limitPerCustomer}/user`}
                  </div>
                </div>
                <button className="w95-btn" style={{ fontSize: 9, padding: '1px 6px' }}
                  onClick={() => setEditing(c)}>Edit</button>
                <button className="w95-btn" style={{ fontSize: 9, padding: '1px 6px', color: '#a00' }}
                  onClick={() => deleteCoupon(c)}>✗</button>
              </div>
            );
          })}
        </div>
      )}
    </>
  );
}

function AdminCouponEditor({ coupon, onClose, onSaved }) {
  const isNew = !coupon;
  const [code, setCode] = React.useState(coupon ? coupon.code : '');
  const [label, setLabel] = React.useState(coupon ? (coupon.label || '') : '');
  const [kind, setKind] = React.useState(coupon ? coupon.kind : 'percent');
  const [value, setValue] = React.useState(coupon ? String(coupon.value || 0) : '');
  const [scope, setScope] = React.useState(coupon ? (coupon.scope || 'stores') : 'stores');
  const [active, setActive] = React.useState(coupon ? coupon.active !== false : true);
  const [firstOrderOnly, setFirstOrderOnly] = React.useState(coupon ? !!coupon.firstOrderOnly : false);
  const [usageLimit, setUsageLimit] = React.useState(coupon && coupon.usageLimit != null ? String(coupon.usageLimit) : '');
  const [limitPerCustomer, setLimitPerCustomer] = React.useState(coupon && coupon.limitPerCustomer != null ? String(coupon.limitPerCustomer) : '');
  const [startTime, setStartTime] = React.useState(coupon && coupon.startTime ? new Date(coupon.startTime).toISOString().slice(0, 16) : '');
  const [expirationTime, setExpirationTime] = React.useState(coupon && coupon.expirationTime ? new Date(coupon.expirationTime).toISOString().slice(0, 16) : '');
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState('');

  const save = async () => {
    setError('');
    const payload = {
      code: code.trim().toUpperCase(),
      label: label.trim() || code.trim().toUpperCase(),
      kind,
      value: kind === 'free_shipping' ? 0 : Number(value),
      scope,
      active,
      firstOrderOnly,
      usageLimit: usageLimit === '' ? null : Number(usageLimit),
      limitPerCustomer: limitPerCustomer === '' ? null : Number(limitPerCustomer),
      startTime: startTime ? new Date(startTime).getTime() : null,
      expirationTime: expirationTime ? new Date(expirationTime).getTime() : null,
    };
    setBusy(true);
    try {
      const res = await window.adminUpsertCoupon(payload);
      onSaved(res.coupon, res.created);
    } catch (e) {
      setError(e.message || 'Save failed.');
    } finally {
      setBusy(false);
    }
  };

  const valueLabel =
    kind === 'percent' ? 'Percent off (0-100)'
    : kind === 'flat' ? 'Flat ₹ off'
    : 'Free shipping (no value needed)';

  return (
    <>
      <AdminPanelHeader tabId="coupons"
        title={isNew ? 'New promo code' : `Edit ${coupon.code}`}
        sub={isNew ? 'Create a new coupon' : `last updated ${fmtDate(coupon.updatedAt)}`}
        right={<button className="w95-btn" style={{ fontSize: 9, padding: '1px 8px' }} onClick={onClose}>← Back</button>}
      />
      {error && <div className="w95-inset" style={{ background: '#ffe5e5', padding: 6, fontSize: 10, color: '#a00', marginBottom: 6 }}>{error}</div>}
      <div className="w95-inset" style={{ background: '#fff', padding: 8, display: 'flex', flexDirection: 'column', gap: 6, fontSize: 10 }}>
        <label style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
          <span style={{ fontSize: 9, color: '#444' }}>Code (uppercase, letters/digits/_/-)</span>
          <input className="w95-input" value={code} disabled={!isNew}
            onChange={(e) => setCode(e.target.value.toUpperCase().replace(/[^A-Z0-9_-]/g, ''))}
            style={{ fontFamily: 'monospace', padding: '3px 4px' }} maxLength={40} />
        </label>
        <label style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
          <span style={{ fontSize: 9, color: '#444' }}>Display label (shown on checkout)</span>
          <input className="w95-input" value={label} onChange={(e) => setLabel(e.target.value)}
            style={{ padding: '3px 4px' }} maxLength={80} />
        </label>
        <div style={{ display: 'flex', gap: 6 }}>
          <label style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
            <span style={{ fontSize: 9, color: '#444' }}>Type</span>
            <select className="w95-input" value={kind} onChange={(e) => setKind(e.target.value)} style={{ padding: '2px 4px' }}>
              <option value="percent">% off</option>
              <option value="flat">Flat ₹ off</option>
              <option value="free_shipping">Free shipping</option>
            </select>
          </label>
          <label style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
            <span style={{ fontSize: 9, color: '#444' }}>{valueLabel}</span>
            <input className="w95-input" type="number" value={kind === 'free_shipping' ? '' : value}
              onChange={(e) => setValue(e.target.value)} disabled={kind === 'free_shipping'}
              min="0" max={kind === 'percent' ? '100' : '100000'}
              style={{ padding: '3px 4px' }} />
          </label>
        </div>
        <label style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
          <span style={{ fontSize: 9, color: '#444' }}>Scope (where this code is valid)</span>
          <select className="w95-input" value={scope} onChange={(e) => setScope(e.target.value)} style={{ padding: '2px 4px' }}>
            <option value="stores">Store (t-shirts etc.)</option>
            <option value="bookings">Bookings (workshops only — not on store cart)</option>
          </select>
        </label>
        <div style={{ display: 'flex', gap: 6 }}>
          <label style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
            <span style={{ fontSize: 9, color: '#444' }}>Total usage cap (blank = unlimited)</span>
            <input className="w95-input" type="number" min="0" value={usageLimit}
              onChange={(e) => setUsageLimit(e.target.value)} style={{ padding: '3px 4px' }} />
          </label>
          <label style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
            <span style={{ fontSize: 9, color: '#444' }}>Per customer (blank = unlimited)</span>
            <input className="w95-input" type="number" min="0" value={limitPerCustomer}
              onChange={(e) => setLimitPerCustomer(e.target.value)} style={{ padding: '3px 4px' }} />
          </label>
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          <label style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
            <span style={{ fontSize: 9, color: '#444' }}>Starts (optional)</span>
            <input className="w95-input" type="datetime-local" value={startTime}
              onChange={(e) => setStartTime(e.target.value)} style={{ padding: '2px 4px' }} />
          </label>
          <label style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
            <span style={{ fontSize: 9, color: '#444' }}>Expires (optional)</span>
            <input className="w95-input" type="datetime-local" value={expirationTime}
              onChange={(e) => setExpirationTime(e.target.value)} style={{ padding: '2px 4px' }} />
          </label>
        </div>
        <div style={{ display: 'flex', gap: 12, marginTop: 2 }}>
          <label style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10 }}>
            <input type="checkbox" checked={active} onChange={(e) => setActive(e.target.checked)} />
            Active
          </label>
          <label style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10 }}>
            <input type="checkbox" checked={firstOrderOnly} onChange={(e) => setFirstOrderOnly(e.target.checked)} />
            First order only
          </label>
        </div>
        <div style={{ display: 'flex', gap: 6, marginTop: 6 }}>
          <button className="w95-btn" onClick={save} disabled={busy || !code.trim()}
            style={{ flex: 1, padding: '4px 8px', fontWeight: 'bold' }}>
            {busy ? 'saving…' : (isNew ? 'Create code' : 'Save changes')}
          </button>
          <button className="w95-btn" onClick={onClose} disabled={busy}
            style={{ padding: '4px 12px' }}>Cancel</button>
        </div>
      </div>
    </>
  );
}

// ─── Panel 6 — Analytics ─────────────────────────────────────────────────
function AdminAnalyticsPanel() {
  const [days, setDays] = React.useState(30);
  const [data, setData] = React.useState(null);
  const [error, setError] = React.useState('');

  React.useEffect(() => {
    setData(null); setError('');
    window.adminGetAnalytics({ days })
      .then(setData)
      .catch((e) => setError(e.message || 'Failed.'));
  }, [days]);

  return (
    <>
      <AdminPanelHeader tabId="stats" title="Analytics" sub={`last ${days} days`}
        right={
          <select className="w95-input" value={days} onChange={(e) => setDays(Number(e.target.value))}
            style={{ fontSize: 9 }}>
            <option value={7}>7d</option>
            <option value={30}>30d</option>
            <option value={90}>90d</option>
            <option value={365}>1y</option>
          </select>
        } />
      {error && <AdminEmpty>{error}</AdminEmpty>}
      {!data ? <AdminLoader label="crunching numbers…" /> : (
        <>
          <div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
            <AdminStatTile label="Revenue" value={fmtINR(data.revenue)} color="#ff3eb5" />
            <AdminStatTile label="Orders"  value={data.orders} sub={`of ${data.totalCreated} carts`} />
          </div>
          <div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
            <AdminStatTile label="AOV"     value={fmtINR(data.aov)} />
            <AdminStatTile label="Conv."   value={`${data.conversion}%`} sub="paid / created" color="#0a8a0a" />
          </div>

          <div style={{ fontSize: 9, fontWeight: 'bold', margin: '6px 0 3px' }}>Daily revenue</div>
          <div className="w95-inset" style={{ background: '#fff', padding: 4, marginBottom: 6 }}>
            <RevenueChart series={data.series || []} />
          </div>

          <div style={{ fontSize: 9, fontWeight: 'bold', margin: '6px 0 3px' }}>Top products</div>
          <div className="w95-inset" style={{ background: '#fff' }}>
            {(data.topProducts || []).length === 0 ? (
              <div style={{ padding: 8, textAlign: 'center', fontSize: 9, color: '#666' }}>no sales in this window</div>
            ) : (
              <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 9 }}>
                <thead>
                  <tr style={{ background: 'var(--w95-title)', color: '#fff' }}>
                    <th style={thStyle}>Product</th>
                    <th style={{ ...thStyle, textAlign: 'right' }}>Units</th>
                    <th style={{ ...thStyle, textAlign: 'right' }}>Revenue</th>
                  </tr>
                </thead>
                <tbody>
                  {data.topProducts.map((p, i) => (
                    <tr key={p.slug} style={{ background: i % 2 ? '#f0f0f0' : '#fff' }}>
                      <td style={tdStyle}>{p.name}</td>
                      <td style={{ ...tdStyle, textAlign: 'right' }}>{p.units}</td>
                      <td style={{ ...tdStyle, textAlign: 'right' }}>{fmtINR(p.revenue)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </div>

          <div style={{ fontSize: 9, fontWeight: 'bold', margin: '6px 0 3px' }}>Recent paid orders</div>
          <div className="w95-inset" style={{ background: '#fff' }}>
            {(data.recent || []).length === 0 ? (
              <div style={{ padding: 8, textAlign: 'center', fontSize: 9, color: '#666' }}>none yet</div>
            ) : (
              data.recent.map((o, i) => (
                <div key={o.id} style={{
                  display: 'flex', justifyContent: 'space-between', padding: '3px 4px',
                  fontSize: 9, background: i % 2 ? '#f0f0f0' : '#fff', borderBottom: '1px solid #eee',
                }}>
                  <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                    {o.receipt || o.id.slice(-8)} · {fmtDay(o.paidAt || o.createdAt)}
                  </span>
                  <span style={{ fontWeight: 'bold', color: '#0a8a0a' }}>{fmtINR(o.total)}</span>
                </div>
              ))
            )}
          </div>
        </>
      )}
    </>
  );
}

function RevenueChart({ series }) {
  const max = Math.max(1, ...series.map((d) => d.revenue || 0));
  const w = 280, h = 70;
  const bw = series.length > 0 ? (w / series.length) : 0;
  return (
    <svg viewBox={`0 0 ${w} ${h + 14}`} style={{ width: '100%', height: 'auto', display: 'block' }}>
      {series.map((d, i) => {
        const bh = (d.revenue / max) * h;
        return (
          <g key={i}>
            <rect x={i * bw + 0.5} y={h - bh} width={Math.max(1, bw - 1)} height={bh}
              fill="#ff3eb5" stroke="#000" strokeWidth="0.3">
              <title>{d.date}: {fmtINR(d.revenue)} ({d.orders} orders)</title>
            </rect>
          </g>
        );
      })}
      {/* axis */}
      <line x1="0" y1={h} x2={w} y2={h} stroke="#000" strokeWidth="0.5" />
      <text x="0" y={h + 10} fontSize="6" fill="#444">
        {series.length > 0 ? series[0].date.slice(5) : ''}
      </text>
      <text x={w} y={h + 10} fontSize="6" fill="#444" textAnchor="end">
        {series.length > 0 ? series[series.length - 1].date.slice(5) : ''}
      </text>
      <text x={w / 2} y={h + 10} fontSize="6" fill="#444" textAnchor="middle">
        max {fmtINR(max)}
      </text>
    </svg>
  );
}

// ─── Panel 6 — AI generator ─────────────────────────────────────────────
// Workflow:
//   1. Upload a reference design image (+ optional steering keyword).
//   2. Function calls Gemini to:
//        a. analyse the artwork and write copy + a subtle refinement note,
//        b. regenerate the design on a flat white background,
//        c. cut the white away (background removal) → transparent PNG,
//        d. composite the transparent design onto two cached blank tees
//           (one per Gemini-picked palette colour).
//   3. Draft preview lets admin Publish (becomes a live product) or Discard.
//
// Blank tees live in Storage at mockup-blanks/<key>.png and are populated
// once via the "Seed blanks" button below.

function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const s = String(reader.result || '');
      const i = s.indexOf(',');
      resolve(i >= 0 ? s.slice(i + 1) : s);
    };
    reader.onerror = () => reject(reader.error || new Error('Failed to read file.'));
    reader.readAsDataURL(file);
  });
}

function AdminGeneratorPanel({ onSaved }) {
  const [keyword, setKeyword] = React.useState('');
  const [refFile, setRefFile] = React.useState(null);
  const [refPreview, setRefPreview] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [progress, setProgress] = React.useState('');
  const [error, setError] = React.useState('');
  const [drafts, setDrafts] = React.useState([]);
  const [loadingList, setLoadingList] = React.useState(true);
  const [seeding, setSeeding] = React.useState(false);
  const fileInputRef = React.useRef(null);

  const loadDrafts = React.useCallback(() => {
    setLoadingList(true);
    window.adminListDrafts({ status: 'pending', limit: 25 })
      .then((res) => { setDrafts(res.drafts || []); setLoadingList(false); })
      .catch((e) => { setError(e.message || 'Failed.'); setLoadingList(false); });
  }, []);

  React.useEffect(() => { loadDrafts(); }, [loadDrafts]);

  // Cleanup the object URL on unmount / replacement.
  React.useEffect(() => () => { if (refPreview) URL.revokeObjectURL(refPreview); }, [refPreview]);

  // Fake progress ticker — the function is doing 4 sequential Gemini calls
  // plus a CPU bg-removal pass, ~60-90s end-to-end. Cycles through phases.
  React.useEffect(() => {
    if (!busy) { setProgress(''); return; }
    const phases = [
      'analysing your design with Gemini…',
      'regenerating the design on a clean background…',
      'cutting out the background…',
      'compositing onto blank tee 1 of 2…',
      'compositing onto blank tee 2 of 2…',
      'uploading to Storage…',
      'almost there…',
    ];
    let i = 0;
    setProgress(phases[0]);
    const id = setInterval(() => {
      i = Math.min(phases.length - 1, i + 1);
      setProgress(phases[i]);
    }, 11000);
    return () => clearInterval(id);
  }, [busy]);

  const onPickFile = (file) => {
    if (!file) return;
    if (!file.type || !file.type.startsWith('image/')) {
      setError('Pick an image file (PNG, JPG, WebP).'); return;
    }
    if (file.size > 8 * 1024 * 1024) {
      setError('Image too large — keep it under 8 MB.'); return;
    }
    if (refPreview) URL.revokeObjectURL(refPreview);
    setRefFile(file);
    setRefPreview(URL.createObjectURL(file));
    setError('');
  };

  const onDrop = (e) => {
    e.preventDefault();
    const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
    if (f) onPickFile(f);
  };

  const clearRef = () => {
    if (refPreview) URL.revokeObjectURL(refPreview);
    setRefFile(null); setRefPreview('');
    if (fileInputRef.current) fileInputRef.current.value = '';
  };

  const generate = async () => {
    if (!refFile) { setError('Upload a reference design image first.'); return; }
    setBusy(true); setError('');
    try {
      const refImage = await fileToBase64(refFile);
      const res = await window.adminGenerateProduct({
        refImage,
        refImageMime: refFile.type,
        keyword: keyword.trim() || undefined,
      });
      setDrafts((prev) => [res.draft, ...prev.filter((d) => d.draftId !== res.draft.draftId)]);
      onSaved(`✦ Generated draft: ${res.draft.name}`);
      setKeyword('');
      clearRef();
    } catch (e) {
      setError(e.message || 'Generation failed.');
    } finally {
      setBusy(false);
    }
  };

  const seedBlanks = async (force = false) => {
    if (force && !window.confirm('Force-regenerate ALL 6 blank tee mockups? This costs 6 Gemini image calls.')) return;
    setSeeding(true); setError('');
    try {
      const res = await window.adminSeedBlankMockups({ force });
      const created = res.results.filter((r) => r.status === 'created').length;
      const kept    = res.results.filter((r) => r.status === 'kept').length;
      const failed  = res.results.filter((r) => r.status === 'failed');
      onSaved(`Blanks: ${created} created, ${kept} kept${failed.length ? `, ${failed.length} failed` : ''}.`);
      if (failed.length) setError('Failed: ' + failed.map((f) => `${f.key} (${f.error})`).join('; '));
    } catch (e) {
      setError(e.message || 'Seed failed.');
    } finally {
      setSeeding(false);
    }
  };

  return (
    <>
      <AdminPanelHeader tabId="ai" title="AI product generator"
        sub="powered by Gemini 2.5 + @imgly bg-removal"
        right={<button className="w95-btn" style={{ minWidth: 0, padding: '1px 6px', fontSize: 9 }}
          onClick={loadDrafts}>↻</button>} />

      <div className="w95-inset" style={{ background: '#fff', padding: 6, marginBottom: 6 }}>
        <div style={{ fontSize: 9, fontWeight: 'bold', marginBottom: 3 }}>
          Reference design <span style={{ fontWeight: 'normal', color: '#888' }}>(required)</span>
        </div>
        {refPreview ? (
          <div style={{ display: 'flex', gap: 6, alignItems: 'flex-start', marginBottom: 4 }}>
            <div style={{ width: 96, height: 96, border: '1px solid #888', background: '#f4f4f4', overflow: 'hidden', flexShrink: 0 }}>
              <img src={refPreview} alt="reference" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
            </div>
            <div style={{ flex: 1, fontSize: 9, color: '#444' }}>
              <div style={{ wordBreak: 'break-all', marginBottom: 3 }}>{refFile.name}</div>
              <div style={{ color: '#888', marginBottom: 4 }}>
                {(refFile.size / 1024).toFixed(0)} KB · {refFile.type.replace('image/', '')}
              </div>
              <button className="w95-btn" onClick={clearRef} disabled={busy}
                style={{ fontSize: 9, padding: '1px 6px' }}>Replace</button>
            </div>
          </div>
        ) : (
          <div
            onDragOver={(e) => e.preventDefault()}
            onDrop={onDrop}
            onClick={() => fileInputRef.current && fileInputRef.current.click()}
            style={{
              border: '1px dashed #888', background: '#fafafa', padding: 14,
              fontSize: 10, color: '#666', textAlign: 'center', cursor: 'pointer',
              marginBottom: 4,
            }}>
            Drop a PNG/JPG/WebP here or click to pick — up to 8 MB.
          </div>
        )}
        <input ref={fileInputRef} type="file" accept="image/*" style={{ display: 'none' }}
          onChange={(e) => onPickFile(e.target.files && e.target.files[0])} />

        <div style={{ fontSize: 9, fontWeight: 'bold', marginTop: 6, marginBottom: 3 }}>
          Steering keyword <span style={{ fontWeight: 'normal', color: '#888' }}>(optional)</span>
        </div>
        <div style={{ display: 'flex', gap: 4 }}>
          <input className="w95-input" value={keyword}
            placeholder='e.g. "lonely planet", "9pm jakarta" — flavours the copy only'
            onChange={(e) => setKeyword(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter' && !busy && refFile) generate(); }}
            style={{ flex: 1, fontSize: 11 }} disabled={busy} />
          <button className="w95-btn" disabled={busy || !refFile} onClick={generate} style={{
            background: 'var(--ap-pink-deep)', color: '#fff', fontWeight: 'bold',
            minWidth: 80,
          }}>{busy ? '…' : '✦ Generate'}</button>
        </div>
        <div style={{ fontSize: 8, color: '#666', marginTop: 3, lineHeight: 1.4 }}>
          Gemini analyses your reference, regenerates the design ~10% cleaner on
          a transparent background, then composites it onto two blank tees in
          colours it picks from the cached palette. ~60-90s end-to-end.
        </div>
        {busy && (
          <div style={{ marginTop: 6, padding: 6, background: 'var(--ap-mint)', border: '1px solid #000', display: 'flex', alignItems: 'center', gap: 6 }}>
            <Hourglass size={16} />
            <span style={{ fontSize: 10 }}>{progress || 'starting…'}</span>
          </div>
        )}
        {error && <div style={{ color: '#a00', fontSize: 9, marginTop: 4 }}>{error}</div>}
      </div>

      <div className="w95-inset" style={{ background: '#fff', padding: 6, marginBottom: 6 }}>
        <div style={{ fontSize: 9, fontWeight: 'bold', marginBottom: 3 }}>
          Blank mockup cache
        </div>
        <div style={{ fontSize: 8, color: '#666', marginBottom: 4, lineHeight: 1.4 }}>
          The generator composites onto pre-rendered blank tees stored at
          <code style={{ background: '#f4f4f4', padding: '0 2px' }}> mockup-blanks/&lt;key&gt;.png</code>.
          Run this once before your first generation. Idempotent — only creates
          missing colours.
        </div>
        <div style={{ display: 'flex', gap: 4 }}>
          <button className="w95-btn" disabled={seeding || busy} onClick={() => seedBlanks(false)}
            style={{ fontSize: 10 }}>{seeding ? 'Seeding…' : 'Seed missing blanks'}</button>
          <button className="w95-btn" disabled={seeding || busy} onClick={() => seedBlanks(true)}
            style={{ fontSize: 10 }}>Force re-seed all</button>
        </div>
      </div>

      <div style={{ fontSize: 9, fontWeight: 'bold', marginBottom: 4 }}>
        Pending drafts ({drafts.length})
      </div>
      {loadingList ? <AdminLoader label="loading drafts…" /> :
       drafts.length === 0 ? <AdminEmpty>no drafts yet — generate one above</AdminEmpty> :
       drafts.map((d) => <AdminDraftCard key={d.draftId} draft={d}
         onPublished={(slug) => { onSaved(`Published: ${slug}`); loadDrafts(); }}
         onDiscarded={() => { onSaved('Draft discarded'); loadDrafts(); }} />)}
    </>
  );
}

function AdminDraftCard({ draft, onPublished, onDiscarded }) {
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [name, setName] = React.useState(draft.name);
  const [price, setPrice] = React.useState(String(draft.price || 940));
  const [slug, setSlug] = React.useState(draft.slug);
  const [upscaling, setUpscaling] = React.useState(false);
  const [upscaled, setUpscaled] = React.useState(draft.upscaledDesignUrl ? {
    url:    draft.upscaledDesignUrl,
    factor: draft.upscaledFactor || 4,
    size:   draft.upscaledSize || null,
    bytes:  draft.upscaledBytes || null,
  } : null);
  const collections = window.COLLECTIONS || [];
  const [cols, setCols] = React.useState(['all', 'tshirts'].filter((id) =>
    collections.find((c) => c.id === id)
  ));
  const toggleCol = (id) => setCols((p) => p.includes(id) ? p.filter((x) => x !== id) : [...p, id]);

  const publish = async () => {
    setBusy(true); setErr('');
    try {
      const res = await window.adminPublishDraft(draft.draftId, {
        name: name.trim(),
        slug: slug.trim(),
        price: Number(price),
        cols,
      });
      onPublished(res.slug);
    } catch (e) {
      setErr(e.message || 'Publish failed.');
      setBusy(false);
    }
  };

  const upscale = async (factor = 4) => {
    setUpscaling(true); setErr('');
    try {
      const res = await window.adminUpscaleDesign(draft.draftId, factor);
      setUpscaled({
        url: res.url, factor: res.factor,
        size: { width: res.width, height: res.height }, bytes: res.bytes,
      });
    } catch (e) {
      setErr(e.message || 'Upscale failed.');
    } finally {
      setUpscaling(false);
    }
  };

  const discard = async () => {
    if (!window.confirm('Discard this draft? Images stay in Storage but the draft hides from this list.')) return;
    setBusy(true); setErr('');
    try {
      await window.adminDiscardDraft(draft.draftId);
      onDiscarded();
    } catch (e) {
      setErr(e.message || 'Failed.');
      setBusy(false);
    }
  };

  return (
    <div className="w95-inset" style={{ background: '#fff', padding: 6, marginBottom: 6, fontSize: 10 }}>
      <div style={{ display: 'flex', gap: 4, marginBottom: 4 }}>
        {(draft.imageUrls || []).map((u, i) => (
          <div key={i} style={{ flex: 1, aspectRatio: '1/1', background: '#fff', border: '1px solid #888', overflow: 'hidden', position: 'relative' }}>
            <img src={u} alt={`${draft.name || 'draft'} – view ${i + 1}`} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
            <div style={{
              position: 'absolute', bottom: 2, left: 2, right: 2,
              background: (draft.colors && draft.colors[i] && draft.colors[i].hex) || '#000',
              border: '1px solid #000', padding: '0 3px', fontSize: 8,
              color: '#fff', fontWeight: 'bold', textShadow: '0 0 2px #000',
              textAlign: 'center',
            }}>
              {(draft.colors && draft.colors[i] && draft.colors[i].label) || 'colour'}
            </div>
          </div>
        ))}
      </div>

      <div style={{ fontSize: 8, color: '#888', marginBottom: 3 }}>
        keyword: <span style={{ color: '#000' }}>{draft.keyword}</span> · {fmtDate(draft.createdAt)}
      </div>

      <FieldRow label="Name">
        <input className="w95-input" value={name} onChange={(e) => setName(e.target.value)}
          style={{ flex: 1, fontSize: 10 }} />
      </FieldRow>
      <FieldRow label="Slug">
        <input className="w95-input" value={slug} onChange={(e) => setSlug(e.target.value)}
          style={{ flex: 1, fontSize: 10, fontFamily: 'monospace' }} />
      </FieldRow>
      <FieldRow label="Price ₹">
        <input className="w95-input" type="number" value={price} onChange={(e) => setPrice(e.target.value)}
          style={{ flex: 1, fontSize: 10 }} />
      </FieldRow>

      <div style={{ marginBottom: 4 }}>
        <div style={{ fontSize: 9, fontWeight: 'bold', marginBottom: 2 }}>Description</div>
        <div style={{ background: '#f8f8f8', padding: 4, fontSize: 9, lineHeight: 1.4, whiteSpace: 'pre-wrap', border: '1px solid #ddd' }}>
          {draft.longDescription}
        </div>
      </div>
      <div style={{ marginBottom: 4 }}>
        <div style={{ fontSize: 9, fontWeight: 'bold', marginBottom: 2 }}>Design brief</div>
        <div style={{ background: '#f8f8f8', padding: 4, fontSize: 9, lineHeight: 1.4, color: '#444', border: '1px solid #ddd' }}>
          {draft.designDescription}
        </div>
      </div>
      {draft.refinementNotes && (
        <div style={{ marginBottom: 4 }}>
          <div style={{ fontSize: 9, fontWeight: 'bold', marginBottom: 2 }}>Refinement applied</div>
          <div style={{ background: '#fff8e1', padding: 4, fontSize: 9, lineHeight: 1.4, color: '#444', border: '1px solid #e8d8a0' }}>
            {draft.refinementNotes}
          </div>
        </div>
      )}
      {draft.designUrl && (
        <div style={{ marginBottom: 4 }}>
          <div style={{ fontSize: 9, fontWeight: 'bold', marginBottom: 2 }}>
            Cut-out artwork
            {(draft.libraryUrl || upscaled) && (
              <span style={{ fontWeight: 'normal', color: '#666', marginLeft: 4 }}>
                · saved to design library
              </span>
            )}
          </div>
          <div style={{ display: 'flex', gap: 6, alignItems: 'flex-start' }}>
            <a href={draft.designUrl} target="_blank" rel="noopener noreferrer"
              title="Open original cut-out in new tab"
              style={{ width: 80, height: 80, border: '1px solid #888', background: 'repeating-conic-gradient(#ddd 0% 25%, #fff 0% 50%) 50% / 16px 16px', display: 'block', flexShrink: 0 }}>
              <img src={draft.designUrl} alt="design transparent" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
            </a>
            <div style={{ flex: 1, fontSize: 9, color: '#444', lineHeight: 1.4 }}>
              {!upscaled ? (
                <>
                  <div style={{ marginBottom: 3 }}>
                    Upscale this design with sharp lanczos3 + light sharpen and
                    save the print master to the permanent design library.
                  </div>
                  <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                    {[2, 4].map((f) => (
                      <button key={f} className="w95-btn" disabled={upscaling || busy}
                        onClick={() => upscale(f)}
                        style={{ fontSize: 9, padding: '1px 6px' }}>
                        {upscaling ? '…' : `↑ ${f}× & save`}
                      </button>
                    ))}
                  </div>
                </>
              ) : (
                <>
                  <div style={{ marginBottom: 3, color: '#0a7d34', fontWeight: 'bold' }}>
                    ✓ Upscaled {upscaled.factor}×
                    {upscaled.size && ` → ${upscaled.size.width}×${upscaled.size.height}px`}
                    {upscaled.bytes && ` (${(upscaled.bytes / 1024).toFixed(0)} KB)`}
                  </div>
                  <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', alignItems: 'center' }}>
                    <a href={upscaled.url} target="_blank" rel="noopener noreferrer"
                      className="w95-btn"
                      style={{ fontSize: 9, padding: '1px 6px', textDecoration: 'none', color: '#000' }}>
                      Open full size
                    </a>
                    <a href={upscaled.url} download={`${draft.slug || draft.draftId}-${upscaled.factor}x.png`}
                      className="w95-btn"
                      style={{ fontSize: 9, padding: '1px 6px', textDecoration: 'none', color: '#000' }}>
                      Download
                    </a>
                    <button className="w95-btn" disabled={upscaling || busy}
                      onClick={() => upscale(upscaled.factor === 2 ? 4 : 2)}
                      style={{ fontSize: 9, padding: '1px 6px' }}>
                      {upscaling ? '…' : `re-upscale ${upscaled.factor === 2 ? 4 : 2}×`}
                    </button>
                  </div>
                </>
              )}
            </div>
          </div>
        </div>
      )}

      <div style={{ marginBottom: 4 }}>
        <div style={{ fontSize: 9, fontWeight: 'bold', marginBottom: 2 }}>Collections</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {collections.map((c) => (
            <label key={c.id} style={{ display: 'flex', alignItems: 'center', gap: 3, fontSize: 9 }}>
              <input type="checkbox" checked={cols.includes(c.id)} onChange={() => toggleCol(c.id)} />
              <span>{c.name}</span>
            </label>
          ))}
        </div>
      </div>

      {err && <div style={{ color: '#a00', fontSize: 9, marginBottom: 4 }}>{err}</div>}

      <div style={{ display: 'flex', gap: 4 }}>
        <button className="w95-btn" disabled={busy} onClick={publish} style={{
          flex: 2, background: 'var(--ap-pink-deep)', color: '#fff', fontWeight: 'bold',
        }}>{busy ? 'Working…' : '★ Publish as live product'}</button>
        <button className="w95-btn" disabled={busy} onClick={discard} style={{ flex: 1 }}>
          Discard
        </button>
      </div>
    </div>
  );
}

// Expose to global scope so app.jsx can render it from its switch.
Object.assign(window, { AdminContent });
