/* @jsx React.createElement */
/* ============================================================
   INDIE Music Shop — indiemusic.id/shop.html
   React 18 UMD + Babel standalone. Tanpa build step, sama seperti site.jsx.
   Semua panggilan backend lewat same-origin api/bridge.php → :3022.
   ============================================================ */

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

/* ─────────────────────────────────────────────
   Config
   ───────────────────────────────────────────── */
const BRIDGE     = 'api/bridge.php';
const CART_KEY   = 'ims_shop_cart_v1';
const WA_NUMBER  = '628998267999';
const WA_DISPLAY = '+62 899-8267-999';

function waLink(text) {
  const base = 'https://wa.me/' + WA_NUMBER;
  return text ? base + '?text=' + encodeURIComponent(text) : base;
}

// Cermin daftar di routes/checkout.js — HANYA untuk estimasi tampilan.
// Ongkir final tetap dihitung server.
const JABODETABEK_KEYWORDS = [
  'jakarta', 'bogor', 'depok', 'tangerang', 'bekasi', 'serpong',
  'cibubur', 'ciputat', 'pamulang', 'bintaro', 'cileungsi'
];
const SHIPPING_EST = { jabodetabek: 20000, luar_jabodetabek: 50000 };
function zoneOf(city) {
  const c = String(city || '').toLowerCase();
  return JABODETABEK_KEYWORDS.some(k => c.includes(k)) ? 'jabodetabek' : 'luar_jabodetabek';
}

const rupiah = (n) => 'Rp' + Number(n || 0).toLocaleString('id-ID');

const STATUS_LABEL = {
  pending_payment: { label: 'Menunggu pembayaran', bg: '#FBF1D9', fg: '#8A6200' },
  paid:            { label: 'Sudah dibayar',       bg: '#E5F4EC', fg: '#1F6B42' },
  processing:      { label: 'Sedang disiapkan',    bg: '#E0F4FD', fg: '#0A6B92' },
  shipped:         { label: 'Dikirim',             bg: '#E6EEF5', fg: '#00417F' },
  delivered:       { label: 'Sampai tujuan',       bg: '#E5F4EC', fg: '#1F6B42' },
  cancelled:       { label: 'Dibatalkan',          bg: '#FBEEEB', fg: '#A52416' },
};
const statusInfo = (s) => STATUS_LABEL[s] || { label: s || '—', bg: 'var(--paper-2)', fg: 'var(--txt-3)' };

/* ─────────────────────────────────────────────
   Ikon inline (tanpa dependensi eksternal)
   ───────────────────────────────────────────── */
function Ico({ d, size = 20, fill = 'none', stroke = 'currentColor', sw = 2, children }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke={stroke}
         strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" aria-hidden>
      {children || <path d={d} />}
    </svg>
  );
}
const IcoCart  = (p) => <Ico {...p}><circle cx="9" cy="20" r="1.6" /><circle cx="18" cy="20" r="1.6" /><path d="M2 3h2.2l2.4 12.4a2 2 0 0 0 2 1.6h8.5a2 2 0 0 0 2-1.6L21 7H5.3" /></Ico>;
const IcoPlus  = (p) => <Ico {...p}><path d="M12 5v14M5 12h14" /></Ico>;
const IcoX     = (p) => <Ico {...p}><path d="M18 6 6 18M6 6l12 12" /></Ico>;
const IcoTrash = (p) => <Ico {...p}><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6" /></Ico>;
const IcoCheck = (p) => <Ico {...p}><path d="M20 6 9 17l-5-5" /></Ico>;
const IcoBack  = (p) => <Ico {...p}><path d="M19 12H5M12 19l-7-7 7-7" /></Ico>;
const IcoSearch= (p) => <Ico {...p}><circle cx="11" cy="11" r="7" /><path d="m20 20-3.2-3.2" /></Ico>;
const IcoAlert = (p) => <Ico {...p}><circle cx="12" cy="12" r="9" /><path d="M12 8v5M12 16.5v.01" /></Ico>;
const IcoTruck = (p) => <Ico {...p}><path d="M3 16V6h11v10M14 9h4l3 3.2V16M6.5 19a1.8 1.8 0 1 0 0-3.6 1.8 1.8 0 0 0 0 3.6ZM17.5 19a1.8 1.8 0 1 0 0-3.6 1.8 1.8 0 0 0 0 3.6Z" /></Ico>;

/* ─────────────────────────────────────────────
   Hash routing ringan: #/rute?query
   ───────────────────────────────────────────── */
