// Internal admin dashboard — password-gated, not linked from public nav.
// Routes: #/admin

const PW_KEY = "consignment_pw"; // shared with consignment

async function adminApi(params, opts = {}) {
  try {
    const pw = localStorage.getItem(PW_KEY) || "";
    let r;
    if (opts.method === "POST") {
      r = await fetch("/api/admin", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ...opts.body, password: pw }),
      });
    } else {
      const qs = new URLSearchParams({ ...params, password: pw }).toString();
      r = await fetch("/api/admin?" + qs);
    }
    const text = await r.text();
    let data = {};
    try { data = JSON.parse(text); } catch {}
    return { ok: r.ok, status: r.status, data, _raw: text.slice(0, 300) };
  } catch (e) {
    return { ok: false, status: 0, data: { error: e.message || "Network error" }, _raw: "" };
  }
}

// ── Password gate ──────────────────────────────────────────
const AdminPasswordGate = ({ onUnlock }) => {
  const [pw, setPw] = React.useState("");
  const [err, setErr] = React.useState("");
  const [busy, setBusy] = React.useState(false);

  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr("");
    localStorage.setItem(PW_KEY, pw);
    const { ok, status } = await adminApi({ action: "stock" });
    setBusy(false);
    if (ok) onUnlock();
    else { localStorage.removeItem(PW_KEY); setErr(status === 401 ? "Password salah." : "Gagal terhubung."); }
  };

  return (
    <div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", padding: 24 }}>
      <form onSubmit={submit} style={{ width: "100%", maxWidth: 360 }}>
        <div className="caption" style={{ marginBottom: 8 }}>Internal</div>
        <h1 className="d-m" style={{ margin: "0 0 24px" }}>Admin</h1>
        <input
          type="password" value={pw} autoFocus placeholder="Password"
          onChange={(e) => setPw(e.target.value)}
          style={{ width: "100%", padding: "14px 16px", border: "1px solid var(--ink)", background: "transparent", fontFamily: "var(--mono)", fontSize: 13, letterSpacing: "0.04em", borderRadius: 0 }}
        />
        {err && <div style={{ color: "#a3402f", fontFamily: "var(--mono)", fontSize: 11, marginTop: 10 }}>{err}</div>}
        <button className="btn solid" type="submit" disabled={busy} style={{ width: "100%", marginTop: 16, justifyContent: "center" }}>
          {busy ? "…" : "Masuk"}
        </button>
      </form>
    </div>
  );
};

// ── Image uploader (drag-drop + browse) ───────────────────
const ImageUploader = ({ productId, currentUrl, onUploaded }) => {
  const [dragging, setDragging] = React.useState(false);
  const [uploading, setUploading] = React.useState(false);
  const [preview, setPreview] = React.useState(currentUrl || "");
  const [uploadErr, setUploadErr] = React.useState("");
  const fileRef = React.useRef(null);

  React.useEffect(() => { setPreview(currentUrl || ""); }, [currentUrl]);

  const processFile = async (file) => {
    if (!file || !file.type.startsWith("image/")) {
      setUploadErr("File harus berupa gambar (JPEG, PNG, WebP).");
      return;
    }
    setUploadErr("");

    // Show local preview immediately
    const localUrl = URL.createObjectURL(file);
    setPreview(localUrl);
    setUploading(true);

    try {
      const b64 = await new Promise((res, rej) => {
        const r = new FileReader();
        r.onload = () => res(r.result.split(",")[1]);
        r.onerror = rej;
        r.readAsDataURL(file);
      });

      const { ok, data } = await adminApi({}, {
        method: "POST",
        body: { action: "upload_image", product_id: productId, file_base64: b64, file_type: file.type },
      });

      if (ok && data.url) {
        setPreview(data.url);
        onUploaded(data.url);
      } else {
        setUploadErr(data?.error || "Upload gagal.");
        setPreview(currentUrl || "");
      }
    } catch (e) {
      setUploadErr(e.message || "Upload gagal.");
      setPreview(currentUrl || "");
    } finally {
      setUploading(false);
    }
  };

  const onDrop = (e) => {
    e.preventDefault();
    setDragging(false);
    const file = e.dataTransfer.files[0];
    if (file) processFile(file);
  };

  const zoneStyle = {
    border: `2px dashed ${dragging ? "var(--ink)" : "var(--hair-strong)"}`,
    borderRadius: 2,
    padding: preview ? "14px 16px" : "28px 20px",
    textAlign: "center",
    cursor: uploading ? "wait" : "pointer",
    background: dragging ? "rgba(0,0,0,0.03)" : "transparent",
    transition: "border-color .15s, background .15s",
    position: "relative",
    overflow: "hidden",
  };

  return (
    <div>
      <div
        style={zoneStyle}
        onClick={() => !uploading && fileRef.current?.click()}
        onDragOver={(e) => { e.preventDefault(); setDragging(true); }}
        onDragEnter={(e) => { e.preventDefault(); setDragging(true); }}
        onDragLeave={(e) => { if (!e.currentTarget.contains(e.relatedTarget)) setDragging(false); }}
        onDrop={onDrop}
      >
        {preview ? (
          <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
            <div style={{ position: "relative", flexShrink: 0 }}>
              <img src={preview} alt="preview"
                style={{ width: 64, height: 64, objectFit: "cover", border: "1px solid var(--hair)", display: "block", opacity: uploading ? 0.4 : 1 }} />
              {uploading && (
                <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center" }}>
                  <span className="mono" style={{ fontSize: 9, letterSpacing: ".1em" }}>…</span>
                </div>
              )}
            </div>
            <div style={{ textAlign: "left" }}>
              <div className="mono" style={{ fontSize: 11, color: "var(--ink)" }}>
                {uploading ? "Uploading…" : "Foto terpasang"}
              </div>
              {!uploading && (
                <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)", marginTop: 3 }}>
                  Drag atau klik untuk ganti
                </div>
              )}
            </div>
          </div>
        ) : (
          <div>
            <div className="mono" style={{ fontSize: 13, marginBottom: 6, color: dragging ? "var(--ink)" : "var(--ink-mute)" }}>
              {dragging ? "↓ Drop di sini" : uploading ? "Uploading…" : "Drag foto ke sini"}
            </div>
            <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)" }}>
              atau klik untuk pilih dari komputer · JPEG, PNG, WebP · max 5 MB
            </div>
          </div>
        )}
      </div>
      {uploadErr && (
        <div className="mono" style={{ fontSize: 11, color: "#a23", marginTop: 6 }}>{uploadErr}</div>
      )}
      <input ref={fileRef} type="file" accept="image/jpeg,image/png,image/webp,image/gif"
        style={{ display: "none" }}
        onChange={(e) => { const f = e.target.files[0]; if (f) processFile(f); e.target.value = ""; }} />
    </div>
  );
};

