// ─── App shell: single-page catalog, whole-package checkout ───
const LISTINGS = window.LISTINGS;
const CATALOG = window.CATALOG;

// Shared: close a popover on outside-click / Escape.
function usePopClose(ref, open, setOpen) {
  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
  }, [open]);
}

// Clickable counter chip → popover of every produced creative. Click a row to jump to where it can be viewed/played.
function CreativesChip({ ads, total, nav }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  usePopClose(ref, open, setOpen);
  const list = ads ? ads.creatives : [];
  const bc = (b) => (ads && ads.meta.brands[b] && ads.meta.brands[b].accent) || '#999';
  const paid = list.filter((c) => c.price > 0).length;
  const onRow = (c) => {
    setOpen(false);
    if (c.listing && nav) nav(c.listing);                                      // approved CGI videos live in the listings
    else { const el = document.querySelector('.plat-sec'); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); } // allergy is displayed right here
  };
  return (
    <div className="cre-wrap" ref={ref}>
      <button className={`pkg-chip cre-chip${open ? ' on' : ''}`} onClick={() => setOpen((o) => !o)} aria-expanded={open} aria-haspopup="true">
        <span className="dot"></span><span className="n">{list.length}</span>&nbsp;ad creatives <span className="pinline">· {money(total)}</span>
        <span className="cre-cv">▾</span>
      </button>
      {open && (
        <div className="cre-pop" role="menu">
          <div className="cre-pop-h"><span>Ad creatives produced</span><span className="cre-pop-tot">{money(total)}</span></div>
          <div className="cre-pop-list">
            {list.map((c) => {
              const live = (c.liveOn || []).length > 0;
              return (
                <button key={c.id} className="cre-row cre-rowbtn" onClick={() => onRow(c)}>
                  <span className="cre-thumb">{c.url && <video src={c.url + '#t=1'} muted preload="metadata" playsInline/>}</span>
                  <div className="cre-info">
                    <div className="cre-nm"><span className="bd" style={{ background: bc(c.brand) }}></span>{c.product}</div>
                    <div className={`cre-st ${live ? 'live' : 'ready'}`}>{live ? '● Live on Amazon' : '○ Approved · ready to launch'}{c.price > 0 ? <span className="px-cre"> · {money(c.price)}</span> : ''}</div>
                  </div>
                  <span className="cre-go">→</span>
                </button>
              );
            })}
          </div>
          <div className="cre-pop-foot">{paid} paid · {list.length - paid} approved &amp; ready · tap to view</div>
        </div>
      )}
    </div>
  );
}

// Clickable deliverables counter → popover of all listing sets. Click a row to open that product's creative section.
function DeliverablesChip({ nav }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  usePopClose(ref, open, setOpen);
  const items = LISTINGS.filter((L) => L.makeover);
  const tot = CATALOG.totals;
  const bc = (b) => (CATALOG.brands[b] && CATALOG.brands[b].accent) || '#999';
  return (
    <div className="cre-wrap" ref={ref}>
      <button className={`pkg-chip cre-chip${open ? ' on' : ''}`} onClick={() => setOpen((o) => !o)} aria-expanded={open} aria-haspopup="true">
        <span className="dot"></span><span className="n">{tot.deliverables}</span>&nbsp;deliverables <span className="cre-cv">▾</span>
      </button>
      {open && (
        <div className="cre-pop" role="menu">
          <div className="cre-pop-h"><span>Creative deliverables</span><span className="cre-pop-tot">{tot.listings} sets</span></div>
          <div className="cre-pop-list cre-scroll">
            {items.map((L) => (
              <button key={L.id} className="cre-row cre-rowbtn" onClick={() => { setOpen(false); nav && nav(L.id); }}>
                <span className="cre-thumb cre-thumb-img">{L.hero && <img src={L.hero} alt="" loading="lazy"/>}</span>
                <div className="cre-info">
                  <div className="cre-nm"><span className="bd" style={{ background: bc(L.brand) }}></span>{L.short}</div>
                  <div className="cre-st ready">{L.brand} · {L.deliverables.length} deliverables</div>
                </div>
                <span className="cre-go">→</span>
              </button>
            ))}
          </div>
          <div className="cre-pop-foot">{tot.deliverables} deliverables across {tot.listings} sets · tap to view</div>
        </div>
      )}
    </div>
  );
}

