// ─── Finanzas · Imputación de comprobantes a un recibo / pago ─────────────
// Permite asociar VARIOS comprobantes (facturas / notas de crédito / débito)
// a un recibo (cobro) o pago, e imputar un monto a cada uno, con la regla de
// negocio de letra fiscal única (A o B) por documento. Movida de screens-
// imputacion.jsx (P8) — "tomar tal cual" (mapa-comprension.md
// §Reutilización: "el patrón vivo... está bien resuelto"). MUERE
// `ComprobanteSearchModal` (mapa §Muertos: `setPicking(true)` no existe en
// todo el archivo — el modal nunca se monta, reemplazado por
// ComprobanteCombobox) junto con el estado `picking`/`setPicking` que solo
// él consumía. Expone a window: ImputacionCard, ComprobanteCombobox. Consume
// window.FACTURAS/NOTAS_CREDITO/NOTAS_DEBITO/PAGOS/RECIBOS, MicosStore,
// DocCard (ui/doc-kit-shell.jsx), formatMoney/formatDate (dominio/formato.js).

// Prefijo `imp*` (namespace, no cripticismo): estos top-level conviven en un
// único scope global compartido por TODOS los <script type="text/babel"> del
// bundle (Babel standalone no envuelve en módulo) — nombres genéricos como
// "money"/"fecha" colisionarían con constantes de mismo nombre en otro
// paquete cargado en paralelo. Antes `_impMoney`/`_impDate`/`_impPad4`/
// `_impFirstLetter` (mismo criterio, solo se les saca el "_").
const impMoney = (n) => (window.formatMoney ? formatMoney(n) : ('$' + (n || 0)));
const impFecha = (d) => (window.formatDate ? formatDate(d) : (d || ''));
const impPad4 = (n) => String(n == null ? '' : n).padStart(4, '0');
// Letra fiscal (A/B/C) de un comprobante a partir de un código/numero tipo "A-0001-…".
const impPrimeraLetra = (s) => { const m = String(s || '').trim().match(/^([ABC])/i); return m ? m[1].toUpperCase() : ''; };

// Comprobantes elegibles según el lado (cobro = emitidos / pago = recibidos).
function comprobantesDisponibles(lado) {
  const out = [];
  if (lado === 'cobro') {
    // El serializer de facturas expone el cliente como `f.cliente` (objeto {razonSocial,…}, ver
    // src/lib/api/serializers/facturas.ts) — NO como `f.entidad`. Leer `f.entidad` dejaba el
    // cliente en '' y la factura NO se podía buscar por nombre del cliente en el combobox (ni
    // etiquetarla con él). Se mantiene el fallback a `f.entidad` por si alguna vista vieja lo trae.
    (window.FACTURAS || []).forEach((f) => out.push({ kind: 'factura', id: f.id, tipo: f.tipoFactura || '', numero: `${f.tipoFactura}-${impPad4(f.puntoVenta)}-${f.numero}`, fecha: f.fechaEmision, total: f.total || 0, label: 'Factura ' + f.tipoFactura, cliente: (f.cliente && f.cliente.razonSocial) || (f.entidad && f.entidad.razonSocial) || '' }));
    (window.NOTAS_CREDITO || []).filter((n) => n.lado === 'cobro').forEach((n) => out.push({ kind: 'nc', id: n.id, tipo: n.tipo || '', numero: `${n.tipo}-${n.puntoVenta}-${n.numero}`, fecha: n.fechaEmision, total: n.total || 0, label: 'Nota de crédito ' + n.tipo, cliente: (n.cliente && n.cliente.razonSocial) || '' }));
    (window.NOTAS_DEBITO || []).filter((n) => n.lado === 'cobro').forEach((n) => out.push({ kind: 'nd', id: n.id, tipo: n.tipo || '', numero: `${n.tipo}-${n.puntoVenta}-${n.numero}`, fecha: n.fechaEmision, total: n.total || 0, label: 'Nota de débito ' + n.tipo, cliente: (n.cliente && n.cliente.razonSocial) || '' }));
  } else {
    (window.PAGOS || []).filter((p) => p.facturaRecibida).forEach((p) => { const t = (p.facturasRecibidasDocs && p.facturasRecibidasDocs[0] && p.facturasRecibidasDocs[0].tipoFactura) || impPrimeraLetra(p.facturaRecibida.codigo); out.push({ kind: 'facturaRecibida', id: p.id, tipo: t, numero: (p.facturaRecibida.codigo || p.publicId), fecha: p.facturaRecibida.fecha || p.fecha, total: p.total || 0, label: 'Factura recibida' + (t ? ' ' + t : ''), cliente: p.proveedor || '' }); });
    (window.NOTAS_CREDITO || []).filter((n) => n.lado === 'pago').forEach((n) => out.push({ kind: 'nc', id: n.id, tipo: n.tipo || '', numero: `${n.tipo}-${n.puntoVenta}-${n.numero}`, fecha: n.fechaEmision, total: n.total || 0, label: 'Nota de crédito ' + n.tipo, cliente: n.proveedor || (n.cliente && n.cliente.razonSocial) || '' }));
    (window.NOTAS_DEBITO || []).filter((n) => n.lado === 'pago').forEach((n) => out.push({ kind: 'nd', id: n.id, tipo: n.tipo || '', numero: `${n.tipo}-${n.puntoVenta}-${n.numero}`, fecha: n.fechaEmision, total: n.total || 0, label: 'Nota de débito ' + n.tipo, cliente: n.proveedor || (n.cliente && n.cliente.razonSocial) || '' }));
  }
  return out;
}