// ── Store photo uploader (drag-drop + browse) ─────────────
const StorePhotoUploader = ({ storeKey, currentUrl, onUploaded }) => {
  const [dragging, setDragging] = React.useState(false);
  const [uploading, setUploading] = React.useState(false);
  const [preview, setPreview] = React.useState(currentUrl || "");
  const [uploadErr, setUploadErr] = React.useState("");
  const fileRef = React.useRef(null);

  React.useEffect(() => { setPreview(currentUrl || ""); }, [currentUrl]);

  const processFile = async (file) => {
    if (!file || !file.type.startsWith("image/")) { setUploadErr("File harus gambar (JPEG, PNG, WebP)."); return; }
    setUploadErr("");
    setPreview(URL.createObjectURL(file));
    setUploading(true);
    try {
      const b64 = await new Promise((res, rej) => {
        const r = new FileReader();
        r.onload = () => res(r.result.split(",")[1]);
        r.onerror = rej;
        r.readAsDataURL(file);
      });
      const { ok, data } = await adminApi({}, {
        method: "POST",
        body: { action: "upload_store_photo", store_key: storeKey, file_base64: b64, file_type: file.type },
      });
      if (ok && data.url) { setPreview(data.url); onUploaded(data.url); }
      else { setUploadErr(data?.error || "Upload gagal."); setPreview(currentUrl || ""); }
    } catch (e) { setUploadErr(e.message || "Upload gagal."); setPreview(currentUrl || ""); }
    finally { setUploading(false); }
  };

  const onDrop = (e) => { e.preventDefault(); setDragging(false); const f = e.dataTransfer.files[0]; if (f) processFile(f); };
  const zoneStyle = {
    border: `2px dashed ${dragging ? "var(--ink)" : "var(--hair-strong)"}`,
    borderRadius: 2, padding: preview ? "14px 16px" : "28px 20px",
    textAlign: "center", cursor: uploading ? "wait" : "pointer",
    background: dragging ? "rgba(0,0,0,0.03)" : "transparent",
    transition: "border-color .15s, background .15s",
  };

  return (
    <div>
      <div style={zoneStyle}
        onClick={() => !uploading && fileRef.current?.click()}
        onDragOver={(e) => { e.preventDefault(); setDragging(true); }}
        onDragEnter={(e) => { e.preventDefault(); setDragging(true); }}
        onDragLeave={(e) => { if (!e.currentTarget.contains(e.relatedTarget)) setDragging(false); }}
        onDrop={onDrop}>
        {preview ? (
          <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
            <div style={{ position: "relative", flexShrink: 0 }}>
              <img src={preview} alt="preview" style={{ width: 64, height: 64, objectFit: "cover", border: "1px solid var(--hair)", display: "block", opacity: uploading ? 0.4 : 1 }} />
            </div>
            <div style={{ textAlign: "left" }}>
              <div className="mono" style={{ fontSize: 11 }}>{uploading ? "Uploading…" : "Foto terpasang"}</div>
              {!uploading && <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)", marginTop: 3 }}>Drag atau klik untuk ganti</div>}
            </div>
          </div>
        ) : (
          <div>
            <div className="mono" style={{ fontSize: 13, marginBottom: 6, color: dragging ? "var(--ink)" : "var(--ink-mute)" }}>
              {dragging ? "↓ Drop di sini" : uploading ? "Uploading…" : "Drag foto ke sini"}
            </div>
            <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)" }}>atau klik untuk pilih · JPEG, PNG, WebP</div>
          </div>
        )}
      </div>
      {uploadErr && <div className="mono" style={{ fontSize: 11, color: "#a23", marginTop: 6 }}>{uploadErr}</div>}
      <input ref={fileRef} type="file" accept="image/jpeg,image/png,image/webp" style={{ display: "none" }}
        onChange={(e) => { const f = e.target.files[0]; if (f) processFile(f); e.target.value = ""; }} />
    </div>
  );
};

