// ─── Finanzas · lista: dataset (lado × tipo de documento) ─────────────────
// Separado de lista.jsx (P8: MUERE el bloque muerto de screens-finanzas.jsx,
// mapa-comprension.md §finanzas) por tema — no por tamaño: esto es la
// PROYECCIÓN fila-cruda→shape-de-tabla (qué columnas/chips/mappers le
// corresponden a cada combinación Cobros/Pagos × Facturas/NC/ND/Recibos/
// Pagos), lista.jsx es la PANTALLA (render + paginación + KPIs) que lo
// consume. "Tomar tal cual" (mapa §Reutilización): la lógica no cambió, solo
// de archivo. Expone a window: buildFinDataset, FIN_DOCTABS, FIN_PAGINABLE,
// NEW_DOC_LABELS, finPagConfig (antes _finPagConfig, sin "_" ya en su propio
// archivo). Consume window.FACTURAS/RECIBOS/PAGOS/NOTAS_CREDITO/
// NOTAS_DEBITO/ENTIDADES/EXPEDIENTES/COBRO_ESTADOS/PAGO_ESTADOS (data-
// finanzas.js) y TablePill/IdDateCell (ui/entity-list.jsx).

// ─── 2da tanda de Finanzas: selectores por tipo de documento ──────
// Cada tab principal (Cobros/Pagos) tiene su tanda de tipos de documento.
// La tabla pasa a listar UN documento por fila (la combinación lado × tipo).
const FIN_DOCTABS = {
  cobros: [
    { key: 'facturas', label: 'Facturas (A/B)',          short: 'facturas' },
    { key: 'nc',       label: 'Notas de Crédito (A/B)',  short: 'N. de crédito' },
    { key: 'nd',       label: 'Notas de Débito (A/B)',   short: 'N. de débito' },
    { key: 'recibos',  label: 'Recibos',                 short: 'recibos' },
  ],
  pagos: [
    { key: 'facturas', label: 'Facturas (A/B)',          short: 'facturas' },
    { key: 'nc',       label: 'Notas de Crédito (A/B)',  short: 'N. de crédito' },
    { key: 'nd',       label: 'Notas de Débito (A/B)',   short: 'N. de débito' },
    { key: 'pagos',    label: 'Pagos',                   short: 'pagos' },
  ],
};

