// ============================================================
// Saraya Events — ADMIN MODE
// A localStorage-backed "overrides" layer. Public visitors never
// trigger any of this — they always see the default site. Once an
// admin logs in (hidden #admin route or the footer key), edit mode
// turns on and inline controls appear: upload/replace/remove images,
// edit text, and add/remove/reorder catalogue & gallery items.
//
// HOW SAVING WORKS
//   Every change is written to localStorage immediately under the key
//   'saraya_overrides_v1'. It survives refresh, tab close, and reopen
//   on the SAME browser. Uploaded images are compressed to data-URLs
//   so they persist without any server. Use Export/Import in the admin
//   bar to move content between devices or to back it up.
//   (A real multi-visitor CMS needs a backend — see STRATEGY notes.)
// ============================================================

const { useState: useStateAd, useEffect: useEffectAd, useRef: useRefAd, useContext: useContextAd, useCallback: useCallbackAd } = React;

const OVR_KEY = 'saraya_overrides_v1';
const PASS_KEY = 'saraya_admin_code';
const SESS_KEY = 'saraya_admin_session';
const DEFAULT_CODE = 'saraya2026';

/* ---- IndexedDB image store (holds far more than localStorage's ~5MB) ---- */
const IMG_DB = 'saraya_images_db';
const IMG_STORE = 'images';
function idbOpen() {
  return new Promise((res, rej) => {
    let r;
    try { r = indexedDB.open(IMG_DB, 1); } catch (e) { return rej(e); }
    r.onupgradeneeded = () => { if (!r.result.objectStoreNames.contains(IMG_STORE)) r.result.createObjectStore(IMG_STORE); };
    r.onsuccess = () => res(r.result);
    r.onerror = () => rej(r.error);
  });
}
async function idbGetAll() {
  const db = await idbOpen();
  return new Promise((res, rej) => {
    const out = {};
    const tx = db.transaction(IMG_STORE, 'readonly');
    const cur = tx.objectStore(IMG_STORE).openCursor();
    cur.onsuccess = (e) => { const c = e.target.result; if (c) { out[c.key] = c.value; c.continue(); } else res(out); };
    cur.onerror = () => rej(cur.error);
  });
}
async function idbSet(key, val) {
  const db = await idbOpen();
  return new Promise((res, rej) => { const tx = db.transaction(IMG_STORE, 'readwrite'); tx.objectStore(IMG_STORE).put(val, key); tx.oncomplete = () => res(); tx.onerror = () => rej(tx.error); tx.onabort = () => rej(tx.error); });
}
async function idbDel(key) {
  const db = await idbOpen();
  return new Promise((res, rej) => { const tx = db.transaction(IMG_STORE, 'readwrite'); tx.objectStore(IMG_STORE).delete(key); tx.oncomplete = () => res(); tx.onerror = () => rej(tx.error); });
}
async function idbClear() {
  const db = await idbOpen();
  return new Promise((res, rej) => { const tx = db.transaction(IMG_STORE, 'readwrite'); tx.objectStore(IMG_STORE).clear(); tx.oncomplete = () => res(); tx.onerror = () => rej(tx.error); });
}

function blankOverrides() {
  return { v: 1, images: {}, text: {}, entities: {}, catAdded: [], catDeleted: [], storeAdded: [], storeDeleted: [], storeOrder: [], galAdded: [], galDeleted: [], galOrder: [] };
}
function loadOverrides() {
  const baked = (window.__bakedContent && typeof window.__bakedContent === 'object') ? window.__bakedContent : null;
  try {
    const stored = localStorage.getItem(OVR_KEY);
    if (stored) return Object.assign(blankOverrides(), JSON.parse(stored));
    // No local edits yet — fall back to content baked into the build (Netlify export).
    if (baked) { const b = Object.assign(blankOverrides(), baked); b.images = {}; return b; }
    return blankOverrides();
  }
  catch (e) { return blankOverrides(); }
}

/* deep helpers (immutable-ish) */
function dget(obj, path) { return path.split('.').reduce((o, k) => (o == null ? undefined : o[k]), obj); }
function deepMerge(a, b) {
  if (Array.isArray(b)) return b.slice();
  if (b && typeof b === 'object') {
    const out = Object.assign({}, a && typeof a === 'object' && !Array.isArray(a) ? a : {});
    Object.keys(b).forEach((k) => { out[k] = deepMerge(out[k], b[k]); });
    return out;
  }
  return b;
}
function nestFromFlat(flat) {
  // { 'name.en':'x', color:'y' } -> { name:{en:'x'}, color:'y' }
  const out = {};
  Object.keys(flat).forEach((key) => {
    const parts = key.split('.'); let cur = out;
    parts.forEach((p, i) => { if (i === parts.length - 1) cur[p] = flat[key]; else cur = (cur[p] = cur[p] || {}); });
  });
  return out;
}

/* compress an uploaded image to a reasonably-sized JPEG data-URL */
function compressImage(file, maxDim, quality) {
  maxDim = maxDim || 1700; quality = quality || 0.84;
  return new Promise((resolve, reject) => {
    const type = (file.type || '').toLowerCase();
    if (type.includes('svg') || type.includes('gif')) {
      const r = new FileReader(); r.onload = () => resolve(r.result); r.onerror = reject; r.readAsDataURL(file); return;
    }
    const url = URL.createObjectURL(file);
    const img = new Image();
    img.onload = () => {
      let { width: w, height: h } = img;
      if (Math.max(w, h) > maxDim) { const s = maxDim / Math.max(w, h); w = Math.round(w * s); h = Math.round(h * s); }
      const c = document.createElement('canvas'); c.width = w; c.height = h;
      const ctx = c.getContext('2d'); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, w, h); ctx.drawImage(img, 0, 0, w, h);
      URL.revokeObjectURL(url);
      try { resolve(c.toDataURL('image/jpeg', quality)); } catch (e) { reject(e); }
    };
    img.onerror = reject; img.src = url;
  });
}