// ── Stock tab ──────────────────────────────────────────────
const StockTab = () => {
  const [stock, setStock] = React.useState({});
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState("");
  const [editing, setEditing] = React.useState(null);
  const [editVals, setEditVals] = React.useState({ quantity: "", price: "", member_price: "", image_url: "" });
  const [saving, setSaving] = React.useState(null);
  const [saveMsg, setSaveMsg] = React.useState("");

  const products = (window.PRODUCTS || []).filter((p) => p.status !== "Archived");
  const RP = new Intl.NumberFormat("id-ID");

  const loadStock = React.useCallback(async () => {
    setLoading(true); setErr("");
    const { ok, data } = await adminApi({ action: "stock" });
    setLoading(false);
    if (!ok) { setErr(data?.error || "Gagal load stock."); return; }
    const map = {};
    (data.stock || []).forEach((r) => { map[r.product_id] = r; });
    setStock(map);
  }, []);

  React.useEffect(() => { loadStock(); }, [loadStock]);

  const startEdit = (id) => {
    const row = stock[id] || {};
    setEditing(id);
    setEditVals({
      quantity: row.quantity !== undefined ? String(row.quantity) : "",
      price: row.price != null ? String(row.price) : "",
      member_price: row.member_price != null ? String(row.member_price) : "",
      image_url: row.image_url || "",
    });
    setSaveMsg("");
  };

  const saveStock = async (id) => {
    setSaving(id);
    const body = {
      action: "stock_set",
      product_id: id,
      quantity: Math.max(0, parseInt(editVals.quantity, 10) || 0),
      price: editVals.price || null,
      member_price: editVals.member_price || null,
      image_url: editVals.image_url || null,
    };
    const { ok, data } = await adminApi({}, { method: "POST", body });
    setSaving(null);
    if (ok) {
      setStock((s) => ({
        ...s,
        [id]: {
          ...(s[id] || { product_id: id }),
          quantity: body.quantity,
          price: body.price ? parseFloat(body.price) : null,
          member_price: body.member_price ? parseFloat(body.member_price) : null,
          image_url: body.image_url,
        },
      }));
      setEditing(null);
      setSaveMsg("Tersimpan.");
      setTimeout(() => setSaveMsg(""), 2000);
    } else {
      setSaveMsg(data?.error || "Gagal simpan.");
    }
  };

  const numInput = (placeholder) => ({
    type: "number", placeholder,
    style: { width: "100%", padding: "8px 12px", border: "1px solid var(--ink)", background: "transparent", fontFamily: "var(--mono)", fontSize: 12, borderRadius: 0 },
  });

  return (
    <div style={{ padding: "32px var(--pad-x)" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 28 }}>
        <h2 className="d-m" style={{ margin: 0 }}>Stock</h2>
        {saveMsg && <span className="mono" style={{ fontSize: 11, color: "var(--ink-mute)" }}>{saveMsg}</span>}
      </div>

      {loading && <p className="mono" style={{ fontSize: 11, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-mute)" }}>Loading…</p>}
      {err && <p className="mono" style={{ fontSize: 11, color: "#a23" }}>{err}</p>}

      {!loading && products.map((p) => {
        const row = stock[p.id] || {};
        const isEditing = editing === p.id;
        const qty = row.quantity;

        return (
          <div key={p.id} style={{ borderBottom: "1px solid var(--hair)" }}>
            {/* Summary row */}
            <div style={{ display: "flex", alignItems: "center", gap: 16, padding: "14px 0" }}>
              <div style={{ width: 56, height: 56, flexShrink: 0, border: "1px solid var(--hair)", overflow: "hidden", background: "var(--cream)", display: "flex", alignItems: "center", justifyContent: "center" }}>
                {(row.image_url || p.image)
                  ? <img src={row.image_url || p.image} alt={p.name} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
                  : <span className="mono" style={{ fontSize: 9, color: "var(--ink-mute)" }}>foto</span>
                }
              </div>

              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: "var(--serif)", fontStyle: "italic", fontSize: 18 }}>{p.name}</div>
                <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)", marginTop: 2 }}>{p.id}</div>
              </div>

              <div style={{ textAlign: "right", minWidth: 110 }}>
                <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 3 }}>Harga</div>
                <div className="mono" style={{ fontSize: 13 }}>
                  {row.price ? `Rp ${RP.format(row.price)}` : <span style={{ color: "var(--ink-mute)" }}>—</span>}
                </div>
                {row.member_price != null && (
                  <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)", marginTop: 2 }}>
                    Member Rp {RP.format(row.member_price)}
                  </div>
                )}
              </div>

              <div style={{ textAlign: "right", minWidth: 64 }}>
                <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 3 }}>Stock</div>
                <div className="mono" style={{ fontSize: 13, color: qty === 0 ? "#a23" : qty == null ? "var(--ink-mute)" : "var(--ink)" }}>
                  {qty == null ? "—" : RP.format(qty)}
                </div>
              </div>

              <button className="lnk" style={{ fontSize: 10, flexShrink: 0 }}
                onClick={() => isEditing ? setEditing(null) : startEdit(p.id)}>
                {isEditing ? "Batal" : "Edit"}
              </button>
            </div>

            {/* Edit panel */}
            {isEditing && (
              <div style={{ paddingBottom: 28, paddingLeft: 72 }}>
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 16, marginBottom: 16 }}>
                  <label>
                    <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 6 }}>Stock (unit)</div>
                    <input {...numInput("0")} min="0" value={editVals.quantity} autoFocus
                      onChange={(e) => setEditVals((v) => ({ ...v, quantity: e.target.value }))}
                      onKeyDown={(e) => { if (e.key === "Enter") saveStock(p.id); if (e.key === "Escape") setEditing(null); }} />
                  </label>
                  <label>
                    <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 6 }}>Harga Guest (Rp)</div>
                    <input {...numInput("150000")} min="0" value={editVals.price}
                      onChange={(e) => setEditVals((v) => ({ ...v, price: e.target.value }))}
                      onKeyDown={(e) => { if (e.key === "Escape") setEditing(null); }} />
                  </label>
                  <label>
                    <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 6 }}>Harga Member (Rp)</div>
                    <input {...numInput("140000")} min="0" value={editVals.member_price}
                      onChange={(e) => setEditVals((v) => ({ ...v, member_price: e.target.value }))}
                      onKeyDown={(e) => { if (e.key === "Escape") setEditing(null); }} />
                  </label>
                </div>

                <div style={{ marginBottom: 16 }}>
                  <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 8 }}>Foto Produk</div>
                  <ImageUploader
                    productId={p.id}
                    currentUrl={editVals.image_url}
                    onUploaded={(url) => setEditVals((v) => ({ ...v, image_url: url }))}
                  />
                </div>

                <div style={{ display: "flex", gap: 12 }}>
                  <button className="btn solid" style={{ padding: "8px 20px", fontSize: 11 }}
                    onClick={() => saveStock(p.id)} disabled={saving === p.id}>
                    {saving === p.id ? "…" : "Simpan"}
                  </button>
                  <button className="lnk" style={{ fontSize: 11 }} onClick={() => setEditing(null)}>Batal</button>
                </div>
              </div>
            )}
          </div>
        );
      })}

      <p className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", marginTop: 24, lineHeight: 1.7 }}>
        Stock "—" = belum diset (tidak ada batas). Stock 0 = habis (tombol beli di-disable).<br />
        Harga dan foto override nilai dari Notion — kosongkan untuk pakai nilai Notion.
      </p>
    </div>
  );
};

// ── Orders tab ─────────────────────────────────────────────

const STATUS_COLORS = {
  pending: "#b45309",
  paid: "#2563eb",
  processing: "#7c3aed",
  shipped: "#0891b2",
  delivered: "#16a34a",
  cancelled: "#dc2626",
  expired: "#9ca3af",
};

const STATUS_LABELS = {
  pending: "Pending", paid: "Paid", processing: "Processing",
  shipped: "Shipped", delivered: "Delivered", cancelled: "Cancelled", expired: "Expired",
};

const STATUS_NEXT = {
  paid: ["processing", "cancelled"],
  processing: ["shipped", "cancelled"],
  shipped: ["delivered"],
};