// Etiqueta del botón de alta según el tipo de documento en pantalla. El
// sustantivo NO cambia entre cobros/pagos (una factura es una factura); lo que
// cambia es el creador (emitida por nosotros vs recibida), que define el lado.
const NEW_DOC_LABELS = {
  facturas: 'Nueva factura',
  nc: 'Nueva nota de crédito',
  nd: 'Nueva nota de débito',
  recibos: 'Nuevo recibo',
  pagos: 'Nuevo pago',
};
function _finList(name) { return Array.isArray(window[name]) ? window[name] : []; }
function _finEnt(id) { const e = _finList('ENTIDADES').find(x => x.id === id); return e ? e.razonSocial : ''; }
function _finExp(id) { return _finList('EXPEDIENTES').find(x => x.id === id) || null; }
function _finExpPub(id) { const e = _finExp(id); return e ? e.publicId : ''; }
function _finFacCliente(f) {
  return _finEnt(f.entidadId)
    || (() => { const e = _finExp(f.expedienteId); return e && e.entidad ? e.entidad.razonSocial : ''; })()
    || '—';
}
// Estado de cobro REAL para facturas/notas (facturas/notas_credito/notas_debito no
// tienen columna `estado`: antes se fabricaba con un hash del id — _finHash/
// _finFacEstado, BUGS-caza-01-07-2026.md #1/A1). Fuente de verdad: `f.imputado`,
// que el backend calcula como SUM(imputacion.monto_imputado) vivas por comprobante
// (src/app/api/facturas + notas-credito + notas-debito, route.ts + [id]/route.ts).
// `doneLabel` distingue COBRADO (facturas y notas del lado cobro) de PAGADO (notas
// del lado pago, mismas claves que PAGO_ESTADOS).
function _finEstadoFromImputado(imputado, total, doneLabel) {
  const imp = Number(imputado) || 0;
  const tot = Number(total) || 0;
  if (imp <= 0) return 'PENDIENTE';
  if (imp < tot) return 'PARCIAL';
  return doneLabel;
}
// Imputación REAL para recibos/pagos. Recibos: el backend ya embebe `imputaciones`
// (array real, por comprobante, ver src/app/api/recibos/route.ts) — se suma acá. NO
// se compara contra `r.total` (columna `recibos.total` está en 0.00 para el 100% de
// los recibos en la DB — gap de datos aparte, no forma parte de A1/A5/M12): un
// recibo con al menos una imputación real registrada es IMPUTADO, sin ninguna es
// A_CUENTA (cobrado pero todavía no aplicado a un comprobante puntual — estado real
// de AR, no inventado). Pagos: la tabla `imputacion` hoy no tiene NINGUNA fila del
// lado pago_id (0 en la DB), así que sin datos reales queda A_CUENTA (honesto: "no
// hay imputación registrada"), nunca un hash aleatorio (antes _finHash/_finImput,
// BUGS-caza-01-07-2026.md #1/A1). Si el día de mañana se cablea imputación real de
// pagos, esta misma función ya la toma sin cambios. `i.imputado` sale crudo de
// `imputacion.monto_imputado` (src/app/api/recibos/route.ts, shapeImput): es
// NEGATIVO cuando el comprobante imputado es una nota de crédito (verificado en
// DB, convención de neteo) y POSITIVO cuando es una factura — se suma en valor
// absoluto por entrada para que un recibo imputado a una NC también cuente como
// IMPUTADO (participó de una imputación real), no A_CUENTA por el signo.
function _finImput(r) {
  const imputado = (Array.isArray(r.imputaciones) ? r.imputaciones : [])
    .reduce((s, i) => s + Math.abs(Number(i.imputado) || 0), 0);
  return imputado > 0.5 ? 'IMPUTADO' : 'A_CUENTA';
}

function _finTipoBadge(tipo) {
  return (
    <span className="inline-flex items-center justify-center font-mono font-extrabold rounded-md flex-shrink-0"
      style={{ width: 22, height: 22, fontSize: 11,
        background: 'color-mix(in oklab, var(--accent) 14%, transparent)',
        color: 'var(--accent)', border: '1px solid color-mix(in oklab, var(--accent) 30%, transparent)' }}>
      {tipo}
    </span>
  );
}

// Fábricas de columnas (compartidas entre tipos de documento).
const _colCompTipo = (label) => ({ key: 'comp', label, width: 'minmax(190px, 1.2fr)',
  render: r => (
    <div className="flex items-center gap-2.5 min-w-0">
      {_finTipoBadge(r.tipo)}
      <div className="min-w-0">
        <p className="font-mono text-[12.5px] font-bold tracking-wide truncate" style={{ color: 'var(--foreground)' }}>{r.publicId}</p>
        <p className="text-[11px] font-medium" style={{ color: 'var(--muted-foreground)' }}>{formatDate(r.fecha)}</p>
      </div>
    </div>
  ) });
const _colCliente = (label) => ({ key: 'cliente', label, width: 'minmax(190px, 1.3fr)',
  render: r => <p className="text-[13px] font-bold truncate" style={{ color: 'var(--foreground)' }}>{r.cliente || '—'}</p> });
const _colRef = (label) => ({ key: 'ref', label, width: 'minmax(160px, 1fr)',
  render: r => r.ref
    ? <span className="font-mono text-[12px] font-bold tracking-wide uppercase" style={{ color: 'var(--accent)' }}>{r.ref}</span>
    : <span className="text-[12px]" style={{ color: 'color-mix(in oklab, var(--muted-foreground) 55%, transparent)', fontStyle: 'italic' }}>—</span> });
const _colTotal = { key: 'total', label: 'Total', width: 'minmax(140px, 0.9fr)',
  render: r => <span className="text-[14px] font-extrabold tabular-nums" style={{ color: 'var(--foreground)' }}>{formatMoney(r.total)}</span> };