/* ---------------- Provider ---------------- */
function AdminProvider({ children }) {
  const [overrides, setOverrides] = useStateAd(loadOverrides);
  const [images, setImages] = useStateAd({});   // slot -> dataURL, backed by IndexedDB
  const [isAdmin, setIsAdmin] = useStateAd(() => localStorage.getItem(SESS_KEY) === '1');
  const [editMode, setEditMode] = useStateAd(() => localStorage.getItem(SESS_KEY) === '1');
  const [loginOpen, setLoginOpen] = useStateAd(false);
  const [editConfig, setEditConfig] = useStateAd(null);
  const [ordersOpen, setOrdersOpen] = useStateAd(false);
  const [subsOpen, setSubsOpen] = useStateAd(false);
  const [stripeOpen, setStripeOpen] = useStateAd(false);
  const [toast, setToast] = useStateAd(null);
  const [savedAt, setSavedAt] = useStateAd(null);
  const fileResolver = useRefAd(null);

  /* load images from IndexedDB on mount; migrate any legacy localStorage images */
  useEffectAd(() => {
    let cancelled = false;
    (async () => {
      let imgs = {};
      try { imgs = await idbGetAll(); } catch (e) { imgs = {}; }
      // Seed from images baked into the build (Netlify export) for any slot not already set locally.
      const baked = (window.__bakedContent && window.__bakedContent.images) || null;
      if (baked) { for (const k of Object.keys(baked)) { if (imgs[k] == null) imgs[k] = baked[k]; } }
      const legacy = (overrides.images && Object.keys(overrides.images).length) ? overrides.images : null;
      if (legacy) {
        for (const k of Object.keys(legacy)) { try { await idbSet(k, legacy[k]); imgs[k] = legacy[k]; } catch (e) {} }
        setOverrides((prev) => Object.assign({}, prev, { images: {} }));
      }
      if (!cancelled) setImages(imgs);
    })();
    return () => { cancelled = true; };
  }, []);

  /* persist on every change */
  useEffectAd(() => {
    try { localStorage.setItem(OVR_KEY, JSON.stringify(overrides)); setSavedAt(Date.now()); }
    catch (e) { setToast({ type: 'error', msg: 'Storage is full. Remove a few images or Export, then try again.' }); }
  }, [overrides]);

  /* propagate editable contact info site-wide by mutating the shared object */
  useEffectAd(() => {
    const C = window.SARAYA.CONTACT; const tx = overrides.text || {};
    ['phoneDisplay', 'phoneIntl', 'email', 'city', 'showroom', 'instagram'].forEach((k) => {
      const v = tx['contact.' + k]; if (v != null && v !== '') C[k] = v;
    });
  }, [overrides.text]);

  /* Sync admin status from Supabase auth role (when configured).
     Falls back to the localStorage passcode when Supabase is not set up. */
  useEffectAd(() => {
    const db = window.SarayaDB;
    if (!db) return;
    const { data: { subscription } } = db.auth.onAuthStateChange(async (_event, session) => {
      if (session?.user) {
        const { data: p } = await db.from('profiles').select('role').eq('id', session.user.id).single();
        if (p?.role === 'admin') {
          setIsAdmin(true);
          setEditMode(true);
          localStorage.setItem(SESS_KEY, '1');
        } else if (p && p.role !== 'admin') {
          setIsAdmin(false);
          setEditMode(false);
          localStorage.removeItem(SESS_KEY);
        }
      } else if (window.supabaseConfigured) {
        setIsAdmin(false);
        setEditMode(false);
        localStorage.removeItem(SESS_KEY);
      }
    });
    return () => subscription?.unsubscribe();
  }, []);

  /* hidden access: visiting #admin opens the login */
  useEffectAd(() => {
    const check = () => { if ((location.hash || '').toLowerCase() === '#admin') { setLoginOpen(true); location.hash = '#home'; } };
    window.addEventListener('hashchange', check); check();
    return () => window.removeEventListener('hashchange', check);
  }, []);

  const toastMsg = useCallbackAd((msg, type) => { setToast({ msg, type: type || 'ok' }); setTimeout(() => setToast(null), 2600); }, []);

  /* ----- auth ----- */
  const code = () => localStorage.getItem(PASS_KEY) || DEFAULT_CODE;
  const login = useCallbackAd((entered) => {
    if (entered === code()) { setIsAdmin(true); setEditMode(true); localStorage.setItem(SESS_KEY, '1'); setLoginOpen(false); toastMsg('Welcome back — admin mode on'); return true; }
    return false;
  }, []);
  const logout = useCallbackAd(() => { setIsAdmin(false); setEditMode(false); localStorage.removeItem(SESS_KEY); setEditConfig(null); }, []);
  const changeCode = useCallbackAd(() => {
    const n = window.prompt('Set a new admin access code (min 4 characters):'); if (n == null) return;
    if (n.length < 4) { toastMsg('Code too short', 'error'); return; }
    localStorage.setItem(PASS_KEY, n); toastMsg('Access code updated');
  }, []);

  /* ----- mutators ----- */
  const commit = useCallbackAd((fn) => setOverrides((prev) => { const next = JSON.parse(JSON.stringify(prev)); fn(next); return next; }), []);

  const setImg = useCallbackAd(async (slot, dataUrl) => {
    try {
      await idbSet(slot, dataUrl);
      setImages((p) => Object.assign({}, p, { [slot]: dataUrl }));
      setSavedAt(Date.now());
      return true;
    } catch (e) {
      setToast({ type: 'error', msg: 'Could not save that image. Try a smaller file.' });
      return false;
    }
  }, []);
  const removeImg = useCallbackAd(async (slot) => {
    try { await idbDel(slot); } catch (e) {}
    setImages((p) => { const n = Object.assign({}, p); delete n[slot]; return n; });
  }, []);
  const getImg = useCallbackAd((slot) => {
    if (!slot) return null;
    if (images[slot]) return images[slot];
    const baked = (window.__bakedContent && window.__bakedContent.images) || null;
    return (baked && baked[slot]) || null;
  }, [images]);

  const pickImage = useCallbackAd((slot) => {
    const input = document.createElement('input');
    input.type = 'file'; input.accept = 'image/*';
    input.style.position = 'fixed'; input.style.left = '-9999px'; input.style.opacity = '0';
    const cleanup = () => { if (input.parentNode) input.parentNode.removeChild(input); };
    input.onchange = async () => {
      const f = input.files && input.files[0];
      if (!f) { cleanup(); return; }
      try {
        const data = await compressImage(f);
        const ok = await setImg(slot, data);
        if (ok) { toastMsg('Image saved'); try { window.dispatchEvent(new CustomEvent('saraya:image-uploaded', { detail: { slot: slot, fileName: f.name } })); } catch (e2) {} }
      } catch (e) { toastMsg('Could not read that image', 'error'); }
      cleanup();
    };
    // appending to the document makes the file dialog reliable across browsers
    document.body.appendChild(input);
    input.click();
  }, [setImg, toastMsg]);

  const mergeEntity = useCallbackAd((id, partial) => commit((o) => { o.entities[id] = deepMerge(o.entities[id] || {}, partial); }), [commit]);
  const resolveEntity = useCallbackAd((id, def) => { const e = overrides.entities[id]; return e ? deepMerge(def, e) : def; }, [overrides.entities]);

  const getText = useCallbackAd((key, fallback) => { const v = overrides.text[key]; return v != null && v !== '' ? v : fallback; }, [overrides.text]);
  const setText = useCallbackAd((key, val) => commit((o) => { o.text[key] = val; }), [commit]);

  /* catalogue */
  const resolveCatalogue = useCallbackAd(() => {
    const base = window.SARAYA.CATALOGUE_ITEMS.concat(overrides.catAdded || []);
    return base.filter((i) => !(overrides.catDeleted || []).includes(i.id)).map((i) => resolveEntity('cat:' + i.id, i));
  }, [overrides.catAdded, overrides.catDeleted, overrides.entities, resolveEntity]);
  const addCatalogueItem = useCallbackAd((item) => commit((o) => { o.catAdded.push(item); }), [commit]);
  const deleteCatalogueItem = useCallbackAd((id) => { idbDel('cat:' + id); removeImg('cat:' + id); commit((o) => {
    if ((o.catAdded || []).some((x) => x.id === id)) o.catAdded = o.catAdded.filter((x) => x.id !== id);
    else if (!o.catDeleted.includes(id)) o.catDeleted.push(id);
    delete o.entities['cat:' + id];
  }); }, [commit, removeImg]);

  /* gallery */
  const resolveGallery = useCallbackAd(() => {
    let base = window.SARAYA.GALLERY.concat(overrides.galAdded || []).filter((g) => !(overrides.galDeleted || []).includes(g.id));
    base = base.map((g) => resolveEntity('gal:' + g.id, g));
    const order = overrides.galOrder || [];
    if (order.length) base.sort((a, b) => { const ia = order.indexOf(a.id), ib = order.indexOf(b.id); return (ia < 0 ? 999 : ia) - (ib < 0 ? 999 : ib); });
    return base;
  }, [overrides.galAdded, overrides.galDeleted, overrides.galOrder, overrides.entities, resolveEntity]);
  const addGalleryItem = useCallbackAd((item) => commit((o) => { o.galAdded.push(item); }), [commit]);
  const deleteGalleryItem = useCallbackAd((id) => { idbDel('gal:' + id); removeImg('gal:' + id); commit((o) => {
    if ((o.galAdded || []).some((x) => x.id === id)) o.galAdded = o.galAdded.filter((x) => x.id !== id);
    else if (!o.galDeleted.includes(id)) o.galDeleted.push(id);
    o.galOrder = (o.galOrder || []).filter((x) => x !== id);
    delete o.entities['gal:' + id];
  }); }, [commit, removeImg]);
  const moveGallery = useCallbackAd((id, dir) => {
    const cur = resolveGallery().map((g) => g.id);
    const i = cur.indexOf(id); const j = i + dir;
    if (i < 0 || j < 0 || j >= cur.length) return;
    const tmp = cur[i]; cur[i] = cur[j]; cur[j] = tmp;
    commit((o) => { o.galOrder = cur; });
  }, [resolveGallery, commit]);

  /* store (retail) */
  const resolveStore = useCallbackAd(() => {
    let base = window.SARAYA.STORE_ITEMS.concat(overrides.storeAdded || []);
    base = base.filter((i) => !(overrides.storeDeleted || []).includes(i.id)).map((i) => resolveEntity('store:' + i.id, i));
    const order = overrides.storeOrder || [];
    if (order.length) base.sort((a, b) => { const ia = order.indexOf(a.id), ib = order.indexOf(b.id); return (ia < 0 ? 999 : ia) - (ib < 0 ? 999 : ib); });
    return base;
  }, [overrides.storeAdded, overrides.storeDeleted, overrides.storeOrder, overrides.entities, resolveEntity]);
  const moveStore = useCallbackAd((id, dir) => {
    const ordered = resolveStore();
    const cur = ordered.map((i) => i.id);
    const item = ordered.find((i) => i.id === id);
    if (!item) return;
    const sameCat = ordered.filter((i) => i.category === item.category);
    const ci = sameCat.findIndex((i) => i.id === id); const nj = ci + dir;
    if (nj < 0 || nj >= sameCat.length) return;
    const ii = cur.indexOf(id), jj = cur.indexOf(sameCat[nj].id);
    const tmp = cur[ii]; cur[ii] = cur[jj]; cur[jj] = tmp;
    commit((o) => { o.storeOrder = cur; });
  }, [resolveStore, commit]);
  const addStoreItem = useCallbackAd((item) => commit((o) => { (o.storeAdded = o.storeAdded || []).push(item); }), [commit]);
  const deleteStoreItem = useCallbackAd((id) => { idbDel('store:' + id); removeImg('store:' + id); commit((o) => {
    if ((o.storeAdded || []).some((x) => x.id === id)) o.storeAdded = o.storeAdded.filter((x) => x.id !== id);
    else { o.storeDeleted = o.storeDeleted || []; if (!o.storeDeleted.includes(id)) o.storeDeleted.push(id); }
    delete o.entities['store:' + id];
  }); }, [commit, removeImg]);

  /* data import / export / reset */
  const exportData = useCallbackAd(() => {
    const payload = Object.assign({}, overrides, { images: images });
    const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
    const a = document.createElement('a'); a.href = URL.createObjectURL(blob);
    a.download = 'saraya-content-' + new Date().toISOString().slice(0, 10) + '.json'; a.click();
    setTimeout(() => URL.revokeObjectURL(a.href), 1000);
  }, [overrides, images]);
  const importData = useCallbackAd(() => {
    const input = document.createElement('input'); input.type = 'file'; input.accept = 'application/json,.json';
    input.style.position = 'fixed'; input.style.left = '-9999px';
    const cleanup = () => { if (input.parentNode) input.parentNode.removeChild(input); };
    input.onchange = () => { const f = input.files[0]; if (!f) { cleanup(); return; } const r = new FileReader();
      r.onload = async () => {
        try {
          const parsed = JSON.parse(r.result);
          const imgs = parsed.images || {};
          try { await idbClear(); } catch (e) {}
          for (const k of Object.keys(imgs)) { try { await idbSet(k, imgs[k]); } catch (e) {} }
          setImages(imgs);
          const rest = Object.assign(blankOverrides(), parsed); rest.images = {};
          setOverrides(rest);
          toastMsg('Content imported');
        } catch (e) { toastMsg('Invalid file', 'error'); }
        cleanup();
      };
      r.readAsText(f); };
    document.body.appendChild(input); input.click();
  }, [toastMsg]);
  const resetData = useCallbackAd(() => { if (window.confirm('Reset ALL admin changes back to the original site? This cannot be undone.')) { idbClear().catch(() => {}); setImages({}); setOverrides(blankOverrides()); toastMsg('Reset to defaults'); } }, [toastMsg]);

  const api = {
    isAdmin, editMode, setEditMode, openLogin: () => setLoginOpen(true), logout, changeCode,
    getImg, setImg, removeImg, pickImage,
    mergeEntity, resolveEntity, getText, setText,
    resolveCatalogue, addCatalogueItem, deleteCatalogueItem,
    resolveStore, addStoreItem, deleteStoreItem, moveStore,
    resolveGallery, addGalleryItem, deleteGalleryItem, moveGallery,
    openEdit: setEditConfig, openOrders: () => setOrdersOpen(true), openSubs: () => setSubsOpen(true), openStripe: () => setStripeOpen(true), exportData, importData, resetData, toastMsg, savedAt,
    overrides,
  };

  return (
    <window.AdminContext.Provider value={api}>
      {children}
      {loginOpen && <AdminLogin onClose={() => setLoginOpen(false)} onSubmit={login} />}
      {isAdmin && <AdminBar api={api} />}
      {editConfig && <EditPanel config={editConfig} onClose={() => setEditConfig(null)} />}
      {ordersOpen && <AdminOrdersPanel onClose={() => setOrdersOpen(false)} />}
      {subsOpen && <AdminSubsPanel onClose={() => setSubsOpen(false)} />}
      {stripeOpen && <StripeSettings onClose={() => setStripeOpen(false)} />}
      {toast && <AdminToast toast={toast} />}
    </window.AdminContext.Provider>
  );
}