function parseHash() {
  const raw = (location.hash || '').replace(/^#\/?/, '');
  const [path, qs] = raw.split('?');
  const params = {};
  new URLSearchParams(qs || '').forEach((v, k) => { params[k] = v; });
  return { path: path || '', params };
}
function useHashRoute() {
  const [route, setRoute] = useState(parseHash);
  useEffect(() => {
    const on = () => { setRoute(parseHash()); window.scrollTo({ top: 0, behavior: 'auto' }); };
    window.addEventListener('hashchange', on);
    return () => window.removeEventListener('hashchange', on);
  }, []);
  return route;
}
const go = (to) => { location.hash = to; };

/* ─────────────────────────────────────────────
   Keranjang — localStorage { product_id: qty }
   ───────────────────────────────────────────── */
function loadCart() {
  try {
    const raw = JSON.parse(localStorage.getItem(CART_KEY) || '{}');
    const out = {};
    Object.keys(raw).forEach(k => {
      const q = parseInt(raw[k], 10);
      if (Number.isFinite(q) && q > 0) out[k] = q;
    });
    return out;
  } catch (e) { return {}; }
}
function useCart() {
  const [cart, setCart] = useState(loadCart);
  useEffect(() => {
    try { localStorage.setItem(CART_KEY, JSON.stringify(cart)); } catch (e) {}
  }, [cart]);
  // Sinkron antar tab.
  useEffect(() => {
    const on = (e) => { if (e.key === CART_KEY) setCart(loadCart()); };
    window.addEventListener('storage', on);
    return () => window.removeEventListener('storage', on);
  }, []);

  const add = useCallback((id, qty = 1, max = Infinity) => {
    setCart(c => ({ ...c, [id]: Math.min(max, (c[id] || 0) + qty) }));
  }, []);
  const setQty = useCallback((id, qty, max = Infinity) => {
    setCart(c => {
      const next = { ...c };
      const q = Math.min(max, Math.max(0, qty));
      if (q <= 0) delete next[id]; else next[id] = q;
      return next;
    });
  }, []);
  const remove = useCallback((id) => setCart(c => { const n = { ...c }; delete n[id]; return n; }), []);
  const clear  = useCallback(() => setCart({}), []);
  return { cart, add, setQty, remove, clear };
}

/* ─────────────────────────────────────────────
   Nav + Footer — meminjam kelas dari styles.css
   ───────────────────────────────────────────── */
function ShopNav({ count, onOpenCart }) {
  return (
    <header className="nav nav--solid nav--shop">
      <div className="nav__inner">
        <a href="index.html" className="nav__brand">
          <img src="assets/logos/ims.png" alt="INDIE Music" width="40" height="40" />
          <span className="nav__brand-text">
            <span className="nav__brand-name">INDIE Music</span>
            <span className="nav__brand-sub">Shop</span>
          </span>
        </a>

        <nav className="nav__links" aria-label="Utama">
          <a href="index.html" className="nav__link">Beranda</a>
          <a href="#/" className="nav__link">Katalog</a>
          <a href="#/cek-order" className="nav__link">Cek Order</a>
        </nav>

        <div className="nav__actions">
          <a className="nav__signin" href={waLink('Halo IMS! Aku mau tanya soal produk di shop.')}>
            Tanya admin <span aria-hidden>↗</span>
          </a>
          <button className="nav__cart" onClick={onOpenCart} aria-label="Buka keranjang">
            <IcoCart size={17} />
            <span>Keranjang</span>
            {count > 0 && <span className="nav__cart-count">{count}</span>}
          </button>
        </div>
      </div>
    </header>
  );
}

function ShopFooter() {
  return (
    <footer className="footer">
      <div className="wrap">
        <div className="footer__top">
          <div className="footer__brand">
            <div className="footer__brand-row">
              <img src="assets/logos/ims.png" alt="INDIE Music" width="48" height="48" />
              <div>
                <div className="footer__brand-name">INDIE Music</div>
                <div className="footer__brand-sub">School &amp; Studio</div>
              </div>
            </div>
            <p className="footer__blurb">
              Merch, aksesori, dan perlengkapan musik pilihan dari INDIE Music School &amp; Studio,
              Gading Serpong. Dikirim ke seluruh Indonesia.
            </p>
          </div>

          <div className="footer__cols">
            <div className="footer__col">
              <div className="footer__col-title">Shop</div>
              <ul>
                <li><a href="#/">Katalog</a></li>
                <li><a href="#/cek-order">Cek status order</a></li>
                <li><a href={waLink('Halo IMS! Aku mau tanya soal order shop.')}>Bantuan via WA</a></li>
              </ul>
            </div>
            <div className="footer__col">
              <div className="footer__col-title">Sekolah</div>
              <ul>
                <li><a href="index.html">Beranda</a></li>
                <li><a href="index.html#programs">Program les</a></li>
                <li><a href="index.html#pricing">Harga</a></li>
              </ul>
            </div>
            <div className="footer__col">
              <div className="footer__col-title">Kontak</div>
              <ul>
                <li>Ruko Neo Arcade Blok A33, Gading Serpong</li>
                <li><a href={waLink('Halo IMS!')}>{WA_DISPLAY} (WA)</a></li>
                <li><a href="mailto:hello@indiemusic.id">hello@indiemusic.id</a></li>
              </ul>
            </div>
          </div>
        </div>

        <div className="footer__bottom">
          <span>© 2026 INDIE Music School &amp; Studio</span>
          <span className="footer__dot">·</span>
          <span>Bagian dari <strong>INDIE Group</strong></span>
          <span className="footer__spacer" />
          <span className="jp footer__jp">音楽は、しあわせの始まり。</span>
        </div>
      </div>
    </footer>
  );
}

/* ─────────────────────────────────────────────
   Kartu produk
   ───────────────────────────────────────────── */
function firstImage(p) {
  const imgs = Array.isArray(p.images) ? p.images : [];
  const src = imgs.find(x => typeof x === 'string' && x.trim());
  return src ? src.trim() : null;
}

function ProductCard({ p, inCart, onOpen, onAdd }) {
  const img = firstImage(p);
  const stock = Number(p.stock) || 0;
  const out = stock <= 0;
  const full = inCart >= stock;
  return (
    <article className="prod">
      <button className="prod__media" onClick={() => onOpen(p)} aria-label={'Lihat detail ' + p.name}>
        {img
          ? <img src={img} alt={p.name} loading="lazy" />
          : <div className="prod__ph">{(p.name || '?').slice(0, 2)}</div>}
        {out
          ? <span className="prod__oos">Stok habis</span>
          : stock <= 3 ? <span className="prod__low">Sisa {stock}</span> : null}
      </button>
      <div className="prod__body">
        {p.category && <div className="prod__cat">{p.category}</div>}
        <h3 className="prod__name">{p.name}</h3>
        {p.description && <p className="prod__desc">{p.description}</p>}
        <div className="prod__foot">
          <div className="prod__price">{rupiah(p.price)}</div>
          <button
            className="prod__add"
            disabled={out || full}
            title={out ? 'Stok habis' : full ? 'Semua stok sudah di keranjang' : 'Tambah ke keranjang'}
            aria-label={'Tambah ' + p.name + ' ke keranjang'}
            onClick={() => onAdd(p)}
          >
            <IcoPlus size={18} />
          </button>
        </div>
      </div>
    </article>
  );
}

/* ─────────────────────────────────────────────
   Modal detail produk
   ───────────────────────────────────────────── */
function ProductModal({ p, inCart, onClose, onAdd }) {
  const [qty, setQty] = useState(1);
  const [idx, setIdx] = useState(0);
  const imgs = (Array.isArray(p.images) ? p.images : []).filter(x => typeof x === 'string' && x.trim());
  const stock = Number(p.stock) || 0;
  const remaining = Math.max(0, stock - inCart);

  useEffect(() => {
    const on = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', on);
    document.body.style.overflow = 'hidden';
    return () => { window.removeEventListener('keydown', on); document.body.style.overflow = ''; };
  }, [onClose]);

  useEffect(() => { setQty(remaining > 0 ? 1 : 0); }, [p.id]);

  return (
    <div className="mod__backdrop" onClick={onClose} role="dialog" aria-modal="true" aria-label={p.name}>
      <div className="mod" onClick={e => e.stopPropagation()}>
        <div className="mod__media">
          <button className="mod__close" onClick={onClose} aria-label="Tutup"><IcoX size={18} /></button>
          {imgs.length
            ? <img src={imgs[Math.min(idx, imgs.length - 1)]} alt={p.name} />
            : <div className="prod__ph" style={{ height: '100%' }}>{(p.name || '?').slice(0, 2)}</div>}
          {imgs.length > 1 && (
            <div className="mod__thumbs">
              {imgs.map((src, i) => (
                <button key={i} className={'mod__thumb' + (i === idx ? ' is-active' : '')}
                        onClick={() => setIdx(i)} aria-label={'Gambar ' + (i + 1)}>
                  <img src={src} alt="" loading="lazy" />
                </button>
              ))}
            </div>
          )}
        </div>

        <div className="mod__body">
          {p.category && <div className="prod__cat">{p.category}</div>}
          <h2 className="mod__name">{p.name}</h2>
          <div className="mod__price">{rupiah(p.price)}</div>
          {p.description && <p className="mod__desc">{p.description}</p>}

          <div className="mod__meta">
            {stock > 0 ? <span>Stok: <strong>{stock}</strong></span> : <span style={{ color: 'var(--ims)', fontWeight: 700 }}>Stok habis</span>}
            {p.weight_gram ? <span>Berat: <strong>{p.weight_gram} g</strong></span> : null}
          </div>

          {remaining > 0 ? (
            <div style={{ marginTop: 24, display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
              <div className="qty">
                <button onClick={() => setQty(q => Math.max(1, q - 1))} disabled={qty <= 1} aria-label="Kurangi">−</button>
                <span>{qty}</span>
                <button onClick={() => setQty(q => Math.min(remaining, q + 1))} disabled={qty >= remaining} aria-label="Tambah">+</button>
              </div>
              <button className="btn btn--primary" style={{ flex: 1, minWidth: 170 }}
                      onClick={() => { onAdd(p, qty); onClose(); }}>
                Tambah ke keranjang
              </button>
            </div>
          ) : (
            <div className="alert alert--info" style={{ marginTop: 24 }}>
              <IcoAlert size={17} />
              <span>{stock <= 0 ? 'Produk ini sedang habis. Hubungi admin untuk info restock.' : 'Semua stok yang tersedia sudah ada di keranjangmu.'}</span>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────
   Drawer keranjang
   ───────────────────────────────────────────── */
function CartDrawer({ open, lines, subtotal, onClose, onSetQty, onRemove, onCheckout }) {
  useEffect(() => {
    if (!open) return;
    const on = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', on);
    document.body.style.overflow = 'hidden';
    return () => { window.removeEventListener('keydown', on); document.body.style.overflow = ''; };
  }, [open, onClose]);

  if (!open) return null;
  const hasIssue = lines.some(l => l.issue);

  return (
    <>
      <div className="cart__backdrop" onClick={onClose} />
      <aside className="cart" role="dialog" aria-modal="true" aria-label="Keranjang belanja">
        <div className="cart__head">
          <div className="cart__title">Keranjang</div>
          <button className="cart__x" onClick={onClose} aria-label="Tutup keranjang"><IcoX size={22} /></button>
        </div>

        <div className="cart__body">
          {lines.length === 0 ? (
            <div className="cart__empty">
              <IcoCart size={44} />
              <div>Keranjangmu masih kosong.</div>
              <button className="btn btn--outline" style={{ marginTop: 18 }} onClick={onClose}>Lihat katalog</button>
            </div>
          ) : lines.map(l => (
            <div className="citem" key={l.product_id}>
              {l.image
                ? <img className="citem__img" src={l.image} alt={l.name} />
                : <div className="citem__ph">{(l.name || '?').slice(0, 2)}</div>}
              <div style={{ flex: 1, minWidth: 0 }}>
                <div className="citem__name">{l.name}</div>
                <div className="citem__price">{rupiah(l.unit_price)} × {l.qty} = {rupiah(l.subtotal)}</div>
                {l.issue && <div className="citem__warn">{l.issue}</div>}
                <div className="citem__row">
                  <div className="qty">
                    <button onClick={() => onSetQty(l.product_id, l.qty - 1, l.stock)} aria-label="Kurangi">−</button>
                    <span>{l.qty}</span>
                    <button onClick={() => onSetQty(l.product_id, l.qty + 1, l.stock)} disabled={l.qty >= l.stock} aria-label="Tambah">+</button>
                  </div>
                  <button className="citem__del" onClick={() => onRemove(l.product_id)} aria-label={'Hapus ' + l.name}>
                    <IcoTrash size={17} />
                  </button>
                </div>
              </div>
            </div>
          ))}
        </div>

        {lines.length > 0 && (
          <div className="cart__foot">
            <div className="cart__line"><span>Subtotal</span><span>{rupiah(subtotal)}</span></div>
            <div className="cart__line" style={{ color: 'var(--txt-3)', fontSize: 13 }}>
              <span>Ongkir</span><span>dihitung saat checkout</span>
            </div>
            <div className="cart__total"><span>Total sementara</span><b>{rupiah(subtotal)}</b></div>
            <button className="btn btn--primary btn--lg" style={{ width: '100%' }}
                    disabled={hasIssue} onClick={onCheckout}>
              Lanjut ke checkout
            </button>
            {hasIssue && <div className="cart__note" style={{ color: 'var(--ims)' }}>Perbaiki item bermasalah dulu sebelum checkout.</div>}
          </div>
        )}
      </aside>
    </>
  );
}

/* ─────────────────────────────────────────────
   Halaman katalog
   ───────────────────────────────────────────── */
function CatalogView({ products, loading, error, cart, onOpen, onAdd, onRetry }) {
  const [cat, setCat] = useState('');
  const [q, setQ] = useState('');

  const categories = useMemo(() => {
    const s = [];
    products.forEach(p => { const c = p.category || 'Lainnya'; if (!s.includes(c)) s.push(c); });
    return s;
  }, [products]);

  const filtered = useMemo(() => {
    const needle = q.trim().toLowerCase();
    return products.filter(p => {
      if (cat && (p.category || 'Lainnya') !== cat) return false;
      if (needle) {
        const hay = ((p.name || '') + ' ' + (p.description || '') + ' ' + (p.category || '')).toLowerCase();
        if (!hay.includes(needle)) return false;
      }
      return true;
    });
  }, [products, cat, q]);

  const grouped = useMemo(() => {
    const map = new Map();
    filtered.forEach(p => {
      const c = p.category || 'Lainnya';
      if (!map.has(c)) map.set(c, []);
      map.get(c).push(p);
    });
    return Array.from(map.entries());
  }, [filtered]);

  return (
    <>
      <section className="shop-hero">
        <div className="wrap">
          <div className="eyebrow-line">INDIE Music · Shop</div>
          <h1 className="shop-hero__title">Perlengkapan buat<br />yang serius main.</h1>
          <p className="shop-hero__intro">
            Merch, aksesori, dan perlengkapan musik pilihan langsung dari studio kami di Gading Serpong.
            Dikirim ke seluruh Indonesia — bayar aman lewat Xendit.
          </p>

          {!loading && !error && products.length > 0 && (
            <>
              <div className="shop-filters">
                <button className={'shop-chip' + (cat === '' ? ' is-active' : '')} onClick={() => setCat('')}>
                  Semua
                </button>
                {categories.map(c => (
                  <button key={c} className={'shop-chip' + (cat === c ? ' is-active' : '')} onClick={() => setCat(c)}>{c}</button>
                ))}
              </div>
              <div style={{ marginTop: 18, maxWidth: 340, position: 'relative' }}>
                <span style={{ position: 'absolute', left: 14, top: 13, color: 'var(--txt-3)' }}><IcoSearch size={17} /></span>
                <input
                  value={q} onChange={e => setQ(e.target.value)}
                  placeholder="Cari produk…"
                  aria-label="Cari produk"
                  style={{
                    width: '100%', padding: '12px 14px 12px 42px', fontFamily: 'var(--f-body)', fontSize: 14,
                    border: '1px solid var(--line)', borderRadius: 999, background: '#fff', outline: 'none',
                  }}
                />
              </div>
            </>
          )}
        </div>
      </section>

      <section className="section" style={{ paddingTop: 12 }}>
        <div className="wrap">
          {loading && (
            <div className="shop-grid">
              {[0, 1, 2, 3, 4, 5, 6, 7].map(i => <div key={i} className="sk sk--card" />)}
            </div>
          )}

          {!loading && error && (
            <div className="alert alert--err" style={{ maxWidth: 560 }}>
              <IcoAlert size={18} />
              <div>
                <div>Katalog gagal dimuat.</div>
                <div style={{ fontWeight: 400, marginTop: 4, fontSize: 12.5 }}>{error}</div>
                <button className="btn btn--outline" style={{ marginTop: 14 }} onClick={onRetry}>Coba lagi</button>
              </div>
            </div>
          )}

          {!loading && !error && products.length === 0 && (
            <div className="alert alert--info" style={{ maxWidth: 560 }}>
              <IcoAlert size={18} />
              <span>Belum ada produk yang tayang. Cek lagi sebentar lagi, ya.</span>
            </div>
          )}

          {!loading && !error && products.length > 0 && filtered.length === 0 && (
            <div className="alert alert--info" style={{ maxWidth: 560 }}>
              <IcoSearch size={18} />
              <span>Tidak ada produk yang cocok dengan pencarianmu.</span>
            </div>
          )}

          {grouped.map(([c, list]) => (
            <div className="shop-cat" key={c}>
              <div className="shop-cat__head">
                <div className="shop-cat__name">{c}</div>
                <div className="shop-cat__count">{list.length} produk</div>
              </div>
              <div className="shop-grid">
                {list.map(p => (
                  <ProductCard key={p.id} p={p} inCart={cart[p.id] || 0} onOpen={onOpen} onAdd={onAdd} />
                ))}
              </div>
            </div>
          ))}
        </div>
      </section>
    </>
  );
}

/* ─────────────────────────────────────────────
   Halaman checkout
   ───────────────────────────────────────────── */
const EMPTY_FORM = { name: '', phone: '', email: '', street: '', city: '', province: '', postal_code: '', notes: '' };

function CheckoutView({ lines, subtotal, onDone }) {
  const [f, setF] = useState(EMPTY_FORM);
  const [errors, setErrors] = useState({});
  const [submitting, setSubmitting] = useState(false);
  const [apiErr, setApiErr] = useState(null);

  const set = (k) => (e) => setF(prev => ({ ...prev, [k]: e.target.value }));

  const zone = zoneOf(f.city);
  const shippingEst = f.city.trim() ? SHIPPING_EST[zone] : null;
  const totalEst = subtotal + (shippingEst || 0);
  const hasIssue = lines.some(l => l.issue);

  const validate = () => {
    const e = {};
    if (!f.name.trim()) e.name = 'Nama wajib diisi';
    const digits = f.phone.replace(/[^0-9]/g, '');
    if (!digits) e.phone = 'Nomor WhatsApp wajib diisi';
    else if (digits.length < 9) e.phone = 'Nomor WhatsApp tidak valid';
    if (!f.email.trim()) e.email = 'Email wajib diisi';
    else if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(f.email.trim())) e.email = 'Format email tidak valid';
    if (!f.street.trim()) e.street = 'Alamat lengkap wajib diisi';
    if (!f.city.trim()) e.city = 'Kota wajib diisi';
    setErrors(e);
    return Object.keys(e).length === 0;
  };

  const submit = async (ev) => {
    ev.preventDefault();
    setApiErr(null);
    if (!validate()) return;
    if (!lines.length) { setApiErr('Keranjang kosong.'); return; }

    setSubmitting(true);
    try {
      const r = await fetch(BRIDGE + '/checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          customer: { name: f.name.trim(), email: f.email.trim(), phone: f.phone.trim() },
          shipping: {
            street: f.street.trim(), city: f.city.trim(), province: f.province.trim(),
            postal_code: f.postal_code.trim(), notes: f.notes.trim(),
          },
          // Hanya id + qty: harga & nama ditentukan server dari DB.
          items: lines.map(l => ({ product_id: l.product_id, qty: l.qty })),
        }),
      });
      const j = await r.json().catch(() => ({ ok: false, error: 'Respons server tidak valid' }));
      if (!j.ok) throw new Error(j.error || 'Checkout gagal');
      if (!j.payment_url) throw new Error('URL pembayaran tidak diterima');

      onDone(j.order_number);
      window.location.href = j.payment_url;
    } catch (err) {
      setApiErr(err.message || String(err));
      setSubmitting(false);
    }
  };

  if (!lines.length) {
    return (
      <section className="status-page">
        <div className="wrap">
          <div className="status-card">
            <div className="status-mark status-mark--wait"><IcoCart size={32} /></div>
            <h1 className="status-title">Keranjang kosong</h1>
            <p className="status-sub">Belum ada produk yang bisa di-checkout.</p>
            <div className="status-actions">
              <a className="btn btn--primary" href="#/">Lihat katalog</a>
            </div>
          </div>
        </div>
      </section>
    );
  }

  return (
    <section className="section" style={{ paddingTop: 118 }}>
      <div className="wrap">
        <a href="#/" className="btn btn--outline" style={{ marginBottom: 26 }}><IcoBack size={16} /> Kembali ke katalog</a>
        <div className="eyebrow-line">Langkah terakhir</div>
        <h1 className="section__title" style={{ marginBottom: 34 }}>Checkout</h1>

        <form onSubmit={submit} style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1.35fr) minmax(0,1fr)', gap: 34, alignItems: 'start' }}
              className="checkout-grid">
          {/* ── Kiri: data pembeli ── */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 26 }}>
            <div style={{ background: '#fff', border: '1px solid var(--line)', borderRadius: 20, padding: 26 }}>
              <h2 style={{ fontSize: 17, fontWeight: 800, marginBottom: 18 }}>Data pembeli</h2>
              <div className="f-grid">
                <div className="field field--full">
                  <label htmlFor="ck-name">Nama lengkap *</label>
                  <input id="ck-name" value={f.name} onChange={set('name')} aria-invalid={!!errors.name} placeholder="Nama kamu" />
                  {errors.name && <div className="field__err">{errors.name}</div>}
                </div>
                <div className="field">
                  <label htmlFor="ck-phone">Nomor WhatsApp *</label>
                  <input id="ck-phone" value={f.phone} onChange={set('phone')} aria-invalid={!!errors.phone}
                         inputMode="tel" placeholder="08xxxxxxxxxx" />
                  {errors.phone
                    ? <div className="field__err">{errors.phone}</div>
                    : <div className="f-hint">Dipakai admin untuk konfirmasi order.</div>}
                </div>
                <div className="field">
                  <label htmlFor="ck-email">Email *</label>
                  <input id="ck-email" value={f.email} onChange={set('email')} aria-invalid={!!errors.email}
                         inputMode="email" placeholder="nama@email.com" />
                  {errors.email
                    ? <div className="field__err">{errors.email}</div>
                    : <div className="f-hint">Invoice pembayaran dikirim ke sini.</div>}
                </div>
              </div>
            </div>

            <div style={{ background: '#fff', border: '1px solid var(--line)', borderRadius: 20, padding: 26 }}>
              <h2 style={{ fontSize: 17, fontWeight: 800, marginBottom: 18 }}>Alamat pengiriman</h2>
              <div className="f-grid">
                <div className="field field--full">
                  <label htmlFor="ck-street">Alamat lengkap *</label>
                  <textarea id="ck-street" value={f.street} onChange={set('street')} aria-invalid={!!errors.street}
                            placeholder="Nama jalan, nomor rumah, RT/RW, kelurahan, kecamatan" />
                  {errors.street && <div className="field__err">{errors.street}</div>}
                </div>
                <div className="field">
                  <label htmlFor="ck-city">Kota / Kabupaten *</label>
                  <input id="ck-city" value={f.city} onChange={set('city')} aria-invalid={!!errors.city} placeholder="Tangerang" />
                  {errors.city && <div className="field__err">{errors.city}</div>}
                </div>
                <div className="field">
                  <label htmlFor="ck-prov">Provinsi</label>
                  <input id="ck-prov" value={f.province} onChange={set('province')} placeholder="Banten" />
                </div>
                <div className="field">
                  <label htmlFor="ck-post">Kode pos</label>
                  <input id="ck-post" value={f.postal_code} onChange={set('postal_code')} inputMode="numeric" placeholder="15334" />
                </div>
                <div className="field">
                  <label htmlFor="ck-notes">Catatan (opsional)</label>
                  <input id="ck-notes" value={f.notes} onChange={set('notes')} placeholder="Patokan, jam kirim, dll" />
                </div>
              </div>
            </div>
          </div>

          {/* ── Kanan: ringkasan ── */}
          <div style={{
            background: '#fff', border: '1px solid var(--line)', borderRadius: 20, padding: 26,
            position: 'sticky', top: 92,
          }}>
            <h2 style={{ fontSize: 17, fontWeight: 800, marginBottom: 18 }}>Ringkasan order</h2>

            {lines.map(l => (
              <div key={l.product_id} style={{ display: 'flex', gap: 12, padding: '11px 0', borderBottom: '1px solid var(--line)' }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 700 }}>{l.name}</div>
                  <div style={{ fontSize: 12.5, color: 'var(--txt-3)' }}>{rupiah(l.unit_price)} × {l.qty}</div>
                  {l.issue && <div className="citem__warn">{l.issue}</div>}
                </div>
                <div style={{ fontSize: 13.5, fontWeight: 700, whiteSpace: 'nowrap' }}>{rupiah(l.subtotal)}</div>
              </div>
            ))}

            <div className="cart__line" style={{ marginTop: 16 }}><span>Subtotal</span><span>{rupiah(subtotal)}</span></div>
            <div className="cart__line">
              <span>Ongkir {f.city.trim() ? (zone === 'jabodetabek' ? '· Jabodetabek' : '· Luar Jabodetabek') : ''}</span>
              <span>{shippingEst != null ? rupiah(shippingEst) : 'isi kota dulu'}</span>
            </div>
            <div className="cart__total">
              <span>Total</span>
              <b>{shippingEst != null ? rupiah(totalEst) : rupiah(subtotal) + '+'}</b>
            </div>

            <div style={{ display: 'flex', gap: 9, alignItems: 'flex-start', fontSize: 12.5, color: 'var(--txt-3)', marginBottom: 18 }}>
              <IcoTruck size={16} />
              <span>Ongkir flat: Jabodetabek {rupiah(SHIPPING_EST.jabodetabek)}, luar Jabodetabek {rupiah(SHIPPING_EST.luar_jabodetabek)}. Nilai final dihitung server.</span>
            </div>

            {apiErr && (
              <div className="alert alert--err" style={{ marginBottom: 16 }}>
                <IcoAlert size={17} /><span>{apiErr}</span>
              </div>
            )}
            {hasIssue && (
              <div className="alert alert--err" style={{ marginBottom: 16 }}>
                <IcoAlert size={17} /><span>Ada item yang stoknya berubah. Perbaiki di keranjang dulu.</span>
              </div>
            )}

            <button type="submit" className="btn btn--primary btn--lg" style={{ width: '100%' }}
                    disabled={submitting || hasIssue}>
              {submitting ? 'Menyiapkan pembayaran…' : 'Bayar sekarang'}
            </button>
            <div className="cart__note">Kamu akan diarahkan ke halaman pembayaran Xendit yang aman.</div>
          </div>
        </form>
      </div>
    </section>
  );
}