function TopBar({ view, setView, onLogo, onCheckout, onNavListing }) {
  const ads = window.ADS;
  const adTotal = ads ? ads.creatives.reduce((s, a) => s + a.price, 0) : 0;
  return (
    <header className="app-top">
      <div className="app-top-inner">
        <a className="wordmark" onClick={onLogo}>
          <span className="name">Vivunt</span>
          <span className="sub">Creative Studio</span>
        </a>
        <div className="view-seg">
          <button aria-pressed={view === 'listings'} onClick={() => setView('listings')}>Listings</button>
          <button aria-pressed={view === 'social'} onClick={() => setView('social')}>Social Ads</button>
          <button aria-pressed={view === 'strategy'} onClick={() => setView('strategy')}>Strategy</button>
        </div>
        <div className="top-actions">
          {view === 'listings' && (
            <>
              <DeliverablesChip nav={onNavListing}/>
              <span className="paid-pill"><Ico d="check" size={12} sw={2.8}/>Paid<span className="pinline">&nbsp;· {money(CATALOG.totals.price)}</span></span>
              <button className="btn btn-primary" onClick={onCheckout}>
                <Ico d="dl" size={15}/>Download package
              </button>
            </>
          )}
          {view === 'social' && <CreativesChip ads={ads} total={adTotal} nav={onNavListing}/>}
          {view === 'strategy' && (
            <span className="pkg-chip"><span className="dot"></span>Growth game plan</span>
          )}
        </div>
      </div>
    </header>
  );
}

function PackageBar({ onCheckout }) {
  return (
    <div className="pkgbar show">
      <div className="pkgbar-inner">
        <div className="pbk">
          <span className="a">Full creative package <span className="pb-paid"><Ico d="check" size={11} sw={2.8}/>Paid</span></span>
          <span className="b">{CATALOG.totals.listings} listings · {CATALOG.totals.deliverables} deliverables · live in market</span>
        </div>
        <span className="ptot">{money(CATALOG.totals.price)}</span>
        <button className="btn-buy" onClick={onCheckout}><Ico d="dl" size={15}/>Download package</button>
      </div>
    </div>
  );
}