/* convenience hook */
function useAdmin() { return useContextAd(window.AdminContext); }

/* ---------------- Login modal ---------------- */
function AdminLogin({ onClose, onSubmit }) {
  const [val, setVal] = useStateAd(''); const [err, setErr] = useStateAd(false);
  const submit = (e) => { e.preventDefault(); if (!onSubmit(val.trim())) { setErr(true); setVal(''); } };
  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 300, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'var(--scrim)' }} />
      <form onSubmit={submit} style={{ position: 'relative', width: 'min(380px,100%)', background: 'var(--white)', borderRadius: 18, padding: 32, boxShadow: 'var(--shadow-deep)', textAlign: 'center' }}>
        <span style={{ display: 'inline-flex', width: 56, height: 56, borderRadius: 999, alignItems: 'center', justifyContent: 'center', background: 'var(--espresso)', color: 'var(--gold-light)' }}><Icon name="lock" size={26} /></span>
        <h3 style={{ fontFamily: 'var(--font-display)', fontSize: 26, fontWeight: 500, margin: '16px 0 4px' }}>Admin Access</h3>
        <p style={{ fontSize: 13.5, color: 'var(--fg-muted)', marginBottom: 20 }}>Enter your access code to manage content.</p>
        <input autoFocus type="password" value={val} onChange={(e) => { setVal(e.target.value); setErr(false); }} placeholder="Access code"
          style={{ width: '100%', textAlign: 'center', letterSpacing: '0.2em', fontFamily: 'var(--font-body)', fontSize: 16, padding: '13px 15px', borderRadius: 10, border: '1px solid ' + (err ? 'var(--error)' : 'var(--line-strong)'), outline: 'none' }} />
        {err && <div style={{ color: 'var(--error)', fontSize: 12.5, marginTop: 8 }}>Incorrect code. Try again.</div>}
        <div style={{ display: 'flex', gap: 10, marginTop: 20 }}>
          <Button variant="secondary" full onClick={onClose}>Cancel</Button>
          <Button variant="primary" full type="submit" icon="log-in">Enter</Button>
        </div>
        <p style={{ fontSize: 11, color: 'var(--fg-muted)', marginTop: 16 }}>Visitors never see this. Default code: <b>saraya2026</b> — change it after first login.</p>
      </form>
    </div>
  );
}