/* ─────────────────────────────────────────────
   Detail order (dipakai order-success & cek-order)
   ───────────────────────────────────────────── */
function OrderDetail({ order, items }) {
  const si = statusInfo(order.status);
  const addr = order.shipping_address || {};
  return (
    <>
      <div style={{ marginTop: 18 }}>
        <span className="badge-status" style={{ background: si.bg, color: si.fg }}>{si.label}</span>
      </div>
      <div className="status-num">{order.order_number}</div>

      <table className="status-table">
        <tbody>
          {(items || []).map((it, i) => (
            <tr key={i}>
              <td>{it.product_name} <span style={{ color: 'var(--txt-3)' }}>× {it.qty}</span></td>
              <td>{rupiah(it.subtotal)}</td>
            </tr>
          ))}
          <tr><td>Subtotal</td><td>{rupiah(order.subtotal)}</td></tr>
          <tr><td>Ongkir {order.shipping_zone ? '(' + order.shipping_zone.replace('_', ' ') + ')' : ''}</td><td>{rupiah(order.shipping_cost)}</td></tr>
          <tr><td><strong>Total</strong></td><td style={{ color: 'var(--ims)', fontSize: 16 }}>{rupiah(order.total)}</td></tr>
        </tbody>
      </table>

      {(addr.street || addr.city) && (
        <div style={{ marginTop: 20, textAlign: 'left', fontSize: 13.5, color: 'var(--txt-3)', lineHeight: 1.6 }}>
          <div style={{ fontWeight: 800, color: 'var(--txt)', fontSize: 11.5, letterSpacing: '.1em', textTransform: 'uppercase', marginBottom: 5 }}>
            Dikirim ke
          </div>
          <div>{order.customer_name}</div>
          <div>{addr.street}</div>
          <div>{[addr.city, addr.province, addr.postal_code].filter(Boolean).join(', ')}</div>
        </div>
      )}
    </>
  );
}

