// Rapprochement encaissement ↔ facture — maquette P7-3 (fiche docs/fiches/p7-3-rapprochement.md).
// Suggestion par règle versionnée + CONFIRMATION HUMAINE toujours — aucune liaison silencieuse.
// Confirmer / ignorer / lier = écriture directe (aucun mouvement d'argent → pas de commande).
// Liaison manuelle = vue interne (pas de Dialog imbriqué) avec répartition 1 encaissement → N factures.
// window.OryzeShared.RapprochementPanel
(function () {
  const { Amount, Tag, Button, StatusBadge, EmptyState, Skeleton, Input } = window.OryzeDesignSystem_78c60a;
  const Ic = window.OryzeIcons;

  const RULE = "Règle rapprochement-v1.0 · calculé le 15/04/2026 07:30";
  const mono = { fontFamily: "var(--font-mono)" };
  const kick = { ...mono, fontSize: 10.5, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--text-muted)" };
  const cardBox = { border: "var(--border-thin) solid var(--line-keyline)", borderRadius: "var(--radius-lg)", background: "var(--surface-card)", padding: "14px 16px" };
  const fmt = (n) => n.toLocaleString("fr-FR", { minimumFractionDigits: 2 }) + " €";

  // Suggestions démo — côté facture DÉRIVÉ du dataset réel (window.OryzeInvoices +
  // OryzeInvoiceCalc) : mêmes réfs, mêmes TTC, mêmes statuts que la liste Factures.
  const invRef = (id) => {
    const f = window.OryzeInvoices.find((x) => x.id === id);
    return { id: f.id, who: f.who, badge: f.status === "late" ? "overdue" : "sent", due: (f.due || "").slice(0, 5), ttc: Math.round(window.OryzeInvoiceCalc(f).ttc * 100) / 100 };
  };
  // strength: strong (montant exact) | weak (à vérifier).
  const SUGGESTIONS = [
    { id: "s1", strength: "strong", criteria: "montant exact · référence dans le libellé · date ±3 j",
      pay: { label: "Virement reçu — ATELIER RIVOLI", date: "14/04", ref: "VIR RIVOLI FAC2046", amount: invRef("FAC-2046").ttc },
      inv: invRef("FAC-2046") },
    { id: "s2", strength: "strong", criteria: "montant exact · nom client",
      pay: { label: "Virement reçu — BOULANGERIE LOU", date: "11/04", ref: "LOU IDENTITE VISUELLE", amount: invRef("FAC-2044").ttc },
      inv: invRef("FAC-2044") },
    { id: "s3", strength: "weak", criteria: "nom client · montant partiel (56 %)",
      pay: { label: "Virement reçu — MARTIN AND CO", date: "15/04", ref: "ACOMPTE MARTIN", amount: 800 },
      inv: invRef("FAC-2047") },
  ];
  const ORPHANS = [
    { id: "o1", label: "Virement reçu — SNC RIVET", date: "13/04", ref: "RIVET SOLDE", amount: 584 },
  ];
  // Candidates pour la liaison manuelle : TOUTES les factures ouvertes (due/late)
  // du dataset — les montants dérivent du même calc, donc pas de contradiction.
  // Celles déjà proposées par une suggestion en attente restent visibles, marquées
  // « Suggestion en attente » ; seule la facture de la carte d'origine est exclue.
  const CANDIDATES = (excludeId) => window.OryzeInvoices
    .filter((f) => (f.status === "due" || f.status === "late") && f.id !== excludeId)
    .map((f) => invRef(f.id));

  function PaySide({ pay }) {
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 10, flex: 1, minWidth: 0 }}>
        <span style={{ width: 34, height: 34, borderRadius: "var(--radius-md)", background: "var(--oryze-mint)", border: "var(--border-thin) solid var(--line-keyline)", display: "inline-flex", alignItems: "center", justifyContent: "center", color: "var(--oryze-mint-ink)", flex: "none" }}>
          <Ic.arrowDownRight size={16} />
        </span>
        <div style={{ minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{pay.label}</div>
          <div style={{ ...mono, fontSize: 11, color: "var(--text-subtle)", marginTop: 2 }}>{pay.date} · « {pay.ref} »</div>
        </div>
        <Amount value={pay.amount} tone="positive" sign size="sm" style={{ marginLeft: "auto", flex: "none" }} />
      </div>
    );
  }

  function InvSide({ inv }) {
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 10, flex: 1, minWidth: 0 }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <span style={{ ...mono, fontSize: 12.5, fontWeight: 600 }}>{inv.id}</span>
            <StatusBadge size="sm" status={inv.badge} />
          </div>
          <div style={{ fontSize: 12, color: "var(--text-muted)", marginTop: 2 }}>{inv.who} · échéance {inv.due}</div>
        </div>
        <Amount value={inv.ttc} size="sm" style={{ marginLeft: "auto", flex: "none" }} />
      </div>
    );
  }

  function SuggestionCard({ s, onConfirm, onIgnore, onManual }) {
    const partial = s.pay.amount < s.inv.ttc;
    const reste = Math.round((s.inv.ttc - s.pay.amount) * 100) / 100;
    return (
      <div style={cardBox}>
        <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
          <PaySide pay={s.pay} />
          <span style={{ color: "var(--text-subtle)", flex: "none" }}><Ic.link size={16} /></span>
          <InvSide inv={s.inv} />
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 11, paddingTop: 10, borderTop: "1px solid var(--line-divider)", flexWrap: "wrap" }}>
          {s.strength === "strong"
            ? <Tag tone="mint" dot>Correspondance forte</Tag>
            : <Tag tone="butter" dot>À vérifier</Tag>}
          <span style={{ ...mono, fontSize: 10.5, color: "var(--text-subtle)", flex: 1, minWidth: 180 }}>{RULE} · {s.criteria}</span>
          <span style={{ display: "inline-flex", gap: 8, flex: "none" }}>
            <Button size="sm" variant="ghost" onClick={() => onIgnore(s)}>Ignorer</Button>
            <Button size="sm" variant="ghost" onClick={() => onManual({ ...s.pay, id: s.id, invId: s.inv.id })}>Lier manuellement</Button>
            <Button size="sm" onClick={() => onConfirm(s)}>{partial ? "Confirmer (partiel)" : "Confirmer"}</Button>
          </span>
        </div>
        {partial && (
          <div style={{ ...mono, fontSize: 11, color: "var(--oryze-butter-ink)", background: "var(--oryze-butter)", border: "var(--border-thin) solid var(--line-keyline)", borderRadius: "var(--radius-sm)", padding: "5px 9px", marginTop: 9 }}>
            Couvre {fmt(s.pay.amount)} sur {fmt(s.inv.ttc)} — la facture passera en « Paiement partiel », reste dû {fmt(reste)}.
          </div>
        )}
      </div>
    );
  }

  // ---- Liaison manuelle : répartition 1 encaissement → N factures ----
  function ManualLink({ pay, excludeId = null, pendingIds = [], onBack, onDone }) {
    const [q, setQ] = React.useState("");
    const [alloc, setAlloc] = React.useState({}); // id -> montant string FR
    const cands = CANDIDATES(excludeId).filter((c) => (c.who + " " + c.id).toLowerCase().includes(q.toLowerCase()));
    const num = (v) => { const n = parseFloat(String(v).replace(/\s/g, "").replace(",", ".")); return Number.isNaN(n) ? 0 : n; };
    const total = Object.values(alloc).reduce((a, v) => a + num(v), 0);
    const over = total > pay.amount + 0.001;
    const nSel = Object.keys(alloc).length;
    const resteAAllouer = Math.max(0, Math.round((pay.amount - total) * 100) / 100);

    function toggle(c) {
      setAlloc((a) => {
        const n = { ...a };
        if (n[c.id] !== undefined) { delete n[c.id]; return n; }
        const def = Math.min(c.ttc, Math.max(0, pay.amount - Object.values(n).reduce((x, v) => x + num(v), 0)));
        n[c.id] = def.toLocaleString("fr-FR", { minimumFractionDigits: 2 });
        return n;
      });
    }

    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <Button size="sm" variant="ghost" iconLeft={<Ic.chevronLeft size={15} />} onClick={onBack}>Retour</Button>
          <div style={{ fontSize: 13.5, fontWeight: 600, flex: 1 }}>Lier « {pay.label} »</div>
          <Amount value={pay.amount} tone="positive" sign size="sm" />
        </div>
        <Input placeholder="Rechercher une facture ou un client…" value={q} onChange={(e) => setQ(e.target.value)} iconLeft={<Ic.search size={16} />} />
        <div style={{ border: "var(--border-thin) solid var(--line-keyline)", borderRadius: "var(--radius-lg)", overflow: "hidden", background: "var(--surface-card)" }}>
          {cands.map((c, i) => {
            const sel = alloc[c.id] !== undefined;
            return (
              <div key={c.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", borderBottom: i === cands.length - 1 ? "none" : "1px solid var(--line-divider)", background: sel ? "var(--surface-sunken)" : "transparent" }}>
                <span onClick={() => toggle(c)} style={{ width: 18, height: 18, borderRadius: 5, border: "var(--border-thin) solid var(--line-keyline)", background: sel ? "var(--oryze-majorelle)" : "var(--surface-card)", display: "inline-flex", alignItems: "center", justifyContent: "center", color: "#fff", cursor: "pointer", flex: "none" }}>
                  {sel && <Ic.check size={12} />}
                </span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <span style={{ ...mono, fontSize: 12.5, fontWeight: 600 }}>{c.id}</span>
                  <span style={{ fontSize: 12.5, color: "var(--text-muted)", marginLeft: 8 }}>{c.who}</span>
                </div>
                <StatusBadge size="sm" status={c.badge} />
                {pendingIds.includes(c.id) && <Tag tone="butter" mono>Suggestion en attente</Tag>}
                <Amount value={c.ttc} size="sm" tone="muted" />
                {sel && (
                  <Input aria-label={"Montant alloué à " + c.id} value={alloc[c.id]} mono suffix="€" style={{ width: 130, flex: "none" }}
                    onChange={(e) => setAlloc((a) => ({ ...a, [c.id]: e.target.value }))} />
                )}
              </div>
            );
          })}
          {cands.length === 0 && <div style={{ padding: 20, textAlign: "center", fontSize: 13, color: "var(--text-muted)" }}>Aucune facture ouverte ne correspond.</div>}
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <span style={{ ...mono, fontSize: 11.5, color: over ? "var(--fin-negative)" : "var(--text-muted)", flex: 1 }}>
            Alloué : {fmt(Math.round(total * 100) / 100)} / {fmt(pay.amount)}{!over && resteAAllouer > 0 && nSel > 0 ? " · excédent " + fmt(resteAAllouer) + " signalé — jamais d'avoir automatique" : ""}
          </span>
          <Button size="sm" disabled={nSel === 0 || over || total <= 0} onClick={() => onDone(nSel, Object.keys(alloc))}>Confirmer la liaison</Button>
        </div>
        {over && <div style={{ fontSize: 12.5, color: "var(--fin-negative)", fontWeight: 600 }}>La répartition dépasse l'encaissement — ajustez les montants.</div>}
      </div>
    );
  }

  function RapprochementPanel({ onCountChange }) {
    const [loading, setLoading] = React.useState(true);
    const [items, setItems] = React.useState(SUGGESTIONS);
    const [orphans, setOrphans] = React.useState(ORPHANS);
    const [ignored, setIgnored] = React.useState([]);
    const [showIgnored, setShowIgnored] = React.useState(false);
    const [manual, setManual] = React.useState(null); // encaissement en cours de liaison
    const [toast, setToast] = React.useState(null);
    React.useEffect(() => { const t = setTimeout(() => setLoading(false), 650); return () => clearTimeout(t); }, []);
    const count = items.length + orphans.length;
    React.useEffect(() => { onCountChange && onCountChange(count); }, [count]);

    function say(msg) { setToast(msg); setTimeout(() => setToast(null), 2600); }
    function confirm(s) {
      setItems((l) => l.filter((x) => x.id !== s.id));
      const partial = s.pay.amount < s.inv.ttc;
      say(partial
        ? s.inv.id + " — paiement partiel · reste dû " + fmt(Math.round((s.inv.ttc - s.pay.amount) * 100) / 100)
        : s.inv.id + " marquée payée");
    }
    function ignore(s) { setItems((l) => l.filter((x) => x.id !== s.id)); setIgnored((l) => [...l, s]); }
    function restore(s) { setIgnored((l) => l.filter((x) => x.id !== s.id)); setItems((l) => [...l, s]); }
    function manualDone(n, linkedIds) {
      setOrphans((l) => l.filter((x) => x.id !== manual.id));
      // La liaison consomme aussi les suggestions en attente sur les factures liées.
      setItems((l) => l.filter((x) => x.id !== manual.id && !linkedIds.includes(x.inv.id)));
      say(n > 1 ? "Encaissement réparti sur " + n + " factures" : "Encaissement lié");
      setManual(null);
    }

    if (loading) {
      return (
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          {[0, 1, 2].map((i) => (
            <div key={i} style={cardBox}>
              <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                <Skeleton variant="circle" size={34} />
                <Skeleton variant="text" width={180} />
                <Skeleton variant="text" width={120} style={{ marginLeft: "auto" }} />
              </div>
              <Skeleton variant="text" width={260} style={{ marginTop: 12 }} />
            </div>
          ))}
        </div>
      );
    }

    if (manual) return <ManualLink pay={manual} excludeId={manual.invId || null} pendingIds={items.map((s) => s.inv.id)} onBack={() => setManual(null)} onDone={manualDone} />;

    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 14, position: "relative" }}>
        {count === 0 ? (
          <EmptyState framed title="Rien à rapprocher"
            description="Tous vos encaissements sont reliés à une facture — les statuts sont à jour." />
        ) : (
          <React.Fragment>
            {items.length > 0 && (
              <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
                <div style={kick}>Suggestions — vous confirmez, rien n'est lié automatiquement</div>
                {items.map((s) => <SuggestionCard key={s.id} s={s} onConfirm={confirm} onIgnore={ignore} onManual={setManual} />)}
              </div>
            )}
            {orphans.length > 0 && (
              <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
                <div style={kick}>Encaissements sans correspondance</div>
                {orphans.map((o) => (
                  <div key={o.id} style={{ ...cardBox, display: "flex", alignItems: "center", gap: 12 }}>
                    <PaySide pay={o} />
                    <Button size="sm" variant="secondary" onClick={() => setManual(o)} style={{ flex: "none" }}>Lier manuellement</Button>
                  </div>
                ))}
              </div>
            )}
          </React.Fragment>
        )}
        {ignored.length > 0 && (
          <div>
            <Button size="sm" variant="ghost" onClick={() => setShowIgnored((v) => !v)}>
              {showIgnored ? "Masquer les suggestions ignorées" : "Suggestions ignorées (" + ignored.length + ")"}
            </Button>
            {showIgnored && ignored.map((s) => (
              <div key={s.id} style={{ ...cardBox, display: "flex", alignItems: "center", gap: 12, marginTop: 8, opacity: 0.75 }}>
                <PaySide pay={s.pay} />
                <span style={{ ...mono, fontSize: 11.5, color: "var(--text-muted)" }}>↔ {s.inv.id}</span>
                <Button size="sm" variant="ghost" onClick={() => restore(s)} style={{ flex: "none" }}>Rétablir</Button>
              </div>
            ))}
          </div>
        )}
        {toast && (
          <div style={{ position: "sticky", bottom: 0, display: "flex", alignItems: "center", gap: 9, padding: "10px 13px", background: "var(--oryze-ink)", color: "var(--oryze-paper)", borderRadius: "var(--radius-md)", fontSize: 13, fontWeight: 600, boxShadow: "var(--shadow-md)" }}>
            <Ic.check size={15} /> {toast}
          </div>
        )}
      </div>
    );
  }

  window.OryzeShared = window.OryzeShared || {};
  window.OryzeShared.RapprochementPanel = RapprochementPanel;
})();