/* ---------------- Admin bar (docked left side) ---------------- */
const ADMIN_NAV = [
  {
    group: 'Platform',
    icon: 'layout-dashboard',
    items: [
      { label: 'Admin Dashboard',      tab: 'overview',    icon: 'layout-dashboard' },
      { label: 'Orders & Bookings',    tab: 'orders',      icon: 'package',         badge: 'orders' },
      { label: 'RFQ Requests',         tab: 'rfqs',        icon: 'file-text',        badge: 'rfqs' },
      { label: 'Leads & Inquiries',    tab: 'leads',       icon: 'inbox' },
      { label: 'Complaints & Disputes',tab: 'complaints',  icon: 'alert-circle',    badge: 'complaints' },
    ],
  },
  {
    group: 'Vendors',
    icon: 'store',
    items: [
      { label: 'Vendor Applications',  tab: 'vendors',     icon: 'user-check',      badge: 'vendors' },
      { label: 'Approved Vendors',     tab: 'vendors',     icon: 'store' },
      { label: 'Vendor Documents',     tab: 'vendors',     icon: 'file-badge' },
      { label: 'Vendor Packages',      tab: 'tiers',       icon: 'layers' },
      { label: 'Vendor Subscriptions', tab: 'tiers',       icon: 'repeat' },
      { label: 'Vendor Listings',      tab: 'marketplace', icon: 'grid-2x2' },
    ],
  },
  {
    group: 'Marketplace',
    icon: 'shopping-bag',
    items: [
      { label: 'Products',             tab: 'marketplace', icon: 'shopping-bag' },
      { label: 'Rental Items',         tab: 'marketplace', icon: 'archive' },
      { label: 'Services',             tab: 'marketplace', icon: 'briefcase' },
      { label: 'Categories',           tab: 'marketplace', icon: 'tag' },
      { label: 'Featured / Sponsored', tab: 'marketing',   icon: 'star' },
      { label: 'Virtual Showroom',     tab: 'marketplace', icon: 'monitor' },
    ],
  },
  {
    group: 'Finance',
    icon: 'banknote',
    items: [
      { label: 'Payments',             tab: 'finance',     icon: 'credit-card' },
      { label: 'Vendor Payouts',       tab: 'payouts',     icon: 'banknote' },
      { label: 'Commissions',          tab: 'finance',     icon: 'percent' },
      { label: 'Refunds',              tab: 'finance',     icon: 'rotate-ccw' },
      { label: 'Stripe Settings',      action: 'stripe',   icon: 'zap' },
    ],
  },
  {
    group: 'Marketing',
    icon: 'megaphone',
    items: [
      { label: 'Campaign Banners',     tab: 'marketing',   icon: 'image' },
      { label: 'Popup Campaigns',      tab: 'marketing',   icon: 'bell' },
      { label: 'Featured Items',       tab: 'marketing',   icon: 'star' },
      { label: 'Customer Leads',       tab: 'leads',       icon: 'users' },
      { label: 'Vendor Leads',         tab: 'leads',       icon: 'store' },
    ],
  },
  {
    group: 'Content',
    icon: 'file-text',
    items: [
      { label: 'Home Page Content',    tab: 'content',     icon: 'home' },
      { label: 'Marketplace Content',  tab: 'content',     icon: 'shopping-bag' },
      { label: 'Rentals Content',      tab: 'content',     icon: 'archive' },
      { label: 'Virtual Showroom',     tab: 'content',     icon: 'monitor' },
      { label: 'Legal Pages',          tab: 'content',     icon: 'scale' },
      { label: 'Footer / Navigation',  tab: 'content',     icon: 'navigation' },
    ],
  },
];