function useOrder(orderNumber) {
  const [state, setState] = useState({ loading: !!orderNumber, order: null, items: [], error: null });
  const load = useCallback(async (num) => {
    if (!num) return;
    setState(s => ({ ...s, loading: true, error: null }));
    try {
      const r = await fetch(BRIDGE + '/order/' + encodeURIComponent(num));
      const j = await r.json().catch(() => ({ ok: false, error: 'Respons tidak valid' }));
      if (!j.ok) throw new Error(j.error || 'Order tidak ditemukan');
      setState({ loading: false, order: j.order, items: j.items || [], error: null });
    } catch (err) {
      setState({ loading: false, order: null, items: [], error: err.message || String(err) });
    }
  }, []);
  useEffect(() => { if (orderNumber) load(orderNumber); }, [orderNumber, load]);
  return { ...state, load };
}

function OrderSuccessView({ orderNumber }) {
  const { loading, order, items, error } = useOrder(orderNumber);
  const paid = order && order.status !== 'pending_payment' && order.status !== 'cancelled';

  return (
    <section className="status-page">
      <div className="wrap">
        <div className="status-card">
          {loading ? (
            <>
              <div className="sk" style={{ width: 74, height: 74, borderRadius: 999, margin: '0 auto 22px' }} />
              <div className="sk" style={{ height: 26, width: '65%', margin: '0 auto', borderRadius: 8 }} />
            </>
          ) : error ? (
            <>
              <div className="status-mark status-mark--err"><IcoAlert size={34} /></div>
              <h1 className="status-title">Order tidak ditemukan</h1>
              <p className="status-sub">{error}</p>
            </>
          ) : (
            <>
              <div className={'status-mark ' + (paid ? 'status-mark--ok' : 'status-mark--wait')}>
                {paid ? <IcoCheck size={34} /> : <IcoAlert size={34} />}
              </div>
              <h1 className="status-title">{paid ? 'Terima kasih!' : 'Menunggu pembayaran'}</h1>
              <p className="status-sub">
                {paid
                  ? 'Pembayaranmu sudah kami terima. Admin akan menghubungi kamu lewat WhatsApp untuk proses pengiriman.'
                  : 'Ordermu sudah tercatat, tapi pembayaran belum masuk. Selesaikan pembayaran lewat tautan invoice, ya.'}
              </p>
              <OrderDetail order={order} items={items} />
              <div className="status-actions">
                {!paid && order.xendit_invoice_url && (
                  <a className="btn btn--primary" href={order.xendit_invoice_url}>Lanjut bayar</a>
                )}
                <a className="btn btn--outline" href="#/">Belanja lagi</a>
                <a className="btn btn--outline" href={waLink('Halo IMS! Aku mau tanya soal order ' + (order.order_number || ''))}>
                  Tanya admin
                </a>
              </div>
            </>
          )}
        </div>
      </div>
    </section>
  );
}