const OrdersTab = ({ autoOrder, autoPrint }) => {
  const [orders, setOrders] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState("");
  const [filterStatus, setFilterStatus] = React.useState("all");
  const [expanded, setExpanded] = React.useState(autoOrder || null);
  const [updatingStatus, setUpdatingStatus] = React.useState(null);
  const [copiedId, setCopiedId] = React.useState(null);
  const [fetchingLabel, setFetchingLabel] = React.useState(null);
  const [labelErr, setLabelErr] = React.useState({});
  const didAutoPrint = React.useRef(false);
  const autoOrderRef = React.useRef(null);

  const RP = new Intl.NumberFormat("id-ID");

  const loadOrders = React.useCallback(async () => {
    setLoading(true); setErr("");
    const params = { action: "orders" };
    if (filterStatus !== "all") params.status = filterStatus;
    const { ok, status, data } = await adminApi(params);
    setLoading(false);
    if (!ok) {
      setErr(`Error ${status}: ${data?.error || "Gagal load orders."}`);
      return;
    }
    setOrders(data.orders || []);
  }, [filterStatus]);

  React.useEffect(() => { loadOrders(); }, [loadOrders]);

  const updateStatus = async (orderId, newStatus, e) => {
    e.stopPropagation();
    setUpdatingStatus(orderId);
    const { ok } = await adminApi({}, { method: "POST", body: { action: "order_status", order_id: orderId, status: newStatus } });
    setUpdatingStatus(null);
    if (ok) setOrders((prev) => prev.map((o) => o.id === orderId ? { ...o, status: newStatus } : o));
  };

  const fetchLabel = async (order) => {
    setFetchingLabel(order.id);
    setLabelErr((prev) => ({ ...prev, [order.id]: "" }));
    const { ok, data } = await adminApi({ action: "order_label", order_id: order.id });
    setFetchingLabel(null);
    if (!ok || !data.html) {
      setLabelErr((prev) => ({ ...prev, [order.id]: data?.error || "Gagal ambil label dari Biteship." }));
      return;
    }
    const w = window.open("", "_blank", "width=800,height=1000");
    w.document.write(data.html);
    w.document.close();
  };

  const openOfficialLabel = (order, e) => { e.stopPropagation(); fetchLabel(order); };

  React.useEffect(() => {
    if (!autoOrder || loading || !orders.length || didAutoPrint.current) return;
    const order = orders.find((o) => o.id === autoOrder);
    if (!order) return;
    didAutoPrint.current = true;
    setExpanded(autoOrder);
    setTimeout(() => autoOrderRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }), 80);
  }, [orders, loading]);

  const copyResi = (resi, orderId, e) => {
    e.stopPropagation();
    navigator.clipboard.writeText(resi);
    setCopiedId(orderId);
    setTimeout(() => setCopiedId(null), 2000);
  };

  const fmtDate = (ts) => {
    if (!ts) return "—";
    return new Date(ts).toLocaleString("id-ID", { day: "2-digit", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" });
  };

  const FILTER_LIST = ["all", "pending", "paid", "processing", "shipped", "delivered", "cancelled"];

  const chipStyle = (active) => ({
    fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".1em", textTransform: "uppercase",
    padding: "5px 12px", cursor: "pointer",
    border: `1px solid ${active ? "var(--ink)" : "var(--hair-strong)"}`,
    background: active ? "var(--ink)" : "transparent",
    color: active ? "var(--cream)" : "var(--ink-mute)",
  });

  return (
    <div style={{ padding: "32px var(--pad-x)" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 24 }}>
        <h2 className="d-m" style={{ margin: 0 }}>Orders</h2>
        <button className="lnk" style={{ fontSize: 11 }} onClick={loadOrders}>Refresh</button>
      </div>

      <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 28 }}>
        {FILTER_LIST.map((f) => (
          <button key={f} style={chipStyle(filterStatus === f)} onClick={() => setFilterStatus(f)}>{f}</button>
        ))}
      </div>

      {loading && <p className="mono" style={{ fontSize: 11, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-mute)" }}>Loading…</p>}

      {err && (
        <div style={{ background: "#fff3f3", border: "1px solid #f5c6c6", padding: "14px 16px", marginBottom: 20 }}>
          <div className="mono" style={{ fontSize: 11, color: "#a23" }}>{err}</div>
          <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)", marginTop: 8, lineHeight: 1.7 }}>
            Pastikan tabel <code>orders</code> sudah dibuat di Supabase (jalankan <code>supabase/orders.sql</code>).
          </div>
        </div>
      )}

{!loading && !err && orders.length === 0 && (
        <div>
          <p className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>Belum ada order.</p>
          <p className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", marginTop: 8, lineHeight: 1.7 }}>
            Untuk test UI, jalankan <code>supabase/dummy_orders.sql</code> di Supabase SQL Editor.
          </p>
        </div>
      )}

      {!loading && orders.map((order) => {
        const isExpanded = expanded === order.id;
        const itemsSummary = (order.items || []).map((i) => `${i.name} ×${i.qty}`).join(", ");
        const nexts = STATUS_NEXT[order.status] || [];

        const isAutoTarget = order.id === autoOrder;
        return (
          <div key={order.id} ref={isAutoTarget ? autoOrderRef : null} style={{ borderBottom: "1px solid var(--hair)", scrollMarginTop: 80 }}>
            <div
              style={{ display: "grid", gridTemplateColumns: "1fr auto 20px", gap: 16, padding: "16px 0", cursor: "pointer", alignItems: "start" }}
              onClick={() => setExpanded(isExpanded ? null : order.id)}
            >
              <div>
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 5 }}>
                  <span className="mono" style={{ fontSize: 10, color: "var(--ink-mute)" }}>{fmtDate(order.created_at)}</span>
                  <span className="mono" style={{ fontSize: 9, padding: "2px 8px", background: STATUS_COLORS[order.status] || "#555", color: "#fff", letterSpacing: ".1em", textTransform: "uppercase" }}>
                    {STATUS_LABELS[order.status] || order.status}
                  </span>
                </div>
                <div style={{ fontFamily: "var(--serif)", fontStyle: "italic", fontSize: 18 }}>{order.customer_name}</div>
                <div className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", marginTop: 2 }}>{itemsSummary}</div>
              </div>
              <div style={{ textAlign: "right" }}>
                <div className="mono" style={{ fontSize: 14 }}>Rp {RP.format(order.total)}</div>
                {order.waybill_id && (
                  <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)", marginTop: 4 }}>Resi: {order.waybill_id}</div>
                )}
              </div>
              <span className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", paddingTop: 2 }}>{isExpanded ? "▲" : "▼"}</span>
            </div>

            {isExpanded && (
              <div style={{ paddingBottom: 24 }}>
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 24, marginBottom: 20 }}>
                  <div>
                    <div className="mono" style={{ fontSize: 9, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 8 }}>Customer</div>
                    <div className="mono" style={{ fontSize: 13, fontWeight: 600 }}>{order.customer_name}</div>
                    <div className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>{order.customer_phone}</div>
                    {order.customer_email && <div className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>{order.customer_email}</div>}
                    <div className="mono" style={{ fontSize: 12, marginTop: 8, lineHeight: 1.6 }}>{order.address}</div>
                    <div className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>{order.area_label}</div>
                    {order.note && <div className="mono" style={{ fontSize: 12, fontStyle: "italic", color: "var(--ink-mute)", marginTop: 4 }}>Catatan: {order.note}</div>}
                  </div>

                  <div>
                    <div className="mono" style={{ fontSize: 9, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 8 }}>Pengiriman</div>
                    <div className="mono" style={{ fontSize: 13 }}>{order.courier_name || "—"}</div>
                    {order.shipping_etd && <div className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>{order.shipping_etd}</div>}
                    <div style={{ marginTop: 12 }}>
                      <div className="mono" style={{ fontSize: 9, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 6 }}>No. Resi / AWB</div>
                      {order.waybill_id ? (
                        <>
                          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                            <span className="mono" style={{ fontSize: 18, fontWeight: 700, letterSpacing: ".04em" }}>{order.waybill_id}</span>
                            <button className="lnk" style={{ fontSize: 10 }} onClick={(e) => copyResi(order.waybill_id, order.id, e)}>
                              {copiedId === order.id ? "Copied!" : "Copy"}
                            </button>
                          </div>
                          {order.tracking_url && (
                            <a href={order.tracking_url} target="_blank" rel="noopener noreferrer" className="lnk" style={{ fontSize: 11, marginTop: 4, display: "inline-block" }} onClick={(e) => e.stopPropagation()}>
                              Cek tracking →
                            </a>
                          )}
                        </>
                      ) : (
                        <span className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>Belum tersedia</span>
                      )}
                    </div>
                  </div>
                </div>

                <div style={{ marginBottom: 20 }}>
                  <div className="mono" style={{ fontSize: 9, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 10 }}>Items</div>
                  {(order.items || []).map((item, idx) => (
                    <div key={idx} style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: "1px solid var(--hair)" }}>
                      <span className="mono" style={{ fontSize: 12 }}>{item.name} × {item.qty}</span>
                      <span className="mono" style={{ fontSize: 12 }}>Rp {RP.format(item.price * item.qty)}</span>
                    </div>
                  ))}
                  <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: "1px solid var(--hair)" }}>
                    <span className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>Ongkir ({order.courier_name || "—"})</span>
                    <span className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>Rp {RP.format(order.shipping_cost)}</span>
                  </div>
                  <div style={{ display: "flex", justifyContent: "space-between", padding: "8px 0" }}>
                    <span className="mono" style={{ fontSize: 13, fontWeight: 600 }}>Total</span>
                    <span className="mono" style={{ fontSize: 13, fontWeight: 600 }}>Rp {RP.format(order.total)}</span>
                  </div>
                </div>

                <div style={{ display: "flex", gap: 12, flexWrap: "wrap", alignItems: "center" }}>
                  <button className="btn solid" style={{ padding: "8px 16px", fontSize: 11, ...(isAutoTarget && autoPrint ? { outline: "2px solid var(--ink)", outlineOffset: 3 } : {}) }}
                    disabled={fetchingLabel === order.id}
                    onClick={(e) => openOfficialLabel(order, e)}>
                    {fetchingLabel === order.id ? "…" : "Cetak Resi"}
                  </button>
                  {nexts.map((ns) => (
                    <button key={ns} className="btn" style={{ padding: "8px 16px", fontSize: 11 }}
                      disabled={updatingStatus === order.id}
                      onClick={(e) => updateStatus(order.id, ns, e)}>
                      {updatingStatus === order.id ? "…" : `Tandai ${STATUS_LABELS[ns]}`}
                    </button>
                  ))}
                </div>
                {labelErr[order.id] && (
                  <div className="mono" style={{ fontSize: 11, color: "#a23", marginTop: 8 }}>{labelErr[order.id]}</div>
                )}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
};