function AdminBar({ api }) {
  const [open,    setOpen]    = useStateAd(() => localStorage.getItem('saraya_adminbar_open') !== '0');
  const [expanded,setExpanded]= useStateAd(() => {
    try { return JSON.parse(localStorage.getItem('saraya_adminbar_exp') || '["Platform","Vendors"]'); } catch(e) { return ['Platform','Vendors']; }
  });

  // Detect when we're on the admin-dashboard so we can hide the duplicate ADMIN_NAV
  const [onDash, setOnDash] = useStateAd(() => location.hash.startsWith('#admin-dashboard'));
  useEffectAd(() => {
    const onHash = () => setOnDash(location.hash.startsWith('#admin-dashboard'));
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);

  const toggle = (v) => { setOpen(v); localStorage.setItem('saraya_adminbar_open', v ? '1' : '0'); };
  const toggleGroup = (g) => {
    const next = expanded.includes(g) ? expanded.filter((x) => x !== g) : [...expanded, g];
    setExpanded(next);
    try { localStorage.setItem('saraya_adminbar_exp', JSON.stringify(next)); } catch(e) {}
  };

  const goTab = (tab) => {
    if (tab) {
      localStorage.setItem('saraya_admin_tab', tab);
      if (window.setAdminDashboardTab) { window.setAdminDashboardTab(tab); }
      location.hash = '#admin-dashboard';
    }
  };

  const handleItem = (item) => {
    if (item.action === 'stripe') { api.openStripe(); return; }
    if (item.tab) goTab(item.tab);
  };

  return (
    <div style={{ position: 'fixed', insetInlineStart: 0, top: 0, bottom: 0, zIndex: 200, fontFamily: 'var(--font-body)', display: 'flex', alignItems: 'flex-start', pointerEvents: 'none' }}>
      {open ? (
        <div style={{ width: 256, height: '100vh', background: 'var(--espresso)', color: 'var(--ivory)', boxShadow: '4px 0 24px rgba(42,31,26,0.28)', display: 'flex', flexDirection: 'column', pointerEvents: 'all' }}>

          {/* Header */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '18px 16px 14px', borderBottom: '1px solid rgba(250,246,240,0.10)', flexShrink: 0 }}>
            <Icon name="shield-check" size={18} style={{ color: 'var(--gold-light)' }} />
            <span style={{ fontWeight: 700, fontSize: 13.5, letterSpacing: '0.04em', color: 'var(--gold-light)', flex: 1 }}>ADMIN MODE</span>
            <button onClick={() => toggle(false)} style={adBtnGhost} title="Collapse"><Icon name="panel-left-close" size={17} /></button>
          </div>

          {/* Dashboard button */}
          <div style={{ padding: '10px 12px 4px', flexShrink: 0 }}>
            <button onClick={() => goTab('overview')} style={{ display: 'flex', alignItems: 'center', gap: 9, width: '100%', padding: '10px 12px', borderRadius: 10, background: 'var(--gold)', color: 'var(--espresso)', border: 'none', cursor: 'pointer', fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 700 }}>
              <Icon name="layout-dashboard" size={15} /> Admin Dashboard
            </button>
          </div>

          {/* Content editing toggle */}
          <div style={{ padding: '8px 12px 4px', flexShrink: 0 }}>
            <label style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 12.5, cursor: 'pointer', padding: '8px 12px', borderRadius: 8, background: 'rgba(250,246,240,0.06)' }}>
              <span onClick={() => api.setEditMode(!api.editMode)} style={{ width: 34, height: 20, borderRadius: 999, background: api.editMode ? '#C9A961' : 'rgba(250,246,240,0.22)', position: 'relative', flexShrink: 0, transition: 'background 200ms' }}>
                <span style={{ position: 'absolute', top: 2, left: api.editMode ? 16 : 2, width: 16, height: 16, borderRadius: 999, background: '#fff', transition: 'left 200ms' }} />
              </span>
              <span style={{ color: api.editMode ? 'var(--gold-light)' : 'rgba(250,246,240,0.7)', fontWeight: api.editMode ? 600 : 400 }}>
                Content Editing Mode
              </span>
            </label>
          </div>

          {/* Scrollable nav */}
          <div style={{ flex: 1, overflowY: 'auto', padding: '6px 0 8px' }}>
            {/* When on admin-dashboard the in-page sidebar already shows all nav — hide ADMIN_NAV here */}
            {onDash ? (
              <div style={{ margin: '12px 12px 0', padding: '10px 12px', borderRadius: 8, background: 'rgba(250,246,240,0.06)', color: 'rgba(250,246,240,0.5)', fontSize: 11.5, textAlign: 'center' }}>
                <Icon name="layout-dashboard" size={13} style={{ marginBottom: 4 }} />
                <div>Use the dashboard sidebar →</div>
              </div>
            ) : ADMIN_NAV.map((section) => (
              <div key={section.group}>
                <button onClick={() => toggleGroup(section.group)} style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '8px 16px', background: 'none', border: 'none', cursor: 'pointer', color: 'rgba(250,246,240,0.5)', fontFamily: 'var(--font-body)', fontSize: 10.5, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', textAlign: 'left' }}>
                  <Icon name={section.icon} size={12} />
                  <span style={{ flex: 1 }}>{section.group}</span>
                  <Icon name={expanded.includes(section.group) ? 'chevron-up' : 'chevron-down'} size={12} />
                </button>
                {expanded.includes(section.group) && (
                  <div style={{ paddingBottom: 4 }}>
                    {section.items.map((item, i) => (
                      <button key={i} onClick={() => handleItem(item)} style={{ display: 'flex', alignItems: 'center', gap: 9, width: '100%', padding: '7px 16px 7px 24px', background: 'none', border: 'none', cursor: 'pointer', color: 'rgba(250,246,240,0.82)', fontFamily: 'var(--font-body)', fontSize: 12.5, textAlign: 'left', transition: 'background 120ms' }}
                        onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(250,246,240,0.08)'; e.currentTarget.style.color = 'var(--ivory)'; }}
                        onMouseLeave={(e) => { e.currentTarget.style.background = 'none'; e.currentTarget.style.color = 'rgba(250,246,240,0.82)'; }}>
                        <Icon name={item.icon} size={13} style={{ flexShrink: 0 }} />
                        <span style={{ flex: 1 }}>{item.label}</span>
                      </button>
                    ))}
                  </div>
                )}
                <div style={{ height: 1, background: 'rgba(250,246,240,0.06)', margin: '2px 12px' }} />
              </div>
            ))}

            {/* System section */}
            <div>
              <div style={{ padding: '8px 16px 6px', color: 'rgba(250,246,240,0.5)', fontSize: 10.5, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase' }}>System</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 5, padding: '0 12px 8px' }}>
                <button onClick={api.exportData} style={adBtn}><Icon name="download" size={13} />Data Export</button>
                <button onClick={api.importData} style={adBtn}><Icon name="upload" size={13} />Data Import</button>
                <button onClick={api.changeCode} style={adBtn}><Icon name="key-round" size={13} />Change Code</button>
                <button onClick={() => goTab('users')} style={adBtn}><Icon name="users" size={13} />Users & Roles</button>
                <button onClick={api.resetData} style={{ ...adBtn, gridColumn: '1/-1', color: '#FCA5A5' }}><Icon name="rotate-ccw" size={13} />Reset Local Changes</button>
              </div>
            </div>
          </div>

          {/* Footer */}
          <div style={{ borderTop: '1px solid rgba(250,246,240,0.10)', padding: '10px 12px', flexShrink: 0 }}>
            <button onClick={api.logout} style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '9px 12px', borderRadius: 8, background: 'rgba(201,169,97,0.15)', color: 'var(--gold-light)', border: '1px solid rgba(201,169,97,0.3)', cursor: 'pointer', fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600 }}>
              <Icon name="log-out" size={14} />Exit Admin Mode
            </button>
            <div style={{ fontSize: 10, color: 'rgba(250,246,240,0.35)', marginTop: 8, display: 'flex', alignItems: 'center', gap: 4 }}>
              <Icon name="check" size={10} style={{ color: 'var(--gold-light)' }} />
              Saved · {api.savedAt ? new Date(api.savedAt).toLocaleTimeString() : 'No changes'}
            </div>
          </div>
        </div>
      ) : (
        <button onClick={() => toggle(true)} style={{ display: 'inline-flex', alignItems: 'center', gap: 8, background: 'var(--espresso)', color: 'var(--ivory)', border: 'none', borderRadius: '0 10px 10px 0', padding: '12px 14px', cursor: 'pointer', boxShadow: '3px 0 12px rgba(42,31,26,0.25)', fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 600, marginTop: 72, pointerEvents: 'all', writingMode: 'vertical-rl', letterSpacing: '0.08em' }}>
          <Icon name="shield-check" size={15} style={{ color: 'var(--gold-light)', writingMode: 'horizontal-tb' }} />
          ADMIN
        </button>
      )}
    </div>
  );
}
const adBtn = { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5, padding: '7px 8px', borderRadius: 7, background: 'rgba(250,246,240,0.07)', color: 'rgba(250,246,240,0.75)', border: '1px solid rgba(250,246,240,0.13)', cursor: 'pointer', fontFamily: 'var(--font-body)', fontSize: 11.5, fontWeight: 500, whiteSpace: 'nowrap' };
const adBtnGhost = { background: 'none', border: 'none', color: 'rgba(250,246,240,0.6)', cursor: 'pointer', display: 'flex', padding: 4, borderRadius: 6 };

