/* =============================================================
   CoreIQ Office — Duplicate review
   The consolidation queue (mirrors Pack review): product records that
   look like the SAME trade item, for a human to review + merge.
   - 'Barcode match' (green): live products sharing a canonical barcode
     — safe, high confidence.
   - 'Possible match' (amber): same-category products with very similar
     names but no shared barcode — the tail the barcode-first receiver
     can't catch; review the details before merging.
   Merge is reversible (audited in merge_records); stock is summed and
   both invoices stay reconcilable. 'Not a duplicate' dismisses it.
   No-fake-data: every candidate + number comes from /duplicates.
   ============================================================= */
const { useState: dupUseState, useEffect: dupUseEffect } = React;
const dupMoney = (c) => (c == null ? "—" : "$" + (Number(c) / 100).toLocaleString("en-AU", { minimumFractionDigits: 2, maximumFractionDigits: 2 }));

function DuplicateReviewScreen() {
  const { pushToast, activeUser } = window.useOffice();
  const [groups, setGroups] = dupUseState(null); // null = loading
  const [err, setErr] = dupUseState(null);
  const [busy, setBusy] = dupUseState(null); // pairKey being acted on
  const [keepSel, setKeepSel] = dupUseState({}); // pairKey -> productId to keep

  const performedBy =
    (activeUser && (activeUser.email || [activeUser.first, activeUser.last].filter(Boolean).join(" "))) || "office-user";

  const load = () => {
    setGroups(null);
    setErr(null);
    setBusy(null);
    window.OfficeAPI.duplicatesList()
      .then((g) => {
        const list = g || [];
        setGroups(list);
        const sel = {};
        list.forEach((grp) => {
          // Default keep = the cheaper record (retire the pricier duplicate); operator can change it.
          const sorted = [...grp.products].sort((a, b) => (a.sellPriceCents ?? 1e12) - (b.sellPriceCents ?? 1e12));
          sel[grp.pairKey] = sorted[0].id;
        });
        setKeepSel(sel);
      })
      .catch((e) => setErr((e && e.message) || String(e)));
  };
  dupUseEffect(load, []);

  const doMerge = (grp) => {
    const keepId = keepSel[grp.pairKey];
    const keep = grp.products.find((p) => p.id === keepId);
    const others = grp.products.filter((p) => p.id !== keepId);
    if (!keep || !others.length) return;
    if (!window.confirm(`Merge ${others.length} record(s) into "${keep.name}" (${dupMoney(keep.sellPriceCents)})?\n\nStock is summed, both invoices stay reconcilable, and this is reversible.`)) return;
    setBusy(grp.pairKey);
    const force = grp.confidence === "name"; // name matches don't share a barcode → force
    others
      .reduce((chain, o) => chain.then(() => window.OfficeAPI.mergeProducts(keep.id, o.id, performedBy, force)), Promise.resolve())
      .then(() => {
        pushToast({ kind: "paid", icon: "check-circle", title: "Merged into one product", meta: keep.name });
        load();
      })
      .catch((e) => {
        pushToast({ kind: "void", icon: "alert", title: "Merge failed", meta: String((e && e.message) || e) });
        setBusy(null);
      });
  };

  const doDismiss = (grp) => {
    setBusy(grp.pairKey);
    window.OfficeAPI.dismissDuplicate(grp.pairKey, performedBy, null)
      .then(() => {
        pushToast({ kind: "paid", icon: "check", title: "Marked not a duplicate" });
        load();
      })
      .catch((e) => {
        pushToast({ kind: "void", icon: "alert", title: "Couldn't dismiss", meta: String((e && e.message) || e) });
        setBusy(null);
      });
  };

  const openInvoice = (invoiceId) => {
    window.OfficeAPI.supplierInvoicePdf(invoiceId)
      .then((blob) => window.open(URL.createObjectURL(blob), "_blank"))
      .catch((e) => pushToast({ kind: "void", icon: "alert", title: "Couldn't open invoice", meta: String((e && e.message) || e) }));
  };

  // Decisive heuristic: records from the SAME supplier with DIFFERENT reorder
  // codes are almost always different products (a wholesaler gives one code per
  // product) — flag it so the operator leans to "Not a duplicate".
  const sameSupplierDiffCode = (grp) => {
    const suppliers = new Set(grp.products.map((p) => (p.supplierName || p.provider || "").toLowerCase()).filter(Boolean));
    const codes = grp.products.map((p) => p.supplierCode).filter(Boolean);
    return suppliers.size === 1 && new Set(codes).size === grp.products.length && codes.length === grp.products.length;
  };

  return (
    <div className="off-screen">
      <header className="off-screen-header">
        <div className="off-screen-header-meta">
          <span className="off-screen-eyebrow">Catalogue &amp; stock · live from Aurora</span>
          <h2 className="off-screen-title">Duplicate review</h2>
        </div>
        <div className="off-screen-actions">
          <button className="btn btn--ghost btn--sm" onClick={load} disabled={groups === null}>
            <window.OIcon name="refresh" /> Refresh
          </button>
        </div>
      </header>

      <div className="off-screen-body">
        <p style={{ margin: "0 0 16px", fontSize: 13, color: "var(--text-subtle)", maxWidth: 760, lineHeight: 1.6 }}>
          Records that might be the same product. If it's the <strong>same item entered twice</strong>, merge them —
          stock is summed, every supplier code + invoice is kept, and it's reversible. If they're <strong>different
          products</strong> (the reorder codes usually tell you), keep both.
        </p>

        {err && (
          <div className="off-empty"><window.OIcon name="alert" /> Couldn't load duplicates: {err}</div>
        )}
        {groups === null && !err && (
          <div className="off-empty"><span className="spinner" /> Scanning the catalogue…</div>
        )}
        {groups && groups.length === 0 && !err && (
          <div className="off-empty">
            <div className="empty-icon"><window.OIcon name="check-circle" width={20} height={20} /></div>
            No duplicate products — the catalogue is clean.
          </div>
        )}

        {groups && groups.map((grp) => {
          const isName = grp.confidence === "name";
          const likelyDifferent = sameSupplierDiffCode(grp);
          const keepBothBtn = (
            <button className={"btn btn--sm " + (likelyDifferent ? "btn--primary" : "btn--ghost")}
              onClick={() => doDismiss(grp)} disabled={busy === grp.pairKey}>
              <window.OIcon name="check" /> Keep both — different products
            </button>
          );
          const mergeBtn = (
            <button className={"btn btn--sm " + (likelyDifferent ? "btn--ghost" : "btn--primary")}
              onClick={() => doMerge(grp)} disabled={busy === grp.pairKey}>
              {busy === grp.pairKey ? <span className="spinner" /> : <window.OIcon name="box" />} Merge — same product (keep ticked)
            </button>
          );
          return (
            <div className="card" key={grp.pairKey} style={{ marginBottom: 14, padding: 16 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
                <span className="chip" style={{
                  background: isName ? "var(--hold-bg)" : "var(--paid-bg)",
                  color: isName ? "var(--hold-text)" : "var(--paid-text)",
                  fontWeight: 600, padding: "3px 10px", borderRadius: 999, fontSize: 12,
                }}>
                  <window.OIcon name={isName ? "alert" : "barcode"} /> {isName ? "Possible match" : "Barcode match"}
                </span>
                {isName && <span style={{ fontSize: 12, color: "var(--muted)" }}>No shared barcode — check the invoices.</span>}
              </div>

              {likelyDifferent && (
                <div style={{
                  display: "flex", alignItems: "center", gap: 8, marginBottom: 12, padding: "8px 10px",
                  background: "var(--hold-bg)", color: "var(--hold-text)", borderRadius: 8, fontSize: 12,
                }}>
                  <window.OIcon name="alert" /> Same supplier, different reorder codes — usually <strong>different products</strong> (variants/sizes), not a duplicate. Lean to “Keep both”.
                </div>
              )}

              <div style={{ display: "grid", gridTemplateColumns: `repeat(${grp.products.length}, minmax(0,1fr))`, gap: 12 }}>
                {grp.products.map((p) => {
                  const keep = keepSel[grp.pairKey] === p.id;
                  return (
                    <label key={p.id} style={{
                      border: `1.5px solid ${keep ? "var(--navy-600)" : "var(--border)"}`,
                      borderRadius: 10, padding: 12, cursor: "pointer",
                      background: keep ? "var(--bg-subtle)" : "var(--surface)",
                    }}>
                      <div style={{ fontWeight: 600, fontSize: 14, marginBottom: 6 }}>{p.name}</div>
                      <div style={{ fontSize: 12, color: "var(--muted)", lineHeight: 1.7 }}>
                        <div>Price: <strong style={{ color: "var(--ink)" }}>{dupMoney(p.sellPriceCents)}</strong>{p.size ? ` · ${p.size}` : ""}</div>
                        <div>On hand: <strong style={{ color: "var(--ink)" }}>{p.onHand}</strong></div>
                        <div>Supplier: {p.supplierName || "—"}{p.provider ? ` (${p.provider})` : ""}</div>
                        <div>Reorder code: <strong style={{ color: "var(--ink)" }}>{p.supplierCode || "—"}</strong></div>
                        <div>Barcodes: {p.barcodes.length ? p.barcodes.join(", ") : "none"}</div>
                        {p.invoices && p.invoices.length ? (
                          <div style={{ marginTop: 4 }}>
                            Invoiced as:
                            {p.invoices.map((inv, i) => (
                              <div key={i} style={{ fontSize: 11, marginTop: 2 }}>
                                “{inv.lineName || p.name}” · qty {inv.qty} @ {dupMoney(inv.unitCostCents)}{" · "}
                                <a href="#" title="Open the source invoice PDF"
                                  onClick={(e) => { e.preventDefault(); e.stopPropagation(); openInvoice(inv.invoiceId); }}
                                  style={{ color: "var(--navy-600)", fontWeight: 600 }}>
                                  Inv {inv.number || "—"}{inv.date ? ` (${String(inv.date).slice(0, 10)})` : ""} ↗
                                </a>
                              </div>
                            ))}
                          </div>
                        ) : (
                          <div>Invoices: none</div>
                        )}
                      </div>
                      <div style={{ marginTop: 8, display: "flex", alignItems: "center", gap: 6, fontSize: 12 }}>
                        <input type="radio" name={"keep-" + grp.pairKey} checked={keep}
                          onChange={() => setKeepSel((s) => ({ ...s, [grp.pairKey]: p.id }))} />
                        <span style={{ color: keep ? "var(--ink)" : "var(--muted)" }}>Keep this one if merged</span>
                      </div>
                    </label>
                  );
                })}
              </div>

              <div style={{ display: "flex", gap: 8, marginTop: 14, alignItems: "center", flexWrap: "wrap" }}>
                {likelyDifferent ? <>{keepBothBtn}{mergeBtn}</> : <>{mergeBtn}{keepBothBtn}</>}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, { duplicateReview: DuplicateReviewScreen });