function OrderFailedView({ orderNumber }) {
  return (
    <section className="status-page">
      <div className="wrap">
        <div className="status-card">
          <div className="status-mark status-mark--err"><IcoX size={34} /></div>
          <h1 className="status-title">Pembayaran belum selesai</h1>
          <p className="status-sub">
            Pembayaranmu dibatalkan atau kedaluwarsa. Tenang, keranjangmu masih tersimpan —
            kamu bisa mengulang checkout kapan saja.
          </p>
          {orderNumber && <div className="status-num">{orderNumber}</div>}
          <div className="status-actions">
            <a className="btn btn--primary" href="#/">Kembali ke katalog</a>
            <a className="btn btn--outline" href={waLink('Halo IMS! Pembayaran order ' + (orderNumber || '') + ' bermasalah.')}>
              Hubungi admin
            </a>
          </div>
        </div>
      </div>
    </section>
  );
}

function CekOrderView({ initial }) {
  const [num, setNum] = useState(initial || '');
  const [query, setQuery] = useState(initial || '');
  const { loading, order, items, error } = useOrder(query);

  const submit = (e) => { e.preventDefault(); setQuery(num.trim().toUpperCase()); };

  return (
    <section className="status-page">
      <div className="wrap">
        <div className="status-card">
          <div className="eyebrow-line" style={{ justifyContent: 'center' }}>Lacak pesanan</div>
          <h1 className="status-title">Cek status order</h1>
          <p className="status-sub">Masukkan nomor order yang kamu terima saat checkout.</p>

          <form onSubmit={submit} style={{ display: 'flex', gap: 10, marginTop: 24, flexWrap: 'wrap', justifyContent: 'center' }}>
            <input
              value={num} onChange={e => setNum(e.target.value)}
              placeholder="MSHOP-20260723-XXXX" aria-label="Nomor order"
              style={{
                flex: 1, minWidth: 230, padding: '13px 16px', fontFamily: 'var(--f-body)', fontSize: 14,
                border: '1px solid var(--line)', borderRadius: 12, background: '#fff', outline: 'none',
              }}
            />
            <button type="submit" className="btn btn--primary" disabled={!num.trim() || loading}>
              {loading ? 'Mencari…' : 'Cek'}
            </button>
          </form>

          {query && error && (
            <div className="alert alert--err" style={{ marginTop: 22, textAlign: 'left' }}>
              <IcoAlert size={17} /><span>{error}</span>
            </div>
          )}

          {order && !error && (
            <>
              <OrderDetail order={order} items={items} />
              <div className="status-actions">
                {order.status === 'pending_payment' && order.xendit_invoice_url && (
                  <a className="btn btn--primary" href={order.xendit_invoice_url}>Lanjut bayar</a>
                )}
                <a className="btn btn--outline" href="#/">Belanja lagi</a>
              </div>
            </>
          )}
        </div>
      </div>
    </section>
  );
}