/* ---------------- Edit panel (modal form) ---------------- */
function EditPanel({ config, onClose }) {
  const { title, fields, values, onSave } = config;
  const init = {}; fields.forEach((f) => {
    const raw = dget(values, f.key);
    init[f.key] = f.type === 'list' ? (Array.isArray(raw) ? raw.join('\n') : (raw || '')) : (raw != null ? raw : '');
  });
  const [form, setForm] = useStateAd(init);
  const upd = (k, v) => setForm((p) => Object.assign({}, p, { [k]: v }));
  const save = () => {
    const flat = {}; fields.forEach((f) => { flat[f.key] = f.type === 'list' ? form[f.key].split('\n').map((s) => s.trim()).filter(Boolean) : form[f.key]; });
    onSave(nestFromFlat(flat)); onClose();
  };
  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 280, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'var(--scrim)' }} />
      <div style={{ position: 'relative', width: 'min(520px,100%)', maxHeight: '88vh', display: 'flex', flexDirection: 'column', background: 'var(--bg-canvas)', borderRadius: 18, boxShadow: 'var(--shadow-deep)', overflow: 'hidden' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '18px 22px', borderBottom: '1px solid var(--line)', background: 'var(--white)' }}>
          <h3 style={{ fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 500 }}>{title}</h3>
          <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--fg-secondary)', display: 'flex', padding: 4 }}><Icon name="x" size={22} /></button>
        </div>
        <div style={{ padding: '20px 22px', overflowY: 'auto', display: 'grid', gap: 15 }}>
          {fields.map((f) => (
            <Field key={f.key} label={f.label}>
              {f.type === 'textarea' || f.type === 'list'
                ? <Textarea value={form[f.key]} dir={f.dir} rows={f.type === 'list' ? 5 : 3} onChange={(e) => upd(f.key, e.target.value)} placeholder={f.placeholder} />
                : f.type === 'select'
                  ? <Select value={form[f.key]} onChange={(e) => upd(f.key, e.target.value)}>{f.options.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}</Select>
                  : <TextInput value={form[f.key]} dir={f.dir} onChange={(e) => upd(f.key, e.target.value)} placeholder={f.placeholder} />}
            </Field>
          ))}
        </div>
        <div style={{ display: 'flex', gap: 10, padding: '16px 22px', borderTop: '1px solid var(--line)', background: 'var(--white)' }}>
          <Button variant="secondary" full onClick={onClose}>Cancel</Button>
          <Button variant="primary" full icon="check" onClick={save}>Save changes</Button>
        </div>
      </div>
    </div>
  );
}