function badgeTipoComprobante(kind) {
  const map = { factura: 'FA', facturaRecibida: 'FR', nc: 'NC', nd: 'ND' };
  return (
    <span className="inline-flex items-center justify-center font-mono font-extrabold rounded-md flex-shrink-0"
      style={{ width: 30, height: 22, fontSize: 10.5, background: 'color-mix(in oklab, var(--accent) 14%, transparent)', color: 'var(--accent)', border: '1px solid color-mix(in oklab, var(--accent) 28%, transparent)' }}>
      {map[kind] || '··'}
    </span>
  );
}

// ─── Combobox inline para asociar comprobantes (typeahead estilo Inventario) ──
// Input + popover en portal que sugiere los comprobantes existentes a cobrar/
// pagar mientras escribís. Al elegir uno, lo asocia vía onPick. Respeta lado +
// letra fijada (A/B) + excluidos.
// `onlyKinds` (opcional): restringe a esos tipos de comprobante (ej. `['factura']` para el
// selector de "Comprobante asociado" de una NC/ND, cuya FK `comprobante_asociado_id` apunta
// SOLO a `facturas`). Omitirlo preserva el comportamiento previo (todos los kinds del lado).
function ComprobanteCombobox({ lado, excludeKeys, lockedLetra, onlyKinds, onPick }) {
  const [q, setQ] = React.useState('');
  const [open, setOpen] = React.useState(false);
  const [rect, setRect] = React.useState(null);
  const inputRef = React.useRef(null);

  const all = comprobantesDisponibles(lado)
    .filter((c) => !onlyKinds || onlyKinds.includes(c.kind))
    .filter((c) => !excludeKeys.includes(c.kind + ':' + c.id))
    .filter((c) => !lockedLetra || !c.tipo || c.tipo === lockedLetra);
  const query = q.trim().toLowerCase();
  const matches = all
    .filter((c) => !query || (`${c.numero} ${c.label} ${c.cliente}`).toLowerCase().includes(query))
    .slice(0, 8);

  const place = () => { if (inputRef.current) setRect(inputRef.current.getBoundingClientRect()); };
  React.useEffect(() => {
    if (!open) return;
    place();
    // Al hacer scroll/resize RE-ANCLAMOS el popover al input (no lo cerramos). Cerrarlo en
    // scroll lo volvía inalcanzable cuando cae bajo el fold: el gesto de traer una fila a la
    // vista para clickearla dispara el scroll -> se cerraba antes del click y la asociación
    // en modo edición nunca persistía (hallazgo ALTA #10 "asociar en edición"). Reanclar es
    // además el comportamiento estándar de un dropdown (sigue pegado al input, como los select
    // nativos). El cierre por interacción real sigue vivo abajo (click afuera).
    const reflow = () => place();
    window.addEventListener('scroll', reflow, true);
    window.addEventListener('resize', reflow);
    const onDoc = (e) => {
      const inPop = e.target.closest && e.target.closest('.inv-pop');
      if (inputRef.current && !inputRef.current.contains(e.target) && !inPop) setOpen(false);
    };
    document.addEventListener('mousedown', onDoc);
    return () => {
      window.removeEventListener('scroll', reflow, true);
      window.removeEventListener('resize', reflow);
      document.removeEventListener('mousedown', onDoc);
    };
  }, [open]);

  const ph = lado === 'cobro' ? 'Buscá una factura o nota a cobrar…' : 'Buscá una factura o nota a pagar…';
  const head = lado === 'cobro' ? 'Comprobantes a cobrar · elegí uno' : 'Comprobantes a pagar · elegí uno';

  // Colocación consciente del viewport: si abajo del input no entra el popover (cae bajo el
  // fold), lo abrimos HACIA ARRIBA — así la primera fila queda SIEMPRE visible sin scroll y se
  // puede clickear (parte del fix del hallazgo ALTA #10: en el detalle del pago el input queda
  // bajo, el popover se salía de pantalla y no había forma de asociar). maxHeight se recorta al
  // espacio real disponible (el propio popover ya scrollea internamente si hay muchas filas).
  const GAP = 5;
  const estH = Math.min(320, 34 + matches.length * 52);
  const spaceBelow = (rect ? window.innerHeight - rect.bottom : 0) - GAP;
  const spaceAbove = (rect ? rect.top : 0) - GAP;
  const openUp = !!rect && spaceBelow < estH && spaceAbove > spaceBelow;
  const popStyle = openUp
    ? { position: 'fixed', bottom: window.innerHeight - rect.top + GAP, left: rect.left, width: Math.max(rect.width, 380), maxHeight: Math.max(140, spaceAbove), zIndex: 60 }
    : { position: 'fixed', top: (rect ? rect.bottom : 0) + GAP, left: (rect ? rect.left : 0), width: Math.max(rect ? rect.width : 0, 380), maxHeight: Math.max(140, spaceBelow), zIndex: 60 };
  const pop = (open && rect && matches.length > 0) ? ReactDOM.createPortal(
    <div className="inv-pop" style={popStyle}>
      <p className="inv-pop__head">{lockedLetra ? head + ' · tipo ' + lockedLetra : head}</p>
      {matches.map((c) => (
        <button key={c.kind + ':' + c.id} type="button" className="inv-pop__row"
          onMouseDown={(e) => { e.preventDefault(); onPick(c); setQ(''); setOpen(false); }}>
          <span className="inv-pop__sku">{c.numero}</span>
          <span className="inv-pop__desc">{c.label}{c.cliente ? ' · ' + c.cliente : ''}</span>
          <span className="inv-pop__meta">
            <span className="inv-pop__price">{impMoney(c.total)}</span>
            <span className="inv-pop__stock">{impFecha(c.fecha)}</span>
          </span>
        </button>
      ))}
    </div>, document.body) : null;

  return (
    <div style={{ position: 'relative' }}>
      <div className="relative">
        <IconSearch size={15} className="absolute left-3.5 top-1/2 -translate-y-1/2" style={{ color: 'var(--muted-foreground)' }} />
        <input ref={inputRef} value={q}
          onChange={(e) => { setQ(e.target.value); if (!open) setOpen(true); }}
          onFocus={() => setOpen(true)}
          placeholder={ph}
          className="w-full h-11 pl-10 pr-3 text-[13px] font-medium rounded-xl outline-none"
          style={{ background: 'color-mix(in oklab, var(--surface) 50%, transparent)', border: '1px solid color-mix(in oklab, var(--border) 45%, transparent)', color: 'var(--foreground)' }} />
      </div>
      {pop}
    </div>
  );
}