// ── Consignment management tab ─────────────────────────────

const RULE_TYPES = [
  { value: "pct_of_gross", label: "% dari Gross" },
  { value: "per_unit", label: "Per Unit (COGS)" },
  { value: "pct_of_remainder", label: "% dari Sisa" },
  { value: "fixed", label: "Fixed (Rp)" },
];

const slugify = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");

const blankConsignmentForm = () => ({
  id: "", name: "", partner: "", note: "",
  revenue_rules: [{ uid: Date.now(), label: "", type: "pct_of_gross", value: "" }],
});

const ConsignmentMgmt = () => {
  const [list, setList] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [form, setForm] = React.useState(null);
  const [isNew, setIsNew] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const [deleting, setDeleting] = React.useState(null);
  const [msg, setMsg] = React.useState("");

  const loadList = React.useCallback(async () => {
    setLoading(true);
    const { ok, data } = await adminApi({ action: "consignment_list" });
    setLoading(false);
    if (ok) setList(data.consignments || []);
  }, []);

  React.useEffect(() => { loadList(); }, [loadList]);

  const openNew = () => { setForm(blankConsignmentForm()); setIsNew(true); setMsg(""); };
  const openEdit = (c) => {
    setForm({
      id: c.id, name: c.name, partner: c.partner || "", note: c.note || "",
      revenue_rules: (c.revenue_rules || []).map((r) => ({ ...r, uid: r.uid || Date.now() + Math.random() })),
    });
    setIsNew(false); setMsg("");
  };
  const closeForm = () => { setForm(null); setMsg(""); };

  const setField = (k, v) => setForm((f) => ({
    ...f, [k]: v,
    ...(k === "name" && isNew && !f.id ? { id: slugify(v) } : {}),
  }));

  const addRule = () => setForm((f) => ({
    ...f, revenue_rules: [...f.revenue_rules, { uid: Date.now(), label: "", type: "pct_of_gross", value: "" }],
  }));
  const removeRule = (uid) => setForm((f) => ({
    ...f, revenue_rules: f.revenue_rules.filter((r) => r.uid !== uid),
  }));
  const setRule = (uid, k, v) => setForm((f) => ({
    ...f, revenue_rules: f.revenue_rules.map((r) => r.uid === uid ? { ...r, [k]: v } : r),
  }));

  const save = async () => {
    if (!form.id || !form.name) { setMsg("ID dan Nama wajib diisi."); return; }
    setSaving(true); setMsg("");
    const rules = form.revenue_rules
      .filter((r) => r.label && r.value !== "")
      .map((r) => ({ id: slugify(r.label), label: r.label, type: r.type, value: parseFloat(r.value) || 0 }));
    const { ok, data } = await adminApi({}, {
      method: "POST",
      body: { action: "consignment_upsert", id: form.id, name: form.name, partner: form.partner || null, note: form.note || null, revenue_rules: rules },
    });
    setSaving(false);
    if (ok) { await loadList(); closeForm(); }
    else setMsg(data?.error || "Gagal simpan.");
  };

  const del = async (id) => {
    if (!window.confirm(`Hapus konsinyasi "${id}"? Data penjualan terkait akan ikut terhapus.`)) return;
    setDeleting(id);
    const { ok, data } = await adminApi({}, { method: "POST", body: { action: "consignment_delete", id } });
    setDeleting(null);
    if (ok) setList((l) => l.filter((c) => c.id !== id));
    else alert(data?.error || "Gagal hapus.");
  };

  const iStyle = {
    width: "100%", padding: "8px 12px", border: "1px solid var(--ink)",
    background: "transparent", fontFamily: "var(--mono)", fontSize: 12, borderRadius: 0,
  };

  return (
    <div style={{ padding: "32px var(--pad-x)" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 24 }}>
        <h2 className="d-m" style={{ margin: 0 }}>Konsinyasi</h2>
        {!form && <button className="btn solid" style={{ padding: "8px 16px", fontSize: 11 }} onClick={openNew}>+ Tambah</button>}
      </div>

      {loading && <p className="mono" style={{ fontSize: 11, color: "var(--ink-mute)" }}>Loading…</p>}

      {/* Form */}
      {form && (
        <div style={{ border: "1px solid var(--hair)", padding: 24, marginBottom: 28 }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 16 }}>
            {isNew ? "Konsinyasi Baru" : `Edit: ${form.id}`}
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 16 }}>
            {[
              { k: "id", label: "ID (slug) *", ph: "muku", disabled: !isNew },
              { k: "name", label: "Nama *", ph: "MuKu (MDS)" },
              { k: "partner", label: "Partner", ph: "MDS" },
              { k: "note", label: "Catatan", ph: "Blok M (705) + Pakuwon SBY" },
            ].map(({ k, label, ph, disabled }) => (
              <label key={k}>
                <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 6 }}>{label}</div>
                <input style={iStyle} value={form[k]} placeholder={ph} disabled={disabled}
                  onChange={(e) => setField(k, k === "id" ? e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, "") : e.target.value)} />
              </label>
            ))}
          </div>

          <div style={{ marginBottom: 20 }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10 }}>
              <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)" }}>Revenue Rules</div>
              <button className="lnk" style={{ fontSize: 10 }} onClick={addRule}>+ Baris</button>
            </div>
            <div className="mono" style={{ fontSize: 9, color: "var(--ink-mute)", marginBottom: 10, lineHeight: 1.6 }}>
              Urutan rules = urutan pemotongan dari Gross. Sisa setelah semua rules = bagian onethird.
            </div>
            {form.revenue_rules.map((rule) => (
              <div key={rule.uid} style={{ display: "grid", gridTemplateColumns: "2fr 1.5fr 1fr auto", gap: 8, marginBottom: 8, alignItems: "center" }}>
                <input style={iStyle} value={rule.label} placeholder="Label (misal: MDS)"
                  onChange={(e) => setRule(rule.uid, "label", e.target.value)} />
                <select style={{ ...iStyle, cursor: "pointer" }} value={rule.type}
                  onChange={(e) => setRule(rule.uid, "type", e.target.value)}>
                  {RULE_TYPES.map((t) => <option key={t.value} value={t.value}>{t.label}</option>)}
                </select>
                <input type="number" style={iStyle} value={rule.value} placeholder="Nilai"
                  onChange={(e) => setRule(rule.uid, "value", e.target.value)} />
                <button onClick={() => removeRule(rule.uid)}
                  style={{ background: "none", border: "none", cursor: "pointer", fontSize: 18, color: "var(--ink-mute)", padding: "0 4px" }}>×</button>
              </div>
            ))}
            {form.revenue_rules.length === 0 && (
              <div className="mono" style={{ fontSize: 11, color: "var(--ink-mute)" }}>Belum ada rule.</div>
            )}
          </div>

          {msg && <div className="mono" style={{ fontSize: 11, color: "#a23", marginBottom: 12 }}>{msg}</div>}
          <div style={{ display: "flex", gap: 12 }}>
            <button className="btn solid" style={{ padding: "8px 20px", fontSize: 11 }} disabled={saving} onClick={save}>{saving ? "…" : "Simpan"}</button>
            <button className="lnk" style={{ fontSize: 11 }} onClick={closeForm}>Batal</button>
          </div>
        </div>
      )}

      {/* List */}
      {!loading && list.map((c) => (
        <div key={c.id} style={{ borderBottom: "1px solid var(--hair)", padding: "14px 0", display: "flex", alignItems: "flex-start", gap: 16 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: "var(--serif)", fontStyle: "italic", fontSize: 18 }}>{c.name}</div>
            <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)", marginTop: 2 }}>
              {c.id}{c.partner ? ` · ${c.partner}` : ""}
              {c.note ? <span style={{ marginLeft: 8 }}>{c.note}</span> : ""}
            </div>
            {(c.revenue_rules || []).length > 0 && (
              <div className="mono" style={{ fontSize: 10, color: "var(--ink-mute)", marginTop: 4 }}>
                {(c.revenue_rules || []).map((r) => {
                  const suffix = r.type === "pct_of_gross" || r.type === "pct_of_remainder" ? "%" : r.type === "per_unit" ? "/unit" : " Rp";
                  return `${r.label} ${r.value}${suffix}`;
                }).join(" → ")}
              </div>
            )}
          </div>
          <button className="lnk" style={{ fontSize: 10, flexShrink: 0 }} onClick={() => openEdit(c)}>Edit</button>
          <button className="lnk" style={{ fontSize: 10, flexShrink: 0, color: "#a23" }}
            disabled={deleting === c.id} onClick={() => del(c.id)}>
            {deleting === c.id ? "…" : "Hapus"}
          </button>
        </div>
      ))}

      {!loading && list.length === 0 && !form && (
        <p className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>Belum ada konsinyasi. Klik "+ Tambah" untuk membuat baru.</p>
      )}
    </div>
  );
};

