// Halaman Laporan — data real dari API /api/report

const MONTHS = ["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agu","Sep","Okt","Nov","Des"];
const DAYS   = ["Min","Sen","Sel","Rab","Kam","Jum","Sab"];

// "YYYY-MM-DD" dari komponen tanggal LOKAL browser — beda dengan d.toISOString().slice(0,10)
// yang mengonversi ke UTC dulu. Untuk browser di WIB (UTC+7), toISOString bisa mundur satu
// hari (mis. tengah malam WIB = sore hari sebelumnya UTC), bikin bucket tanggal di grafik
// salah cocok dengan data dari server (yang sudah dinormalisasi ke WIB).
function toDateKey(d) {
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
}

function formatRp(n) {
  if (n >= 1e9) return "Rp " + (n / 1e9).toFixed(2).replace(".", ",") + " M";
  if (n >= 1e6) return "Rp " + (n / 1e6).toFixed(1).replace(".", ",") + " jt";
  if (n >= 1e3) return "Rp " + Math.round(n / 1e3) + " rb";
  return "Rp " + n;
}

// Normalkan channel string dari Jubelio → key & label
// Contoh: "Shop | Tokopedia" → {key:"tokopedia", label:"Tokopedia", c:"#42b549"}
const CHANNEL_PATTERNS = [
  [/tokopedia/i,   "tokopedia",  "Tokopedia",   "#42b549"],
  [/shopee/i,      "shopee",     "Shopee",       "#ee4d2d"],
  [/tiktok/i,      "tiktok",     "TikTok Shop",  "#111827"],
  [/lazada/i,      "lazada",     "Lazada",       "#2c3a91"],
  [/blibli/i,      "blibli",     "Blibli",       "#0095da"],
  [/bukalapak/i,   "bukalapak",  "Bukalapak",    "#d81f5a"],
  [/zalora/i,      "zalora",     "Zalora",       "#c026d3"],
  [/jd\s*id/i,     "jdid",       "JD.ID",        "#e11d48"],
];

function parseChannel(raw = "", storeName = "") {
  for (const [re, key, label, c] of CHANNEL_PATTERNS) {
    if (re.test(raw)) return { key, label, c };
  }
  // channel berupa kode angka internal (mis. "64", "131076") — pakai nama toko sebagai label
  const isNumericCode = /^\d+$/.test(raw.trim());
  const label = (isNumericCode && storeName) ? storeName : (raw || "Lainnya");
  const key = (isNumericCode && storeName ? storeName : raw).toLowerCase().replace(/[^a-z0-9]/g, "_") || "lainnya";
  return { key, label, c: "#64748b" };
}

// Parse address "Ka***, Kab. Bekasi, Jawa Barat" → {kota, prov}
function parseAddress(addr = "") {
  const parts = addr.split(",").map((s) => s.trim()).filter(Boolean);
  if (parts.length >= 2) return { kota: parts[parts.length - 2], prov: parts[parts.length - 1] };
  if (parts.length === 1) return { kota: parts[0], prov: "-" };
  return { kota: "-", prov: "-" };
}

function getDateRange(period, customFrom, customTo) {
  const today = new Date();
  const to = toDateKey(today);
  if (period === "kustom") return { from: customFrom || to, to: customTo || to };
  if (period === "hari")   return { from: to, to };
  if (period === "minggu") {
    const from = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 6);
    return { from: toDateKey(from), to };
  }
  if (period === "bulan")
    return { from: `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-01`, to };
  return { from: `${today.getFullYear()}-01-01`, to };
}

function getProdDateRange(prodPeriod, customFrom, customTo) {
  const today = new Date();
  const to = toDateKey(today);
  if (prodPeriod === "kustom")   return { from: customFrom || to, to: customTo || to };
  if (prodPeriod === "harian")   return { from: to, to };
  if (prodPeriod === "mingguan") {
    const from = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 6);
    return { from: toDateKey(from), to };
  }
  const from = new Date(today.getFullYear(), today.getMonth(), 1);
  return { from: toDateKey(from), to };
}