function Checkout({ open, onClose }) {
  const items = LISTINGS.filter(L => L.makeover);
  const total = CATALOG.totals.price;
  const tot = CATALOG.totals;
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, [open, onClose]);
  const sum = (k) => items.reduce((a, L) => a + L.counts[k], 0);
  const byBrand = b => items.filter(L => L.brand === b);
  const brandSubtotal = b => byBrand(b).reduce((a, L) => a + L.price, 0);
  const [dl, setDl] = useState({ active: false, done: 0, total: 0, finished: false });
  // Real handoff download: fetch every makeover deliverable, zip into per-product folders, save one archive.
  const downloadAll = async () => {
    if (dl.active || !window.JSZip) return;
    const files = [];
    const ext = (u) => (u.split('?')[0].match(/\.(png|jpe?g|webp|mp4|pdf)$/i) || [, 'png'])[1];
    const clean = (s) => String(s).replace(/[\\/:*?"<>|]/g, '-').trim();
    const pretty = (u) => (u.split('/').pop() || '').replace(/\.\w+$/, '').replace(/^\d+[_-]?/, '').replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
    items.forEach((L) => {
      const folder = clean(`${L.brand} - ${L.short}`);
      let img = 0, vid = 0;
      (L.deliverables || []).forEach((d) => {
        if (d.kind === 'aplus' && Array.isArray(d.modules) && d.modules.length) {
          const sub = /mobile/i.test(d.name) ? 'Mobile' : 'Desktop';            // separate Amazon-ready modules
          d.modules.forEach((mu, mi) => files.push({ path: `${folder}/A+ Content/${sub}/${String(mi + 1).padStart(2, '0')} ${clean(pretty(mu))}.${ext(mu)}`, url: mu }));
          if (d.url && /^https?:/.test(d.url)) files.push({ path: `${folder}/A+ Content/${clean(d.name)} (full preview).${ext(d.url)}`, url: d.url });
          return;
        }
        if (!d.url || !/^https?:/.test(d.url)) return;
        if (d.kind === 'video') { vid++; files.push({ path: `${folder}/Videos/${String(vid).padStart(2, '0')} ${clean(d.name)}.${ext(d.url)}`, url: d.url }); return; }
        img++; files.push({ path: `${folder}/Listing Images/${String(img).padStart(2, '0')} ${clean(d.name)}.${ext(d.url)}`, url: d.url });
      });
    });
    setDl({ active: true, done: 0, total: files.length, finished: false });
    const zip = new window.JSZip();
    const queue = [...files];
    let done = 0;
    const worker = async () => {
      while (queue.length) {
        const f = queue.shift();
        try { zip.file(f.path, await fetch(f.url).then((r) => r.arrayBuffer())); } catch (e) {}
        done++; setDl((s) => ({ ...s, done }));
      }
    };
    await Promise.all(Array.from({ length: 6 }, worker));
    const blob = await zip.generateAsync({ type: 'blob', compression: 'STORE' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = 'Vivunt Creative Package.zip';
    document.body.appendChild(a); a.click(); a.remove();
    setTimeout(() => URL.revokeObjectURL(a.href), 5000);
    setDl((s) => ({ ...s, active: false, finished: true }));
  };
  return (
    <div className={`ovl ${open ? 'open' : ''}`} onClick={onClose}>
      <div className="sheet" onClick={(e) => e.stopPropagation()}>
        <button className="sheet-x" onClick={onClose} aria-label="Close"><Ico d="x" size={16}/></button>
        <div className="sheet-head">
          <div className="receipt-top">
            <h3>Creative package · receipt</h3>
            <span className="paid-stamp"><Ico d="check" size={12} sw={2.8}/>Paid</span>
          </div>
          <p className="sub">Paid in full · assets are live in the marketplaces · download the high-res source files anytime.</p>
          <div className="sum-row">
            <span className="sumchip"><Ico d="image" size={13} sw={1.8}/><strong>{sum('images')}</strong> images</span>
            <span className="sumchip"><Ico d="video" size={13} sw={1.8}/><strong>{sum('videos')}</strong> videos</span>
            <span className="sumchip"><Ico d="layers" size={13} sw={1.8}/><strong>{sum('aplus')}</strong> A+ sets</span>
          </div>
        </div>
        <div className="sheet-scroll">
          {['NOTTS', 'AXIV'].map(b => (
            <div key={b}>
              <div className="cg-head">
                <span className="d" style={{ background: CATALOG.brands[b].accent }}></span>
                <span className="cgn">{b}</span>
                <span className="cgm">{byBrand(b).length} sets<span className="px-cgm"> · {money(brandSubtotal(b))}</span></span>
              </div>
              {byBrand(b).map(L => (
                <div className="ln" key={L.id}>
                  <div>
                    <div className="lnn">{L.short}</div>
                    <div className="lnm">{L.size} · {L.deliverables.length} deliverables</div>
                  </div>
                  <div className="lnp">{money(L.price)}</div>
                </div>
              ))}
            </div>
          ))}
        </div>
        <div className="sheet-foot">
          <div className="tot">
            <span className="tk">Total paid<small>{items.length} sets · {tot.deliverables} deliverables</small></span>
            <span className="tv tv-paid">{money(total)}</span>
          </div>
          <button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center', opacity: dl.active ? 0.85 : 1 }} disabled={dl.active} onClick={downloadAll}>
            <Ico d="dl" size={15}/>{dl.active ? `Packaging… ${dl.done}/${dl.total}` : dl.finished ? 'Saved ✓ · download again' : 'Download all files'}
          </button>
          <div className="securenote">{dl.active ? 'Zipping high-res assets — keep this window open…' : <><Ico d="check" size={12} sw={2}/>Paid in full · {tot.deliverables} files · high-res source · commercial usage</>}</div>
        </div>
      </div>
    </div>
  );
}

function App() {
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "accents": "per-sku",
    "density": "comfortable",
    "prices": false,
    "benchmarks": true
  }/*EDITMODE-END*/;
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [filter, setFilter] = useState('all');
  const [statusFilter, setStatusFilter] = useState('all');
  const [started, setStarted] = useState(() => {
    try { return new Set(JSON.parse(localStorage.getItem('vivunt.started.v1') || '[]')); } catch { return new Set(); }
  });
  const initialHash = (typeof location !== 'undefined' && location.hash) ? location.hash.slice(1) : null;
  const [openId, setOpenId] = useState(LISTINGS.some(L => L.id === initialHash) ? initialHash : null);
  const [lb, setLb] = useState({ items: [], index: -1 });
  const [checkout, setCheckout] = useState(false);
  const [done, setDone] = useState(false);
  const [view, setView] = useState('listings');

  useEffect(() => {
    const b = document.body;
    b.dataset.accents = t.accents === 'monochrome' ? 'mono' : 'per-sku';
    b.dataset.density = t.density;
    b.dataset.prices = t.prices ? 'on' : 'off';
    b.dataset.bench = t.benchmarks ? 'on' : 'off';
  }, [t]);

  // Self-serve price toggle for the public URL (studio edit-panel isn't available there):
  // Alt+Shift+P flips prices for the session; ?prices=on|off is honored at load.
  useEffect(() => {
    const onKey = (e) => {
      if (e.altKey && e.shiftKey && (e.code === 'KeyP' || (e.key || '').toLowerCase() === 'p')) {
        e.preventDefault();
        setTweak('prices', !t.prices);
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [t.prices, setTweak]);

  const toggle = useCallback((id) => setOpenId(prev => {
    const next = prev === id ? null : id;
    if (typeof history !== 'undefined') history.replaceState(null, '', next ? '#' + next : location.pathname);
    return next;
  }), []);
  const openLightbox = (items, index) => { if (index >= 0 && items.length) setLb({ items, index }); };
  const stepLb = (d) => setLb(s => ({ ...s, index: (s.index + d + s.items.length) % s.items.length }));
  const closeLb = () => setLb({ items: [], index: -1 });
  const openCheckout = () => { setDone(false); setCheckout(true); };
  const startPackage = useCallback((id) => {
    setStarted(prev => { const n = new Set(prev); n.add(id); try { localStorage.setItem('vivunt.started.v1', JSON.stringify([...n])); } catch {} return n; });
  }, []);
  // Jump to a listing's creative section: switch to Listings, open it, smooth-scroll to it.
  const navToListing = useCallback((id) => {
    setView('listings');
    setOpenId(id);
    if (typeof history !== 'undefined') history.replaceState(null, '', '#' + id);
    setTimeout(() => {
      const el = document.getElementById('L-' + id);
      if (el) { const top = el.getBoundingClientRect().top + window.scrollY - 84; window.scrollTo({ top, behavior: 'smooth' }); }
    }, 240);
  }, []);

  return (
    <>
      <TopBar view={view} setView={setView} onLogo={() => setOpenId(null)} onCheckout={openCheckout} onNavListing={navToListing}/>
      {view === 'listings'
        ? <Catalog listings={LISTINGS} filter={filter} setFilter={setFilter}
            statusFilter={statusFilter} setStatusFilter={setStatusFilter}
            openId={openId} onToggle={toggle} openLightbox={openLightbox}
            startedSet={started} onStart={startPackage}/>
        : view === 'social'
          ? <SocialAds openLightbox={openLightbox}/>
          : <Strategy go={setView} onCheckout={openCheckout}/>}
      {view === 'listings' && <PackageBar onCheckout={openCheckout}/>}
      <Lightbox items={lb.items} index={lb.index} onClose={closeLb} onStep={stepLb}/>
      <Checkout open={checkout} done={done} onClose={() => setCheckout(false)} onConfirm={() => setDone(true)}/>
      <TweaksPanel>
        <TweakSection label="Presentation"/>
        <TweakRadio label="Accent color" value={t.accents} options={['per-sku', 'monochrome']}
          onChange={(v) => setTweak('accents', v)}/>
        <TweakRadio label="Card density" value={t.density} options={['comfortable', 'compact']}
          onChange={(v) => setTweak('density', v)}/>
        <TweakSection label="Content"/>
        <TweakToggle label="Show prices" value={t.prices} onChange={(v) => setTweak('prices', v)}/>
        <TweakToggle label="Show market benchmarks" value={t.benchmarks} onChange={(v) => setTweak('benchmarks', v)}/>
      </TweaksPanel>
    </>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