// ── Store locations tab ────────────────────────────────────

const blankStoreForm = () => ({ id: null, _photo_key: `new_${Date.now()}`, brand: "", city: "", area: "", hours: "", note: "", maps_url: "", photo_url: "" });

const TokoTab = () => {
  const [stores, setStores] = React.useState([]);
  const [brandOrder, setBrandOrder] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState("");
  const [form, setForm] = React.useState(null);
  const [saving, setSaving] = React.useState(false);
  const [reordering, setReordering] = React.useState(false);
  const [deleting, setDeleting] = React.useState(null);
  const [msg, setMsg] = React.useState("");
  const [syncing, setSyncing] = React.useState(false);
  const [dragOver, setDragOver] = React.useState(null);
  const dragBrandRef = React.useRef(null);
  const formRef = React.useRef(null);

  const loadStores = React.useCallback(async () => {
    setLoading(true); setErr("");
    const { ok, data } = await adminApi({ action: "stores_list" });
    setLoading(false);
    if (ok) {
      const rows = data.stores || [];
      setStores(rows);
      // Derive brand order from sort_order (already sorted asc by API)
      const seen = [];
      rows.forEach((s) => { if (s.brand && !seen.includes(s.brand)) seen.push(s.brand); });
      setBrandOrder(seen);
    } else {
      setErr(data?.error || "Gagal load toko.");
    }
  }, []);

  React.useEffect(() => { loadStores(); }, [loadStores]);

  // Persist brand order: assign sort_order = brandIdx * 1000 + locationIdx
  const saveBrandOrder = async (newOrder, currentStores) => {
    setReordering(true);
    const rows = currentStores || stores;
    for (let bi = 0; bi < newOrder.length; bi++) {
      const locs = rows.filter((s) => s.brand === newOrder[bi]);
      for (let li = 0; li < locs.length; li++) {
        await adminApi({}, {
          method: "POST",
          body: { action: "store_upsert", id: locs[li].id, brand: locs[li].brand, sort_order: bi * 1000 + li },
        });
      }
    }
    setReordering(false);
    setMsg("✓ Urutan tersimpan.");
    setTimeout(() => setMsg(""), 2000);
  };

  const syncFromNotion = async () => {
    setSyncing(true); setMsg(""); setErr("");
    try {
      const r = await fetch("/api/stores?source=notion");
      const brands = await r.json();
      if (!Array.isArray(brands) || !brands.length) { setMsg("Tidak ada data dari Notion."); setSyncing(false); return; }
      let count = 0;
      for (const brand of brands) {
        for (let i = 0; i < (brand.locations || []).length; i++) {
          const loc = brand.locations[i];
          await adminApi({}, {
            method: "POST",
            body: {
              action: "store_upsert",
              brand: brand.name,
              city: loc.city || null,
              area: loc.area || null,
              hours: loc.hours || null,
              note: loc.note || null,
              maps_url: loc.mapsUrl || null,
              photo_url: loc.photo || null,
              sort_order: i,
            },
          });
          count++;
        }
      }
      await loadStores();
      setMsg(`✓ ${count} lokasi diimport dari Notion.`);
    } catch (e) { setErr(e.message || "Gagal sync dari Notion."); }
    setSyncing(false);
  };

  const scrollToForm = () => setTimeout(() => formRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }), 30);
  const openNew = () => { setForm(blankStoreForm()); setMsg(""); scrollToForm(); };
  const openEdit = (s) => {
    setForm({
      id: s.id, brand: s.brand || "", city: s.city || "", area: s.area || "",
      hours: s.hours || "", note: s.note || "", maps_url: s.maps_url || "",
      photo_url: s.photo_url || "",
    });
    setMsg("");
    scrollToForm();
  };
  const closeForm = () => { setForm(null); setMsg(""); };
  const setF = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  const save = async () => {
    if (!form.brand) { setMsg("Brand wajib diisi."); return; }
    setSaving(true); setMsg("");
    // Preserve existing sort_order so drag order isn't lost on edit
    const existing = stores.find((s) => s.id === form.id);
    const { ok, data } = await adminApi({}, {
      method: "POST",
      body: { action: "store_upsert", ...form, sort_order: existing?.sort_order ?? (brandOrder.indexOf(form.brand) * 1000) },
    });
    setSaving(false);
    if (ok) { await loadStores(); closeForm(); }
    else setMsg(data?.error || "Gagal simpan.");
  };

  const del = async (id, brand) => {
    if (!window.confirm(`Hapus lokasi toko "${brand}"?`)) return;
    setDeleting(id);
    const { ok, data } = await adminApi({}, { method: "POST", body: { action: "store_delete", id } });
    setDeleting(null);
    if (ok) setStores((l) => l.filter((s) => s.id !== id));
    else alert(data?.error || "Gagal hapus.");
  };

  const iStyle = {
    width: "100%", padding: "8px 12px", border: "1px solid var(--ink)",
    background: "transparent", fontFamily: "var(--mono)", fontSize: 12, borderRadius: 0,
  };

  const grouped = {};
  stores.forEach((s) => { if (!grouped[s.brand]) grouped[s.brand] = []; grouped[s.brand].push(s); });

  const onDragStart = (brand) => { dragBrandRef.current = brand; };
  const onDragOver = (e, brand) => { e.preventDefault(); setDragOver(brand); };
  const onDrop = (e, targetBrand) => {
    e.preventDefault();
    const srcBrand = dragBrandRef.current;
    if (!srcBrand || srcBrand === targetBrand) { setDragOver(null); return; }
    const newOrder = [...brandOrder];
    const fromIdx = newOrder.indexOf(srcBrand);
    const toIdx = newOrder.indexOf(targetBrand);
    newOrder.splice(fromIdx, 1);
    newOrder.splice(toIdx, 0, srcBrand);
    setBrandOrder(newOrder);
    setDragOver(null);
    dragBrandRef.current = null;
    saveBrandOrder(newOrder);
  };
  const onDragEnd = () => { setDragOver(null); dragBrandRef.current = null; };

  const moveLocation = async (brand, locId, dir) => {
    const locs = [...(grouped[brand] || [])];
    const idx = locs.findIndex((s) => s.id === locId);
    const swapIdx = idx + dir;
    if (swapIdx < 0 || swapIdx >= locs.length) return;
    [locs[idx], locs[swapIdx]] = [locs[swapIdx], locs[idx]];
    const bi = brandOrder.indexOf(brand);
    setReordering(true);
    for (let li = 0; li < locs.length; li++) {
      await adminApi({}, {
        method: "POST",
        body: { action: "store_upsert", id: locs[li].id, brand: locs[li].brand, sort_order: bi * 1000 + li },
      });
    }
    setReordering(false);
    setStores((prev) => [...prev.filter((s) => s.brand !== brand), ...locs]);
    setMsg("✓ Urutan tersimpan.");
    setTimeout(() => setMsg(""), 2000);
  };

  return (
    <div style={{ padding: "32px var(--pad-x)" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 24 }}>
        <h2 className="d-m" style={{ margin: 0 }}>Toko</h2>
        {!form && (
          <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
            {reordering && <span className="mono" style={{ fontSize: 10, color: "var(--ink-mute)" }}>Menyimpan urutan…</span>}
            <button className="lnk" style={{ fontSize: 11 }} disabled={syncing} onClick={syncFromNotion}>
              {syncing ? "Sync…" : "↓ Sync dari Notion"}
            </button>
            <button className="btn solid" style={{ padding: "8px 16px", fontSize: 11 }} onClick={openNew}>+ Tambah</button>
          </div>
        )}
      </div>
      {msg && <div className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", marginBottom: 16 }}>{msg}</div>}

      {loading && <p className="mono" style={{ fontSize: 11, color: "var(--ink-mute)" }}>Loading…</p>}
      {err && <p className="mono" style={{ fontSize: 11, color: "#a23" }}>{err}</p>}

      {/* Form */}
      {form && (
        <div ref={formRef} style={{ border: "1px solid var(--hair)", padding: 24, marginBottom: 28 }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 16 }}>
            {form.id ? "Edit Toko" : "Toko Baru"}
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 16 }}>
            {[
              { k: "brand", label: "Brand / Nama Toko *", ph: "MuKu" },
              { k: "city", label: "Kota", ph: "Jakarta" },
              { k: "area", label: "Area / Lokasi", ph: "Blok M MDS" },
              { k: "hours", label: "Jam Buka", ph: "Mon–Sun 10:00–22:00" },
              { k: "note", label: "Catatan Stok", ph: "Tersedia di MuKu Blok M" },
              { k: "maps_url", label: "Google Maps URL", ph: "https://maps.app.goo.gl/..." },
            ].map(({ k, label, ph, type }) => (
              <label key={k}>
                <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 6 }}>{label}</div>
                <input style={iStyle} type={type || "text"} value={form[k] || ""} placeholder={ph}
                  onChange={(e) => setF(k, e.target.value)} />
              </label>
            ))}
            <label>
              <div className="mono" style={{ fontSize: 9, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-mute)", marginBottom: 6 }}>Foto</div>
              <StorePhotoUploader
                storeKey={form.id ? String(form.id) : (form._photo_key || `new_${Date.now()}`)}
                currentUrl={form.photo_url}
                onUploaded={(url) => setF("photo_url", url)}
              />
            </label>
          </div>

          {msg && <div className="mono" style={{ fontSize: 11, color: "#a23", marginBottom: 12 }}>{msg}</div>}
          <div style={{ display: "flex", gap: 12 }}>
            <button className="btn solid" style={{ padding: "8px 20px", fontSize: 11 }} disabled={saving} onClick={save}>{saving ? "…" : "Simpan"}</button>
            <button className="lnk" style={{ fontSize: 11 }} onClick={closeForm}>Batal</button>
          </div>
        </div>
      )}

      {/* Grouped list — drag brand rows to reorder */}
      {!loading && brandOrder.filter((b) => grouped[b]).map((brand) => {
        const locs = grouped[brand] || [];
        const isOver = dragOver === brand;
        return (
          <div
            key={brand}
            draggable
            onDragStart={() => onDragStart(brand)}
            onDragOver={(e) => onDragOver(e, brand)}
            onDrop={(e) => onDrop(e, brand)}
            onDragEnd={onDragEnd}
            style={{
              marginBottom: 24,
              border: isOver ? "1px dashed var(--ink)" : "1px solid transparent",
              padding: isOver ? "0 8px" : "0 0",
              borderRadius: 2,
              transition: "border .15s, padding .15s",
              cursor: "grab",
            }}
          >
            <div style={{ display: "flex", alignItems: "baseline", gap: 10, marginBottom: 8, paddingBottom: 6, borderBottom: "1px solid var(--hair-strong)" }}>
              <span style={{ fontSize: 14, opacity: 0.3, userSelect: "none", cursor: "grab" }}>⠿</span>
              <span style={{ fontFamily: "var(--serif)", fontStyle: "italic", fontSize: 20 }}>{brand}</span>
            </div>
            {locs.map((s, locIdx) => (
              <div key={s.id} style={{ borderBottom: "1px solid var(--hair)", padding: "10px 0", display: "flex", alignItems: "flex-start", gap: 16 }}>
                <div style={{ display: "flex", flexDirection: "column", gap: 2, flexShrink: 0, paddingTop: 2 }}>
                  <button className="lnk" style={{ fontSize: 10, lineHeight: 1, opacity: locIdx === 0 ? 0.2 : 1 }}
                    disabled={locIdx === 0 || reordering}
                    onClick={(e) => { e.stopPropagation(); moveLocation(brand, s.id, -1); }}>↑</button>
                  <button className="lnk" style={{ fontSize: 10, lineHeight: 1, opacity: locIdx === locs.length - 1 ? 0.2 : 1 }}
                    disabled={locIdx === locs.length - 1 || reordering}
                    onClick={(e) => { e.stopPropagation(); moveLocation(brand, s.id, 1); }}>↓</button>
                </div>
                <div style={{ flex: 1 }}>
                  <div className="mono" style={{ fontSize: 13 }}>{s.city || "—"}{s.area ? `, ${s.area}` : ""}</div>
                  {s.hours && <div className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", marginTop: 2 }}>{s.hours}</div>}
                  {s.note && <div className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", fontStyle: "italic", marginTop: 2 }}>{s.note}</div>}
                  {s.maps_url && (
                    <a href={s.maps_url} target="_blank" rel="noopener noreferrer" className="lnk" style={{ fontSize: 10, marginTop: 4, display: "inline-block" }} onClick={(e) => e.stopPropagation()}>
                      Maps →
                    </a>
                  )}
                </div>
                <button className="lnk" style={{ fontSize: 10, flexShrink: 0 }} onClick={(e) => { e.stopPropagation(); openEdit(s); }}>Edit</button>
                <button className="lnk" style={{ fontSize: 10, flexShrink: 0, color: "#a23" }}
                  disabled={deleting === s.id} onClick={(e) => { e.stopPropagation(); del(s.id, s.brand); }}>
                  {deleting === s.id ? "…" : "Hapus"}
                </button>
              </div>
            ))}
          </div>
        );
      })}

      {!loading && stores.length === 0 && !form && (
        <div>
          <p className="mono" style={{ fontSize: 12, color: "var(--ink-mute)" }}>Belum ada toko di Supabase.</p>
          <p className="mono" style={{ fontSize: 11, color: "var(--ink-mute)", marginTop: 6, lineHeight: 1.7 }}>
            Jalankan <code>supabase/stores.sql</code> di Supabase SQL Editor dulu.<br />
            Setelah ada data di sini, <code>/api/stores</code> akan otomatis pakai Supabase (bukan Notion).
          </p>
        </div>
      )}
    </div>
  );
};