// Transform raw order → TX (hanya untuk CSV download)
function toTX(o) {
  const ts  = new Date(o.created_at).getTime();
  const ch  = parseChannel(o.channel || o.source_name || "");
  const geo = parseAddress(o.address || "");
  const items = (o.items || []).map((it) => ({
    sku:   it.sku  || "",
    name:  it.name || it.sku || "-",
    qty:   Number(it.qty)   || 1,
    price: Number(it.price) || 0,
    cat:   it.cat  || "other",
  }));
  const value = items.reduce((s, it) => s + it.price * it.qty, 0);
  return { ts, doneAt: o.done_at || null, resi: o.resi || "", order_no: o.order_no || "", kota: geo.kota, prov: geo.prov, courier: o.courier, channel: ch, store: o.store_name || "", value, status: o.status, items };
}

// Bangun bucket dari range kustom
function buildTrendCustomRange(from, to, byDay) {
  const dayMap = {};
  byDay.forEach((r) => { dayMap[r.date.slice(0, 10)] = Number(r.count); });
  const startMs = new Date(from).getTime();
  const endMs   = new Date(to).getTime();
  const buckets = [];
  for (let ms = startMs; ms <= endMs; ms += 86400000) {
    const d   = new Date(ms);
    const key = toDateKey(d);
    const dd  = d.getDate(), mo = d.getMonth();
    buckets.push({ label: `${dd}/${mo + 1}`, full: `${dd} ${MONTHS[mo]}`, value: dayMap[key] || 0, key });
  }
  return buckets;
}