const _colEstado = (estados) => ({ key: 'estado', label: 'Estado', width: 'minmax(120px, 0.8fr)',
  render: r => { const cfg = (estados && estados[r.estado]) || { label: r.estado, tone: 'neutral' }; return <TablePill label={cfg.label} tone={cfg.tone} />; } });
const _colImput = { key: 'imput', label: 'Imputación', width: 'minmax(130px, 0.8fr)',
  render: r => <TablePill label={r.imput === 'IMPUTADO' ? 'Imputado' : 'A cuenta'} tone={r.imput === 'IMPUTADO' ? 'pos' : 'warn'} /> };
const _colIdDate = { key: 'idd', label: 'ID · Fecha', width: 'minmax(170px, 1.1fr)',
  render: r => <IdDateCell id={r.publicId} date={r.fecha} /> };

function _finEstadoChips(isCobro) {
  return [
    { key: 'todos', label: 'Todos', pred: () => true },
    { key: 'pend',  label: isCobro ? 'A Cobrar' : 'A Pagar', pred: r => r.estado === 'PENDIENTE' || r.estado === 'PARCIAL' },
    { key: 'ok',    label: isCobro ? 'Cobrados' : 'Pagados', pred: r => r.estado === (isCobro ? 'COBRADO' : 'PAGADO') },
  ];
}
function _finImputChips() {
  return [
    { key: 'todos', label: 'Todos', pred: () => true },
    { key: 'imp',   label: 'Imputados', pred: r => r.imput === 'IMPUTADO' },
    { key: 'cta',   label: 'A cuenta', pred: r => r.imput === 'A_CUENTA' },
  ];
}

// ─── Mappers fila cruda → shape unificado de Finanzas ─────────────
// Reusados TAMBIÉN sobre las filas que llegan paginadas server-side (pgRows,
// ver FIN_PAGINABLE más abajo), sin duplicar la lógica de campos entre el
// modo memoria y el paginado.
function _finMapFacturaCobro(f) {
  return {
    _kind: 'factura', _src: f, id: f.id, publicId: f.publicId, fecha: f.fechaEmision,
    tipo: f.tipoFactura, cliente: _finFacCliente(f), total: f.total,
    estado: _finEstadoFromImputado(f.imputado, f.total, 'COBRADO'), ref: _finExpPub(f.expedienteId),
  };
}
function _finMapFacturaRecibida(p) {
  return {
    _kind: 'facturaRecibida', _src: p, id: p.id, publicId: (p.facturaRecibida && p.facturaRecibida.codigo) || p.publicId,
    fecha: (p.facturaRecibida && p.facturaRecibida.fecha) || p.fecha, tipo: '·',
    cliente: p.proveedor, total: p.total, estado: p.estado, ref: p.compraNumero || '',
  };
}
function _finMapNota(n, isCobro) {
  return {
    _kind: 'nota', _src: n, id: n.id, publicId: n.publicId, fecha: n.fechaEmision, tipo: n.tipo,
    cliente: isCobro ? (n.cliente && n.cliente.razonSocial) : (n.proveedor || (n.cliente && n.cliente.razonSocial)),
    total: n.total, estado: _finEstadoFromImputado(n.imputado, n.total, isCobro ? 'COBRADO' : 'PAGADO'), ref: n.comprobanteAsociado || '',
  };
}
function _finMapRecibo(r) {
  const e = _finExp(r.expedienteId);
  return {
    _kind: 'recibo', _src: r, id: r.id, publicId: r.publicId, fecha: r.fechaCobro || r.fecha,
    cliente: (e && e.entidad && e.entidad.razonSocial) || (r.cliente && r.cliente.razonSocial) || '',
    total: r.total || r.importe || 0, imput: _finImput(r),
  };
}
function _finMapPago(p) {
  return {
    _kind: 'pago', _src: p, id: p.id, publicId: p.publicId, fecha: p.fecha,
    cliente: p.proveedor, total: p.total, imput: _finImput(p),
  };
}