/* ─────────────────────────────────────────────
   Root
   ───────────────────────────────────────────── */
function ShopApp() {
  const route = useHashRoute();
  const { cart, add, setQty, remove, clear } = useCart();

  const [products, setProducts] = useState([]);
  const [loading, setLoading]   = useState(true);
  const [error, setError]       = useState(null);
  const [modal, setModal]       = useState(null);
  const [cartOpen, setCartOpen] = useState(false);
  const [flash, setFlash]       = useState(null);

  const loadCatalog = useCallback(async () => {
    setLoading(true); setError(null);
    try {
      const r = await fetch(BRIDGE + '/catalog/products');
      const j = await r.json().catch(() => ({ ok: false, error: 'Respons tidak valid' }));
      if (!j.ok) throw new Error(j.error || 'Gagal memuat katalog');
      setProducts(Array.isArray(j.data) ? j.data : []);
    } catch (err) {
      setError(err.message || String(err));
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { loadCatalog(); }, [loadCatalog]);

  const byId = useMemo(() => new Map(products.map(p => [p.id, p])), [products]);

  // Baris keranjang = irisan cart × katalog. Produk yang hilang/habis ditandai
  // supaya pembeli tidak baru tahu saat checkout ditolak server.
  const lines = useMemo(() => {
    return Object.keys(cart).map(id => {
      const p = byId.get(id);
      const qty = cart[id];
      if (!p) {
        return { product_id: id, name: 'Produk tidak tersedia', unit_price: 0, qty, subtotal: 0, stock: 0, image: null, issue: 'Produk sudah tidak dijual — hapus dari keranjang.' };
      }
      const stock = Number(p.stock) || 0;
      const unit  = Number(p.price) || 0;
      let issue = null;
      if (stock <= 0)      issue = 'Stok habis — hapus dari keranjang.';
      else if (qty > stock) issue = `Stok tinggal ${stock}. Kurangi jumlahnya.`;
      return {
        product_id: id, name: p.name, unit_price: unit, qty,
        subtotal: unit * qty, stock, image: firstImage(p), issue,
      };
    });
  }, [cart, byId]);

  const subtotal = useMemo(() => lines.reduce((s, l) => s + l.subtotal, 0), [lines]);
  const count    = useMemo(() => lines.reduce((s, l) => s + l.qty, 0), [lines]);

  const handleAdd = useCallback((p, qty = 1) => {
    const stock = Number(p.stock) || 0;
    if (stock <= 0) return;
    add(p.id, qty, stock);
    setFlash(`${p.name} masuk keranjang`);
    setTimeout(() => setFlash(null), 2200);
  }, [add]);

  const onCheckout = () => { setCartOpen(false); go('/checkout'); };
  const onCheckoutDone = (orderNumber) => { clear(); };

  const path = route.path;

  return (
    <>
      <ShopNav count={count} onOpenCart={() => setCartOpen(true)} />

      {path === 'checkout' ? (
        <CheckoutView lines={lines} subtotal={subtotal} onDone={onCheckoutDone} />
      ) : path === 'order-success' ? (
        <OrderSuccessView orderNumber={route.params.order} />
      ) : path === 'order-failed' ? (
        <OrderFailedView orderNumber={route.params.order} />
      ) : path === 'cek-order' ? (
        <CekOrderView initial={route.params.order} />
      ) : (
        <CatalogView
          products={products} loading={loading} error={error} cart={cart}
          onOpen={setModal} onAdd={handleAdd} onRetry={loadCatalog}
        />
      )}

      <ShopFooter />

      {modal && (
        <ProductModal
          p={byId.get(modal.id) || modal}
          inCart={cart[modal.id] || 0}
          onClose={() => setModal(null)}
          onAdd={handleAdd}
        />
      )}

      <CartDrawer
        open={cartOpen} lines={lines} subtotal={subtotal}
        onClose={() => setCartOpen(false)}
        onSetQty={setQty} onRemove={remove} onCheckout={onCheckout}
      />

      {flash && (
        <div style={{
          position: 'fixed', bottom: 26, left: '50%', transform: 'translateX(-50%)', zIndex: 400,
          background: 'var(--dark)', color: '#fff', padding: '13px 22px', borderRadius: 999,
          fontSize: 13.5, fontWeight: 600, boxShadow: '0 12px 34px rgba(0,0,0,.25)',
          display: 'flex', alignItems: 'center', gap: 9,
        }}>
          <IcoCheck size={16} /> {flash}
        </div>
      )}
    </>
  );
}

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