// Bangun bucket trend dari data agregat server [{date, count}]
function buildTrendFromDays(period, byDay) {
  const today = new Date();
  const startToday = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
  const dayMap = {};
  byDay.forEach((r) => { dayMap[r.date.slice(0, 10)] = Number(r.count); });

  if (period === "hari") {
    // Harian: tidak bisa dari byDay (perlu by_hour) — fallback ke nol
    const buckets = [];
    for (let h = 7; h <= 21; h++) buckets.push({ label: String(h).padStart(2, "0"), full: `${h}:00`, value: 0, key: h });
    return buckets;
  }
  if (period === "minggu") {
    const buckets = [];
    for (let i = 6; i >= 0; i--) {
      const d = new Date(startToday - i * 86400000);
      const key = toDateKey(d);
      buckets.push({ label: DAYS[d.getDay()], full: `${d.getDate()} ${MONTHS[d.getMonth()]}`, value: dayMap[key] || 0, key });
    }
    return buckets;
  }
  if (period === "bulan") {
    const days = today.getDate();
    const buckets = [];
    for (let dd = 1; dd <= days; dd++) {
      const key = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(dd).padStart(2, "0")}`;
      buckets.push({ label: String(dd), full: `${dd} ${MONTHS[today.getMonth()]}`, value: dayMap[key] || 0, key });
    }
    return buckets;
  }
  // Tahun: group by month
  const byMonth = new Array(12).fill(0);
  Object.entries(dayMap).forEach(([d, c]) => {
    const mo = new Date(d).getMonth();
    byMonth[mo] += c;
  });
  return MONTHS.map((m, i) => ({ label: m, full: `${m} ${today.getFullYear()}`, value: byMonth[i], key: i }));
}

function buildTrend(period, tx) {
  const today = new Date();
  const startToday = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
  let buckets, assign;

  if (period === "hari") {
    buckets = [];
    for (let h = 7; h <= 21; h++) buckets.push({ label: String(h).padStart(2, "0"), full: `${h}:00`, value: 0, key: h });
    assign = (t) => { const b = buckets.find((x) => x.key === new Date(t.ts).getHours()); if (b) b.value++; };
  } else if (period === "minggu") {
    buckets = [];
    for (let i = 6; i >= 0; i--) {
      const d = new Date(startToday - i * 86400000);
      buckets.push({ label: DAYS[d.getDay()], full: `${d.getDate()} ${MONTHS[d.getMonth()]}`, value: 0, key: new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime() });
    }
    assign = (t) => { const d = new Date(t.ts); const k = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); const b = buckets.find((x) => x.key === k); if (b) b.value++; };
  } else if (period === "bulan") {
    const days = today.getDate();
    buckets = [];
    for (let dd = 1; dd <= days; dd++) buckets.push({ label: String(dd), full: `${dd} ${MONTHS[today.getMonth()]}`, value: 0, key: dd });
    assign = (t) => { const d = new Date(t.ts); if (d.getFullYear() === today.getFullYear() && d.getMonth() === today.getMonth()) buckets[d.getDate() - 1].value++; };
  } else {
    buckets = MONTHS.map((m, i) => ({ label: m, full: `${m} ${today.getFullYear()}`, value: 0, key: i }));
    assign = (t) => { const d = new Date(t.ts); if (d.getFullYear() === today.getFullYear()) buckets[d.getMonth()].value++; };
  }

  tx.forEach(assign);
  return buckets;
}

function csvEscape(c) { const s = String(c ?? ""); return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s; }

const STATUS_PAKET_LABEL = {
  done:      "Belum Dikirim",
  shipped:   "Dikirim",
  completed: "Terkirim",
  canceled:  "Dibatalkan",
  returned:  "Diretur",
  waiting:   "Diproses",
  pending:   "-",
  problem:   "-",
};

function reportCSV(tx) {
  const head = ["NO","No Transaksi","No Resi","Tanggal","Waktu","Marketplace","Toko","Kota/Kabupaten","Provinsi","Kurir","Omset (Rp)","SKU","Produk","Qty","Status QC","Status Paket"];
  const rows = [head];
  let no = 1;
  tx.forEach((t) => {
    const d = t.doneAt ? new Date(t.doneAt) : new Date(t.ts);
    const tgl = d.toLocaleDateString("id-ID");
    const wkt = d.toLocaleTimeString("id-ID", { hour: "2-digit", minute: "2-digit" });
    const cour = (window.QC_COURIERS && window.QC_COURIERS[t.courier]) ? window.QC_COURIERS[t.courier].label : t.courier;
    const statusPaket = STATUS_PAKET_LABEL[t.status] || t.status || "-";
    t.items.forEach((it) => {
      rows.push([no, t.order_no, t.resi, tgl, wkt, t.channel.label, t.store, t.kota, t.prov, cour, t.value || 0, it.sku, it.name, it.qty, "Selesai QC", statusPaket]);
    });
    no++;
  });
  return rows.map((r) => r.map(csvEscape).join(",")).join("\n");
}

function groupByMarketplace(tx) {
  const map = {};
  tx.forEach((t) => {
    const key = t.channel.key || "lainnya";
    if (!map[key]) map[key] = { label: t.channel.label || key, items: [] };
    map[key].items.push(t);
  });
  return Object.values(map);
}

function DownloadModal({ onClose, onDownload }) {
  const today = toDateKey(new Date());
  const [from, setFrom] = React.useState(today);
  const [to,   setTo]   = React.useState(today);
  const [busy, setBusy] = React.useState(false);

  const doExport = async () => {
    setBusy(true);
    try {
      const d = await QC_API.getReport(from, to);
      const done = d.orders.map(toTX);
      if (!done.length) { onClose(); return alert("Tidak ada data yang sudah di-QC pada rentang tanggal tersebut."); }
      const stamp = from === to ? from : `${from}_sd_${to}`;
      downloadCSV(`Laporan_QC_${stamp}.csv`, reportCSV(done));
      onDownload(done.length, stamp);
      onClose();
    } catch (e) {
      alert("Gagal mengunduh: " + e.message);
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="qc-modal-overlay" onClick={onClose}>
      <div className="qc-modal" style={{ maxWidth: 360 }} onClick={(e) => e.stopPropagation()}>
        <div className="qc-modal-head">
          <span className="qc-modal-title">Download CSV</span>
          <button className="qc-modal-close" onClick={onClose}>✕</button>
        </div>
        <div className="qc-modal-body" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          <p style={{ margin: 0, fontSize: 13, color: "var(--ink-2)" }}>
            Hanya pesanan yang <b>sudah di-QC</b> yang diekspor dalam <b>1 file CSV</b>.
          </p>
          <div style={{ display: "flex", gap: 10, alignItems: "flex-end" }}>
            <label style={{ flex: 1, fontSize: 12, color: "var(--ink-2)", display: "flex", flexDirection: "column", gap: 4 }}>
              Dari tanggal
              <input type="date" value={from} max={to} onChange={(e) => setFrom(e.target.value)}
                style={{ padding: "6px 8px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13 }} />
            </label>
            <label style={{ flex: 1, fontSize: 12, color: "var(--ink-2)", display: "flex", flexDirection: "column", gap: 4 }}>
              Sampai tanggal
              <input type="date" value={to} min={from} max={new Date().toISOString().slice(0,10)} onChange={(e) => setTo(e.target.value)}
                style={{ padding: "6px 8px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 13 }} />
            </label>
          </div>
        </div>
        <div className="qc-modal-foot">
          <button className="qc-btn" onClick={onClose} disabled={busy}>Batal</button>
          <button className="qc-btn qc-btn-pri" onClick={doExport} disabled={busy}>
            {busy ? "Mengunduh…" : <><Icon.download size={15} /> Download CSV</>}
          </button>
        </div>
      </div>
    </div>
  );
}

function Report({ push }) {
  const [period,     setPeriod]     = React.useState("hari");
  const [prodPeriod, setProdPeriod] = React.useState("mingguan");
  const [loading,    setLoading]    = React.useState(false);
  const [prodLoading, setProdLoading] = React.useState(false);
  const [summary,   setSummary]   = React.useState(null);  // dari /api/report/summary
  const [prodSkus,  setProdSkus]  = React.useState([]);    // by_sku untuk produk populer
  const [showDlModal, setShowDlModal] = React.useState(false);
  const [kotaPage,    setKotaPage]    = React.useState(0);
  const [provPage,    setProvPage]    = React.useState(0);
  const [courierPage, setCourierPage] = React.useState(0);
  const [prodPage,    setProdPage]    = React.useState(0);
  const [today,       setToday]       = React.useState(() => toDateKey(new Date()));
  // Kustom date range — laporan utama
  const [customFrom, setCustomFrom] = React.useState(() => toDateKey(new Date()));
  const [customTo,   setCustomTo]   = React.useState(() => toDateKey(new Date()));
  // Kustom date range — produk populer
  const [prodCustomFrom, setProdCustomFrom] = React.useState(() => toDateKey(new Date()));
  const [prodCustomTo,   setProdCustomTo]   = React.useState(() => toDateKey(new Date()));
  // Metrik tren yang ditampilkan di grafik ("done" | "total" | "pending")
  const [trendMetric, setTrendMetric] = React.useState("done");

  // Deteksi pergantian hari — reset summary jika period=hari agar tidak tampilkan data kemarin
  React.useEffect(() => {
    const id = setInterval(() => {
      const now = toDateKey(new Date());
      setToday((prev) => {
        if (prev !== now) {
          setSummary(null);
          return now;
        }
        return prev;
      });
    }, 60000);
    return () => clearInterval(id);
  }, []);

  // Fetch agregasi utama (KPI, tren, geo, marketplace)
  React.useEffect(() => {
    if (period === "kustom" && (!customFrom || !customTo)) return;
    const { from, to } = getDateRange(period, customFrom, customTo);
    setLoading(true);
    setKotaPage(0); setProvPage(0);
    QC_API.getReportSummary(from, to)
      .then((d) => setSummary(d))
      .catch((e) => push({ kind: "err", msg: "Gagal memuat laporan", sub: e.message }))
      .finally(() => setLoading(false));
  }, [period, customFrom, customTo, today]);

  // Fetch produk populer (periode sendiri)
  React.useEffect(() => {
    if (prodPeriod === "kustom" && (!prodCustomFrom || !prodCustomTo)) return;
    const { from, to } = getProdDateRange(prodPeriod, prodCustomFrom, prodCustomTo);
    setProdLoading(true);
    setProdPage(0);
    QC_API.getReportSummary(from, to)
      .then((d) => setProdSkus(d.by_sku || []))
      .catch(() => {})
      .finally(() => setProdLoading(false));
  }, [prodPeriod, prodCustomFrom, prodCustomTo]);

  const kpi = React.useMemo(() => {
    if (!summary) return { total: 0, done: 0, pending: 0, pcs: 0, omset: 0, rate: 0 };
    const { total, done, pending, pcs, omset } = summary.kpi;
    const t = Number(total), d = Number(done), p = Number(pending);
    return { total: t, done: d, pending: p, pcs: Number(pcs), omset: Number(omset),
             rate: t ? Math.round((d / t) * 100) : 0 };
  }, [summary]);

  const trend = React.useMemo(() => {
    if (!summary) return buildTrend(period, []);

    // Pilih sumber data sesuai metrik yang aktif
    const getByDay    = () => trendMetric === "total" ? (summary.by_day_total || []) : trendMetric === "pending" ? (summary.by_day_pending || []) : (summary.by_day || []);
    const getByHour   = () => trendMetric === "total" ? (summary.by_hour_total || []) : trendMetric === "pending" ? (summary.by_hour_pending || []) : (summary.by_hour || []);

    if (period === "kustom") return buildTrendCustomRange(customFrom, customTo, getByDay());

    if (period === "hari") {
      const hourMap = {};
      getByHour().forEach((r) => { hourMap[Number(r.hour)] = Number(r.count); });
      const buckets = [];
      for (let h = 7; h <= 21; h++) buckets.push({ label: String(h).padStart(2, "0"), full: `${h}:00`, value: hourMap[h] || 0, key: h });
      return buckets;
    }
    return buildTrendFromDays(period, getByDay());
  }, [period, summary, trendMetric, customFrom, customTo]);

  const topKota = React.useMemo(() => (summary?.by_kota || []).map((r) => ({ label: r.kota, value: Number(r.count) })), [summary]);
  const topProv = React.useMemo(() => (summary?.by_prov || []).map((r) => ({ label: r.prov, value: Number(r.count) })), [summary]);

  const topCourier = React.useMemo(() => {
    const couriers = window.QC_COURIERS || {};
    return (summary?.by_courier || []).map((r) => ({
      label: (couriers[r.courier] || {}).label || r.courier || "Lainnya",
      value: Number(r.count),
    }));
  }, [summary]);

  const market = React.useMemo(() => {
    const map = {};
    (summary?.by_channel || []).forEach((r) => {
      const ch = parseChannel(r.channel || "", r.store_name || "");
      if (!map[ch.key]) map[ch.key] = { meta: ch, tx: 0, omset: 0 };
      map[ch.key].tx    += Number(r.tx);
      map[ch.key].omset += Number(r.omset);
    });
    const arr = Object.values(map).sort((a, b) => b.omset - a.omset);
    return { arr, totalTx: arr.reduce((s, x) => s + x.tx, 0), totalOmset: arr.reduce((s, x) => s + x.omset, 0) };
  }, [summary]);

  const popular = React.useMemo(() => prodSkus.map((x) => ({
    label: (x.name || x.sku || "-").includes("—") ? (x.name || "").split("—")[0].trim() : (x.name || x.sku || "-"),
    sub:   (x.name || "").includes("—") ? (x.name || "").split("—")[1].trim() : x.sku,
    value: Number(x.qty),
    cat:   "other",
  })), [prodSkus]);

  const periodLabel = period === "kustom"
    ? (customFrom === customTo ? customFrom : `${customFrom} – ${customTo}`)
    : ({ hari: "Hari ini", minggu: "7 hari terakhir", bulan: "Bulan ini" }[period] || "");

  const trendLabel = { done: "Selesai QC", total: "Total Pesanan", pending: "Menunggu QC" }[trendMetric];

  return (
    <>
    {showDlModal && (
      <DownloadModal
        onClose={() => setShowDlModal(false)}
        onDownload={(count, range) => push({ kind: "ok", msg: `${count} pesanan QC diekspor`, sub: range })}
      />
    )}
    <div className="qc-page qc-report">
      <div className="qc-rep-head">
        <div>
          <h1 className="qc-rep-title">Laporan</h1>
          <div className="qc-rep-sub">
            Ringkasan transaksi & QC · <b>{periodLabel}</b>
          </div>
        </div>
        <div className="qc-rep-head-actions">
          <div style={{ display: "flex", flexDirection: "column", gap: 6, alignItems: "flex-end" }}>
            <div className="qc-seg">
              {[["hari","Hari"],["minggu","Minggu"],["bulan","Bulan"],["kustom","Kustom"]].map(([k, l]) => (
                <button key={k} className={`qc-seg-btn ${period === k ? "is-on" : ""}`} onClick={() => setPeriod(k)}>{l}</button>
              ))}
            </div>
            {period === "kustom" && (
              <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
                <input type="date" value={customFrom} max={customTo}
                  onChange={(e) => setCustomFrom(e.target.value)}
                  style={{ padding: "4px 8px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 12 }} />
                <span style={{ fontSize: 12, color: "var(--ink-3)" }}>s/d</span>
                <input type="date" value={customTo} min={customFrom} max={new Date().toISOString().slice(0,10)}
                  onChange={(e) => setCustomTo(e.target.value)}
                  style={{ padding: "4px 8px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 12 }} />
              </div>
            )}
          </div>
          <button className="qc-btn qc-btn-pri" onClick={() => setShowDlModal(true)}>
            <Icon.download size={17} /> Download CSV
          </button>
        </div>
      </div>

      {/* KPI */}
      <div className="qc-kpi-grid" style={{ position: "relative" }}>
        {loading && (
          <div style={{
            position: "absolute", inset: 0, borderRadius: 12,
            background: "rgba(255,255,255,0.75)", backdropFilter: "blur(2px)",
            display: "flex", alignItems: "center", justifyContent: "center",
            zIndex: 10, gap: 10, fontSize: 14, color: "var(--ink-2)", fontWeight: 500,
          }}>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{ animation: "spin 1s linear infinite" }}>
              <path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/>
            </svg>
            Memuat data…
          </div>
        )}
        <KPI label="Total pesanan"   value={kpi.total.toLocaleString("id-ID")}   icon={Icon.list}     tone="pri"     sub={`${kpi.pcs.toLocaleString("id-ID")} pcs`}
          active={trendMetric === "total"} onClick={() => setTrendMetric("total")} />
        <KPI label="Menunggu QC"     value={kpi.pending.toLocaleString("id-ID")}  icon={Icon.box}      tone="warn"    sub="dalam antrian"
          active={trendMetric === "pending"} onClick={() => setTrendMetric("pending")} />
        <KPI label="Selesai QC"      value={kpi.done.toLocaleString("id-ID")}     icon={Icon.pkgCheck} tone="ok"      sub="terverifikasi"
          active={trendMetric === "done"} onClick={() => setTrendMetric("done")} />
        <KPI label="Omset periode"   value={formatRp(kpi.omset)}                  icon={Icon.target}   tone="neutral" sub={`tingkat QC ${kpi.rate}%`} />
      </div>

      {/* Kosong */}
      {!loading && kpi.done === 0 && kpi.total === 0 && (
        <div className="qc-rep-empty">
          <Icon.box size={36} stroke="var(--ink-3)" />
          <div>Belum ada data QC pada periode ini.</div>
          <div style={{ fontSize: 12, color: "var(--ink-3)" }}>Data akan muncul setelah pesanan di-QC.</div>
        </div>
      )}

      {(kpi.done > 0 || kpi.total > 0) && (
        <React.Fragment>
          {/* Tren + status */}
          <div className="qc-rep-row qc-rep-row-2">
            <Card title={`Tren ${trendLabel}`} caption={`${periodLabel} · klik kartu atas untuk ganti metrik`} grow>
              <BarChart data={trend} color={trendMetric === "total" ? "var(--pri)" : trendMetric === "pending" ? "var(--warn)" : "var(--ok)"} />
            </Card>
            <Card title="Status QC" caption={periodLabel}>
              <Donut segments={[
                { label: "Selesai",   value: kpi.done,              color: "var(--ok)" },
                { label: "Menunggu",  value: kpi.pending,           color: "var(--warn)" },
                { label: "Lainnya",   value: kpi.total - kpi.done - kpi.pending, color: "var(--line)" },
              ].filter((s) => s.value > 0)} />
            </Card>
          </div>

          {/* Geografis */}
          {(topKota.length > 0 || topProv.length > 0) && (
            <div className="qc-rep-row qc-rep-row-2">
              <Card title="Kota / Kabupaten teratas" caption="Tujuan pengiriman">
                <HBars data={topKota.slice(kotaPage * 10, kotaPage * 10 + 10)} accent="var(--pri)" unit="pesanan" />
                <Paginator page={kotaPage} total={topKota.length} perPage={10} onPage={setKotaPage} />
              </Card>
              <Card title="Provinsi teratas" caption="Tujuan pengiriman">
                <HBars data={topProv.slice(provPage * 10, provPage * 10 + 10)} accent="#0d9488" unit="pesanan" />
                <Paginator page={provPage} total={topProv.length} perPage={10} onPage={setProvPage} />
              </Card>
            </div>
          )}

          {/* Marketplace */}
          {market.arr.length > 0 && (
            <Card title="Penjualan per toko marketplace"
              caption={`${market.totalTx.toLocaleString("id-ID")} transaksi · ${formatRp(market.totalOmset)} omset · ${periodLabel}`}>
              <MarketplacePanel data={market.arr} maxOmset={market.arr[0] ? market.arr[0].omset : 1} />
            </Card>
          )}

          {/* Ekspedisi */}
          {topCourier.length > 0 && (
            <Card title="Kiriman per ekspedisi" caption={`Total pesanan per kurir · ${periodLabel}`}>
              <HBars data={topCourier.slice(courierPage * 10, courierPage * 10 + 10)} accent="#0d9488" unit="pesanan" />
              <Paginator page={courierPage} total={topCourier.length} perPage={10} onPage={setCourierPage} />
            </Card>
          )}
        </React.Fragment>
      )}

      {/* Produk populer — tampil meski periode utama kosong */}
      <Card title="Produk populer" caption="Berdasarkan kuantitas terjual"
        action={
          <div style={{ display: "flex", flexDirection: "column", gap: 6, alignItems: "flex-end" }}>
            <div className="qc-seg qc-seg-sm">
              {[["harian","Harian"],["mingguan","Mingguan"],["bulanan","Bulanan"],["kustom","Kustom"]].map(([k, l]) => (
                <button key={k} className={`qc-seg-btn ${prodPeriod === k ? "is-on" : ""}`} onClick={() => setProdPeriod(k)}>{l}</button>
              ))}
            </div>
            {prodPeriod === "kustom" && (
              <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
                <input type="date" value={prodCustomFrom} max={prodCustomTo}
                  onChange={(e) => setProdCustomFrom(e.target.value)}
                  style={{ padding: "4px 8px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 12 }} />
                <span style={{ fontSize: 12, color: "var(--ink-3)" }}>s/d</span>
                <input type="date" value={prodCustomTo} min={prodCustomFrom} max={new Date().toISOString().slice(0,10)}
                  onChange={(e) => setProdCustomTo(e.target.value)}
                  style={{ padding: "4px 8px", borderRadius: 6, border: "1px solid var(--line)", fontSize: 12 }} />
              </div>
            )}
          </div>
        }>
        {prodLoading && <div style={{ padding: "20px 0", color: "var(--ink-3)", fontSize: 13 }}>Memuat…</div>}
        {!prodLoading && popular.length === 0 && (
          <div style={{ padding: "20px 0", color: "var(--ink-3)", fontSize: 13 }}>Belum ada data produk pada periode ini.</div>
        )}
        {!prodLoading && popular.length > 0 && (
          <div className="qc-pop-grid">
            <HBars data={popular.slice(prodPage * 20, prodPage * 20 + 20)} accent="#7c3aed" showPhoto unit="pcs" />
            <Paginator page={prodPage} total={popular.length} perPage={20} onPage={setProdPage} />
          </div>
        )}
      </Card>
    </div>
    </>
  );
}

function Paginator({ page, total, perPage, onPage }) {
  const pages = Math.ceil(total / perPage);
  if (pages <= 1) return null;
  return (
    <div className="qc-paginator">
      <button className="qc-pag-btn" disabled={page === 0} onClick={() => onPage(page - 1)}>‹</button>
      <span className="qc-pag-info">{page + 1} / {pages}</span>
      <button className="qc-pag-btn" disabled={page >= pages - 1} onClick={() => onPage(page + 1)}>›</button>
    </div>
  );
}

function KPI({ label, value, icon, tone, sub, active, onClick }) {
  const Ic = icon;
  return (
    <div
      className={`qc-kpi qc-stat-${tone}${active ? " is-active" : ""}${onClick ? " is-clickable" : ""}`}
      onClick={onClick}
      title={onClick ? `Klik untuk melihat tren ${label}` : undefined}
      style={onClick ? { cursor: "pointer" } : undefined}
    >
      <div className="qc-kpi-top">
        <div className="qc-stat-ic"><Ic size={19} /></div>
        <span className="qc-kpi-lbl">{label}</span>
        {active && <span style={{ marginLeft: "auto", fontSize: 10, fontWeight: 700, color: "var(--pri)", textTransform: "uppercase", letterSpacing: ".04em" }}>▼ Tren</span>}
      </div>
      <div className="qc-kpi-val">{value}</div>
      <div className="qc-kpi-sub">{sub}</div>
    </div>
  );
}

function MarketplacePanel({ data, maxOmset }) {
  return (
    <div className="qc-mp">
      {data.map((d, i) => (
        <div key={d.meta.key} className="qc-mp-row">
          <div className="qc-mp-rank">{i + 1}</div>
          <div className="qc-mp-logo" style={{ background: d.meta.c }}>{d.meta.label[0]}</div>
          <div className="qc-mp-main">
            <div className="qc-mp-top">
              <span className="qc-mp-name">{d.meta.label}<i> · {d.store}</i></span>
              <span className="qc-mp-omset">{formatRp(d.omset)}</span>
            </div>
            <div className="qc-mp-bar">
              <div className="qc-mp-fill" style={{ width: (d.omset / maxOmset * 100) + "%", background: d.meta.c }} />
            </div>
          </div>
          <div className="qc-mp-tx"><b>{d.tx.toLocaleString("id-ID")}</b><span>transaksi</span></div>
        </div>
      ))}
    </div>
  );
}

function Card({ title, caption, action, children, grow }) {
  return (
    <div className={`qc-card ${grow ? "is-grow" : ""}`}>
      <div className="qc-card-head">
        <div>
          <div className="qc-card-title">{title}</div>
          {caption && <div className="qc-card-cap">{caption}</div>}
        </div>
        {action}
      </div>
      <div className="qc-card-body">{children}</div>
    </div>
  );
}

Object.assign(window, { Report, MarketplacePanel });