// Construye { rows, columns, chips, search } para un lado + tipo de documento.
// `rows` sigue armándose sobre el array COMPLETO en memoria (window.<COLL>,
// hidratado hasta BIG_LIMIT) — se usa tal cual para cualquier (lado,docType)
// que NO esté en FIN_PAGINABLE (modo memoria de siempre) y como fallback si el
// fetch paginado falla. columns/chips/search se usan SIEMPRE (paginado o no).
function buildFinDataset(lado, docType) {
  const isCobro = lado === 'cobros';
  const estados = isCobro ? window.COBRO_ESTADOS : window.PAGO_ESTADOS;
  const estadoChips = _finEstadoChips(isCobro);
  const impChips = _finImputChips();

  if (docType === 'facturas' && isCobro) {
    const rows = _finList('FACTURAS').map(_finMapFacturaCobro);
    return { rows, columns: [_colIdDate, _colCliente('Entidad'), _colTotal, _colEstado(estados)], chips: estadoChips, search: ['publicId', 'cliente', 'ref'] };
  }
  if (docType === 'facturas') {
    const rows = _finList('PAGOS').filter(p => p.facturaRecibida).map(_finMapFacturaRecibida);
    return { rows, columns: [_colIdDate, _colCliente('Proveedor'), _colRef('Compra'), _colTotal, _colEstado(estados)], chips: estadoChips, search: ['publicId', 'cliente', 'ref'] };
  }
  if (docType === 'nc' || docType === 'nd') {
    const coll = _finList(docType === 'nc' ? 'NOTAS_CREDITO' : 'NOTAS_DEBITO').filter(n => n.lado === (isCobro ? 'cobro' : 'pago'));
    const rows = coll.map(n => _finMapNota(n, isCobro));
    return { rows, columns: [_colIdDate, _colCliente(isCobro ? 'Entidad' : 'Proveedor'), _colRef('Comp. asociado'), _colTotal, _colEstado(estados)], chips: estadoChips, search: ['publicId', 'cliente', 'ref'] };
  }
  if (docType === 'recibos') {
    const rows = _finList('RECIBOS').map(_finMapRecibo);
    return { rows, columns: [_colIdDate, _colCliente('Entidad'), _colTotal, _colImput], chips: impChips, search: ['publicId', 'cliente'] };
  }
  // pagos (instrumentos)
  const rows = _finList('PAGOS').map(_finMapPago);
  return { rows, columns: [_colIdDate, _colCliente('Proveedor'), _colTotal, _colImput], chips: impChips, search: ['publicId', 'cliente'] };
}

// ─── Paginación server-side: (lado,docType) → colección única del store ───
// buildFinDataset arriba mapea cada selección a UNA colección subyacente (o a
// un filtro sobre PAGOS/NOTAS_*, ver comentarios de cada rama). De esas, sólo
// estas 3 mapean a una colección SIN filtro adicional que la REST API no
// soporte como query param → pueden paginar server-side de verdad (fetchPage
// con offset/limit reales + X-Total-Count). Las que necesitan un filtro que
// el backend no expone (facturas recibidas = PAGOS con `facturaRecibida`
// truthy; NC/ND = filtro por columna `lado`) quedan en el modo memoria de
// siempre — COMPAT explícita del encargo (no se rompen, quedan documentadas).
// Son además colecciones chicas hoy (recibos 41, notas_credito 41,
// notas_debito 7, todas < 1000 = cap de PostgREST sin `limit`), mientras que
// la prioridad real del encargo (facturas 855 rotas por BIG_LIMIT=500, pagos
// 480 y creciendo) queda resuelta con estas 3.
const FIN_PAGINABLE = {
  'cobros:facturas': { collection: 'facturas', map: _finMapFacturaCobro },
  'cobros:recibos':  { collection: 'recibos',  map: _finMapRecibo },
  'pagos:pagos':     { collection: 'pagos',    map: _finMapPago },
};
function finPagConfig(lado, docType) {
  return FIN_PAGINABLE[`${lado}:${docType}`] || null;
}

Object.assign(window, { buildFinDataset, FIN_DOCTABS, FIN_PAGINABLE, NEW_DOC_LABELS, finPagConfig });