/* ---------------- Card toolbar wrapper (edit / delete) ---------------- */
function AdminCardWrap({ onEdit, onDelete, children }) {
  const admin = useAdmin();
  if (!admin.editMode) return children;
  return (
    <div style={{ position: 'relative' }}>
      {children}
      <div className="saraya-admin-cardtools" style={{ position: 'absolute', top: 8, insetInlineEnd: 8, zIndex: 20, display: 'flex', gap: 6 }}
        onClick={(e) => { e.stopPropagation(); }}>
        {onEdit && <button onClick={(e) => { e.stopPropagation(); onEdit(); }} title="Edit" style={cardToolBtn}><Icon name="pencil" size={15} /></button>}
        {onDelete && <button onClick={(e) => { e.stopPropagation(); onDelete(); }} title="Delete" style={Object.assign({}, cardToolBtn, { color: 'var(--error)' })}><Icon name="trash-2" size={15} /></button>}
      </div>
    </div>
  );
}
const cardToolBtn = { width: 32, height: 32, borderRadius: 8, background: 'rgba(255,255,255,0.95)', border: '1px solid var(--line-strong)', boxShadow: 'var(--shadow-soft)', cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: 'var(--fg-primary)' };

/* Small inline "Edit text" pencil button (for heroes / sections) */
function AdminEditButton({ onClick, label, style }) {
  const admin = useAdmin();
  if (!admin.editMode) return null;
  return (
    <button onClick={(e) => { e.stopPropagation(); onClick(); }} style={Object.assign({ display: 'inline-flex', alignItems: 'center', gap: 7, padding: '8px 14px', borderRadius: 999, background: 'var(--espresso)', color: 'var(--ivory)', border: 'none', cursor: 'pointer', fontFamily: 'var(--font-body)', fontSize: 12.5, fontWeight: 500, boxShadow: 'var(--shadow-soft)' }, style)}>
      <Icon name="pencil" size={13} />{label || 'Edit'}
    </button>
  );
}