// `collection`/`recordId` → modo store (record existente, persiste directo).
// `value`/`onChange` → modo borrador (alta: las imputaciones viven en el draft
// del doc y se guardan junto con el recibo/pago). En ambos casos `lado` define
// la fuente (cobro = emitidos · pago = recibidos).
function ImputacionCard({ collection, recordId, lado, total, value, onChange, editing }) {
  const draftMode = typeof onChange === 'function';
  const isEditing = editing != null ? editing : draftMode;
  const [, force] = React.useState(0);
  const globalName = collection === 'recibos' ? 'RECIBOS' : 'PAGOS';
  const rec = draftMode ? null : (window[globalName] || []).find((r) => r.id === recordId);
  const imps = draftMode
    ? (Array.isArray(value) ? value : [])
    : ((rec && Array.isArray(rec.imputaciones)) ? rec.imputaciones : []);

  const persist = (next) => {
    if (draftMode) { onChange(next); return; }
    if (rec) rec.imputaciones = next;
    if (window.MicosStore) MicosStore.update(collection, recordId, { imputaciones: next });
    force((x) => x + 1);
  };
  const add = (c) => { persist([...imps, { kind: c.kind, id: c.id, tipo: c.tipo || '', numero: c.numero, label: c.label, total: c.total, imputado: c.total, fecha: c.fecha }]); };
  const setAmount = (i, v) => persist(imps.map((it, j) => (j === i ? { ...it, imputado: Number(v) || 0 } : it)));
  const removeImp = (i) => persist(imps.filter((_, j) => j !== i));

  const totalImputado = imps.reduce((s, it) => s + (Number(it.imputado) || 0), 0);
  const docTotal = Number(total) || 0;
  const diferencia = docTotal - totalImputado;
  const excludeKeys = imps.map((it) => it.kind + ':' + it.id);
  // Letra fijada por el primer comprobante asociado (todos deben coincidir).
  const lockedLetra = (imps.find((it) => it.tipo) || {}).tipo || '';
  const verbo = lado === 'cobro' ? 'cobro' : 'pago';

  return (
    <DocCard>
      <div className="flex flex-wrap items-start justify-between gap-3 mb-5">
        <div className="min-w-0">
          <p className="text-[11px] font-bold uppercase tracking-[0.2em]" style={{ color: 'var(--accent)' }}>Comprobantes imputados</p>
          <p className="text-[15px] font-extrabold mt-1.5" style={{ color: 'var(--foreground)' }}>Facturas y notas que cancela este {lado === 'cobro' ? 'recibo' : 'pago'}</p>
          {isEditing && <p className="text-[12.5px] mt-1" style={{ color: 'var(--muted-foreground)' }}>Asociá uno o varios comprobantes e imputá cuánto del {verbo} aplica a cada uno.</p>}
        </div>
        <p className="text-[11.5px] font-bold uppercase tracking-[0.1em] flex items-center gap-2" style={{ color: 'var(--muted-foreground)' }}>
          {lockedLetra && <span className="inline-flex items-center justify-center font-mono font-extrabold rounded-md" style={{ width: 22, height: 20, fontSize: 11, background: 'color-mix(in oklab, var(--accent) 14%, transparent)', color: 'var(--accent)', border: '1px solid color-mix(in oklab, var(--accent) 26%, transparent)' }}>{lockedLetra}</span>}
          <span>{imps.length} {imps.length === 1 ? 'comprobante' : 'comprobantes'}</span>
        </p>
      </div>

      {imps.length > 0 && (
        <div className="space-y-2 mb-4">
          {imps.map((it, i) => isEditing ? (
            <div key={it.kind + ':' + it.id} className="flex items-center gap-3 px-4 py-3 rounded-xl"
              style={{ background: 'color-mix(in oklab, var(--surface) 50%, transparent)', border: '1px solid color-mix(in oklab, var(--border) 45%, transparent)' }}>
              {badgeTipoComprobante(it.kind)}
              <div className="flex-1 min-w-0">
                <p className="font-mono text-[12.5px] font-bold tracking-wide truncate" style={{ color: 'var(--foreground)' }}>{it.numero}</p>
                <p className="text-[11px] truncate" style={{ color: 'var(--muted-foreground)' }}>{it.label} · total {impMoney(it.total)}</p>
              </div>
              <div className="flex items-center gap-1.5 flex-shrink-0">
                <span className="text-[11px] font-semibold" style={{ color: 'var(--muted-foreground)' }}>Imputado</span>
                <div className="relative">
                  <span className="absolute left-2.5 top-1/2 -translate-y-1/2 text-[12px] font-bold pointer-events-none" style={{ color: 'var(--muted-foreground)' }}>$</span>
                  <input className="input tabular-nums font-mono h-9" type="number" inputMode="decimal" value={it.imputado == null ? '' : it.imputado}
                    onChange={(e) => setAmount(i, e.target.value)} style={{ width: 130, paddingLeft: 20 }} />
                </div>
              </div>
              <button type="button" onClick={() => removeImp(i)} title="Quitar" className="inline-flex items-center justify-center rounded-md cursor-pointer flex-shrink-0"
                style={{ width: 32, height: 32, color: 'var(--muted-foreground)' }}
                onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--danger)'; e.currentTarget.style.background = 'rgba(220,38,38,0.10)'; }}
                onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--muted-foreground)'; e.currentTarget.style.background = 'transparent'; }}>
                <IconX size={14} />
              </button>
            </div>
          ) : (
            <div key={it.kind + ':' + it.id} className="w-full flex items-center gap-4 rounded-xl px-4 py-3.5"
              style={{ background: 'color-mix(in oklab, var(--surface) 40%, transparent)', border: '1px solid color-mix(in oklab, var(--border) 40%, transparent)' }}>
              <span className="inline-flex items-center justify-center rounded-lg flex-shrink-0" style={{ width: 38, height: 38, background: 'color-mix(in oklab, var(--accent) 10%, transparent)', color: 'var(--accent)' }}>
                <IconFileText size={17} />
              </span>
              <div className="flex-1 min-w-0">
                <p className="text-[11.5px] font-mono font-bold tracking-wide uppercase" style={{ color: 'var(--accent)' }}>{it.numero}</p>
                <p className="text-[13px] font-bold mt-0.5" style={{ color: 'var(--foreground)' }}>{it.label}</p>
                <p className="text-[10.5px] font-bold uppercase tracking-[0.08em] mt-1" style={{ color: 'var(--muted-foreground)' }}>
                  {it.fecha ? 'Emitido el ' + impFecha(it.fecha) + ' · ' : ''}{impMoney(it.imputado)}
                </p>
              </div>
            </div>
          ))}
        </div>
      )}

      {imps.length === 0 && !isEditing && (
        <p className="text-[12.5px] font-medium py-2" style={{ color: 'var(--muted-foreground)' }}>Sin comprobantes imputados.</p>
      )}

      {isEditing && <ComprobanteCombobox lado={lado} excludeKeys={excludeKeys} lockedLetra={lockedLetra} onPick={add} />}

      {docTotal > 0 && (
        <div className="mt-5 pt-5 grid grid-cols-3 gap-4" style={{ borderTop: '1px solid color-mix(in oklab, var(--border) 55%, transparent)' }}>
          <div>
            <p className="text-[10.5px] font-bold uppercase tracking-[0.18em]" style={{ color: 'var(--muted-foreground)' }}>Total del {lado === 'cobro' ? 'recibo' : 'pago'}</p>
            <p className="text-[15px] font-extrabold tabular-nums mt-1" style={{ color: 'var(--foreground)' }}>{impMoney(docTotal)}</p>
          </div>
          <div>
            <p className="text-[10.5px] font-bold uppercase tracking-[0.18em]" style={{ color: 'var(--muted-foreground)' }}>Imputado</p>
            <p className="text-[15px] font-extrabold tabular-nums mt-1" style={{ color: totalImputado > 0 ? 'var(--success)' : 'var(--muted-foreground)' }}>{impMoney(totalImputado)}</p>
          </div>
          <div className="text-right">
            <p className="text-[10.5px] font-bold uppercase tracking-[0.18em]" style={{ color: 'var(--muted-foreground)' }}>{diferencia >= 0 ? 'A cuenta' : 'Excedido'}</p>
            <p className="text-[15px] font-extrabold tabular-nums mt-1" style={{ color: Math.abs(diferencia) < 1 ? 'var(--success)' : (diferencia < 0 ? 'var(--danger)' : 'var(--foreground)') }}>{impMoney(Math.abs(diferencia))}</p>
          </div>
        </div>
      )}
    </DocCard>
  );
}

Object.assign(window, { ImputacionCard, ComprobanteCombobox });