// ── Admin root ─────────────────────────────────────────────
const Admin = ({ navigate }) => {
  const _params = new URLSearchParams(window.location.search);
  const autoOrder = _params.get("order") || null;
  const autoPrint = _params.has("print");
  const [unlocked, setUnlocked] = React.useState(!!localStorage.getItem(PW_KEY));
  const [tab, setTab] = React.useState(_params.get("tab") || "stock");
  const [cTab, setCTab] = React.useState("kelola");

  if (!unlocked) return <AdminPasswordGate onUnlock={() => setUnlocked(true)} />;

  const logout = () => { localStorage.removeItem(PW_KEY); window.location.reload(); };

  const tabStyle = (active) => ({
    fontFamily: "var(--mono)", fontSize: 11, letterSpacing: ".12em", textTransform: "uppercase",
    padding: "12px 20px", borderBottom: active ? "2px solid var(--ink)" : "2px solid transparent",
    color: active ? "var(--ink)" : "var(--ink-mute)", cursor: "pointer",
  });

  const subTabStyle = (active) => ({
    fontFamily: "var(--mono)", fontSize: 10, letterSpacing: ".12em", textTransform: "uppercase",
    padding: "8px 16px", borderBottom: active ? "2px solid var(--ink)" : "2px solid transparent",
    color: active ? "var(--ink)" : "var(--ink-mute)", cursor: "pointer",
  });

  return (
    <div style={{ minHeight: "100vh" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "20px var(--pad-x)", borderBottom: "1px solid var(--hair)" }}>
        <span style={{ fontFamily: "var(--serif)", fontSize: 22 }}>Admin</span>
        <button className="lnk" style={{ fontSize: 11 }} onClick={logout}>Logout</button>
      </div>

      <div style={{ display: "flex", borderBottom: "1px solid var(--hair)", padding: "0 var(--pad-x)" }}>
        <button style={tabStyle(tab === "stock")} onClick={() => setTab("stock")}>Stock</button>
        <button style={tabStyle(tab === "orders")} onClick={() => setTab("orders")}>Orders</button>
        <button style={tabStyle(tab === "consignment")} onClick={() => setTab("consignment")}>Konsinyasi</button>
        <button style={tabStyle(tab === "toko")} onClick={() => setTab("toko")}>Toko</button>
      </div>

      {tab === "stock" && <StockTab />}
      {tab === "orders" && <OrdersTab autoOrder={autoOrder} autoPrint={autoPrint} />}
      {tab === "toko" && <TokoTab />}

      {tab === "consignment" && (
        <div>
          <div style={{ display: "flex", padding: "0 var(--pad-x)", borderBottom: "1px solid var(--hair)" }}>
            <button style={subTabStyle(cTab === "kelola")} onClick={() => setCTab("kelola")}>Kelola</button>
            <button style={subTabStyle(cTab === "recap")} onClick={() => setCTab("recap")}>Recap</button>
          </div>
          {cTab === "kelola" && <ConsignmentMgmt />}
          {cTab === "recap" && (
            <div style={{ padding: "0 var(--pad-x)" }}>
              <Consignment navigate={navigate} embedded />
            </div>
          )}
        </div>
      )}
    </div>
  );
};

window.Admin = Admin;