/* "Add" button (catalogue / gallery) */
function AdminAddButton({ onClick, label }) {
  const admin = useAdmin();
  if (!admin.editMode) return null;
  return (
    <button onClick={onClick} style={{ display: 'inline-flex', alignItems: 'center', gap: 9, padding: '12px 20px', borderRadius: 10, background: 'var(--white)', color: 'var(--fg-primary)', border: '1.5px dashed var(--gold)', cursor: 'pointer', fontFamily: 'var(--font-body)', fontSize: 14, fontWeight: 500 }}>
      <Icon name="plus" size={17} style={{ color: 'var(--gold-deep)' }} />{label}
    </button>
  );
}

/* ---------------- Toast ---------------- */
function AdminToast({ toast }) {
  return (
    <div style={{ position: 'fixed', insetInlineStart: '50%', transform: 'translateX(-50%)', bottom: 26, zIndex: 320, background: toast.type === 'error' ? 'var(--error)' : 'var(--espresso)', color: '#fff', padding: '11px 20px', borderRadius: 999, boxShadow: 'var(--shadow-deep)', fontFamily: 'var(--font-body)', fontSize: 13.5, display: 'flex', alignItems: 'center', gap: 8, animation: 'sarayaPop 240ms var(--ease-out)' }}>
      <Icon name={toast.type === 'error' ? 'alert-triangle' : 'check-circle'} size={16} style={{ color: toast.type === 'error' ? '#fff' : 'var(--gold-light)' }} />
      {toast.msg}
    </div>
  );
}

Object.assign(window, { AdminProvider, useAdmin, AdminCardWrap, AdminEditButton, AdminAddButton, compressImage });
