// LOLA Consola Web — FINANZAS: Reportes · Facturación (FEL) · Conciliación bancaria.
// Todo contra la API real (/admin/reportes, /admin/facturacion, /admin/conciliacion); nada se
// guarda en el navegador. Usa las piezas compartidas Signature (KpiCard hero, Panel, Modal, GOLD).

const fApi = () => window.LOLA.api;
const FQ = (n) => 'Q' + Number(n || 0).toLocaleString('es-GT', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const FQk = (n) => (Math.abs(n) >= 1e6 ? 'Q' + (n / 1e6).toFixed(1) + ' M' : Math.abs(n) >= 1e4 ? 'Q' + (n / 1e3).toFixed(1) + ' k' : FQ(n));
const FD = (x) => (x ? new Date(x).toLocaleDateString('es-GT', { day: '2-digit', month: 'short', year: 'numeric' }) : '—');
// En el teléfono las filas tipo tabla se apilan en una columna; las tablas se desplazan de lado.
const fCols = (d) => (typeof window !== 'undefined' && window.innerWidth < 760 ? '1fr' : d);
const mesActual = () => { const d = new Date(); return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0'); };
const isoDia = (d) => new Date(d.getTime() - d.getTimezoneOffset() * 60000).toISOString().slice(0, 10);

function FBtn({ kind = 'ghost', children, style, ...p }) {
  const t = window.WC;
  const k = { primary: { background: t.brand, color: '#fff', borderColor: t.brand }, gold: window.GOLD, danger: { background: '#fff', color: t.danger, borderColor: '#F2C4C0' }, ghost: { background: '#fff', color: t.text, borderColor: t.border } }[kind];
  return <button {...p} style={{ height: 38, padding: '0 14px', borderRadius: 10, border: '1px solid', fontFamily: t.font, fontSize: 13.5, fontWeight: 700, cursor: p.disabled ? 'not-allowed' : 'pointer', opacity: p.disabled ? 0.55 : 1, display: 'inline-flex', alignItems: 'center', gap: 7, whiteSpace: 'nowrap', ...k, ...style }}>{children}</button>;
}
function FSeg({ value, onChange, options }) {
  const t = window.WC;
  return (
    <div style={{ display: 'inline-flex', gap: 3, background: '#fff', border: `1px solid ${t.border}`, borderRadius: 11, padding: 3, flexWrap: 'wrap' }}>
      {options.map(([k, l]) => <button key={k} onClick={() => onChange(k)} style={{ height: 32, padding: '0 12px', borderRadius: 8, border: 'none', cursor: 'pointer', fontFamily: t.font, fontSize: 13, fontWeight: 700, background: value === k ? '#0C1826' : 'transparent', color: value === k ? '#fff' : t.sec }}>{l}</button>)}
    </div>
  );
}
function FChip({ tone = 'neutral', children }) {
  const t = window.WC;
  const c = { ok: '#1F8A5B', warn: '#B7791F', bad: t.danger, info: t.brand, neutral: t.sec, gold: '#7A5F00' }[tone];
  return <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11.5, fontWeight: 700, color: c, background: c + '16', padding: '3px 9px', borderRadius: 999, whiteSpace: 'nowrap' }}><span style={{ width: 6, height: 6, borderRadius: 999, background: c }} />{children}</span>;
}
const fInp = (t) => ({ height: 38, padding: '0 11px', borderRadius: 9, border: `1px solid ${t.border}`, fontFamily: t.font, fontSize: 13.5, color: t.text, background: '#fff', outline: 'none', boxSizing: 'border-box' });
const fLbl = (t) => ({ display: 'block', fontSize: 11, fontWeight: 700, color: t.ter, fontFamily: t.mono, textTransform: 'uppercase', letterSpacing: 0.7, marginBottom: 5 });
async function fDescargar(url, nombre) { try { await fApi().descargar(url, nombre); } catch (e) { alert(e.message); } }

// ════════════════════════════════ REPORTES ════════════════════════════════
function ReportesView() {
  const t = window.WC, I = window.WI;
  const hoy = new Date();
  const [preset, setPreset] = React.useState('30');
  const [desde, setDesde] = React.useState(isoDia(new Date(hoy.getTime() - 29 * 864e5)));
  const [hasta, setHasta] = React.useState(isoDia(hoy));
  const [agrupar, setAgrupar] = React.useState('');
  const aplicarPreset = (k) => {
    setPreset(k);
    const h = new Date(); let d;
    if (k === 'mes') d = new Date(h.getFullYear(), h.getMonth(), 1);
    else if (k === 'anio') d = new Date(h.getFullYear(), 0, 1);
    else d = new Date(h.getTime() - (Number(k) - 1) * 864e5);
    setDesde(isoDia(d)); setHasta(isoDia(h));
  };
  const q = window.useAsync(() => fApi().reportes({ desde, hasta, agrupar }), [desde, hasta, agrupar]);
  const d = q.data, k = (d && d.kpis) || {};
  const dl = (v) => (v == null ? null : (v > 0 ? '+' : '') + v + '%');
  const colores = ['#1D57A9', '#F4C71E', '#1F8A5B', '#7A5AF8', '#0E9CA6', '#D98A0B', '#C0392B', '#5B6470'];
  const exp = (tipo) => fDescargar(fApi().reporteExportUrl({ tipo, desde, hasta }), 'lola-' + tipo + '-' + desde + '_' + hasta + '.csv');
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
      <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'flex-end' }}>
        <FSeg value={preset} onChange={aplicarPreset} options={[['7', '7 días'], ['30', '30 días'], ['90', '90 días'], ['mes', 'Este mes'], ['anio', 'Este año']]} />
        <label><span style={fLbl(t)}>Desde</span><input type="date" value={desde} onChange={(e) => { setPreset(''); setDesde(e.target.value); }} style={fInp(t)} /></label>
        <label><span style={fLbl(t)}>Hasta</span><input type="date" value={hasta} onChange={(e) => { setPreset(''); setHasta(e.target.value); }} style={fInp(t)} /></label>
        <FSeg value={agrupar} onChange={setAgrupar} options={[['', 'Auto'], ['dia', 'Día'], ['semana', 'Semana'], ['mes', 'Mes']]} />
        <span style={{ flex: 1 }} />
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {[['pagos', 'Pagos'], ['servicios', 'Servicios'], ['movimientos', 'Libro mayor'], ['usuarios', 'Usuarios']].map(([k2, l]) => <FBtn key={k2} onClick={() => exp(k2)}><I.Download size={15} /> {l}</FBtn>)}
        </div>
      </div>
      {q.loading && !d ? <window.Loading label="Calculando reportes…" /> : q.error ? <window.ErrState error={q.error} onRetry={q.reload} /> : (
        <React.Fragment>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(190px, 1fr))', gap: 14 }}>
            <window.KpiCard hero label="Volumen movido (GMV)" value={FQk(k.gmv)} delta={dl(k.deltaGmv)} deltaLabel="vs. periodo anterior" deltaUp={(k.deltaGmv || 0) >= 0} icon="Trend" spark={(d.serie || []).map((s) => s.gmv)} />
            <window.KpiCard label="Comisión LOLA" value={FQk(k.comision)} delta={dl(k.deltaComision)} deltaLabel="vs. periodo anterior" deltaUp={(k.deltaComision || 0) >= 0} icon="Percent" accent="#7A5F00" />
            <window.KpiCard label="Servicios finalizados" value={String(k.servicios || 0)} delta={dl(k.deltaServicios)} deltaLabel="vs. periodo anterior" deltaUp={(k.deltaServicios || 0) >= 0} icon="Truck" />
            <window.KpiCard label="Ticket promedio" value={FQk(k.ticket)} icon="Tag" accent="#0E9CA6" />
            <window.KpiCard label="Take rate" value={(k.takeRate || 0) + '%'} icon="Grid" accent="#1F8A5B" />
            <window.KpiCard label="Comisión por cobrar" value={FQk(k.porCobrar)} note={k.deudores ? k.deudores + ' transportistas' : null} icon="Alert" accent={k.porCobrar > 0 ? '#C0392B' : '#1F8A5B'} />
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 420px), 1fr))', gap: 18 }}>
            <window.Panel title="Volumen y comisión" sub={'Agrupado por ' + ({ dia: 'día', semana: 'semana', mes: 'mes' })[d.rango.agrupar] + ' · ' + (k.cobroDirectoPct || 0) + '% cobrados directo al transportista'}>
              {(d.serie || []).length < 2 ? <div style={{ color: t.sec, fontSize: 13.5, padding: '40px 0', textAlign: 'center' }}>Aún no hay suficientes servicios finalizados en este rango.</div> : (
                <React.Fragment>
                  <window.AreaChart data={d.serie.map((s) => ({ l: s.l.slice(5), v: s.gmv }))} h={220} fmt={FQk} labelEvery={Math.ceil(d.serie.length / 8)} />
                  <window.BarChart data={d.serie.map((s) => ({ l: s.l.slice(5), v: s.comision }))} h={120} color="#F4C71E" fmt={FQk} />
                </React.Fragment>
              )}
            </window.Panel>
            <window.Panel title="Embudo de servicios" sub={`${k.ofertasPorServicio || 0} ofertas por servicio${k.minutosPrimeraOferta != null ? ' · primera oferta en ' + (k.minutosPrimeraOferta < 120 ? k.minutosPrimeraOferta + ' min' : Math.round(k.minutosPrimeraOferta / 60) + ' h') : ''}`}>
              {(d.embudo[0] || {}).v ? <window.Funnel data={d.embudo} /> : <div style={{ color: t.sec, fontSize: 13.5 }}>Sin servicios publicados en el rango.</div>}
            </window.Panel>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 320px), 1fr))', gap: 18 }}>
            <window.Panel title="Por tipo de carga" sub="Servicios publicados">
              {d.porCarga.length ? <window.DonutChart data={d.porCarga.map((c, i) => ({ l: c.l, v: c.v, c: colores[i % colores.length] }))} size={170} /> : <div style={{ color: t.sec, fontSize: 13.5 }}>Sin datos.</div>}
            </window.Panel>
            <window.Panel title="Top transportistas" sub="Por volumen movido">
              {d.topTransportistas.length ? <window.HBars data={d.topTransportistas} fmt={FQk} /> : <div style={{ color: t.sec, fontSize: 13.5 }}>Sin datos.</div>}
            </window.Panel>
            <window.Panel title="Top clientes" sub="Por volumen contratado">
              {d.topClientes.length ? <window.HBars data={d.topClientes} fmt={FQk} color="#0E9CA6" /> : <div style={{ color: t.sec, fontSize: 13.5 }}>Sin datos.</div>}
            </window.Panel>
            <window.Panel title="Origen de la demanda" sub="Departamento de recogida">
              {d.porDepto.length ? <window.HBars data={d.porDepto} fmt={(v) => v + ' serv.'} color="#7A5AF8" /> : <div style={{ color: t.sec, fontSize: 13.5 }}>Sin datos.</div>}
            </window.Panel>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 320px), 1fr))', gap: 18 }}>
            <window.Panel title="Tesorería del periodo" sub="Dinero que entró y salió por la billetera">
              {[['Recargas acreditadas', k.recargas, '#1F8A5B'], ['Retiros solicitados', k.retiros, t.danger], ['Comisión efectivamente cobrada', k.comisionCobrada, '#7A5F00']].map(([l, v, c]) => (
                <div key={l} style={{ display: 'flex', justifyContent: 'space-between', padding: '10px 0', borderBottom: `1px solid ${t.grid}`, fontSize: 14 }}><span style={{ color: t.sec }}>{l}</span><b style={{ color: c, fontFamily: t.mono }}>{FQ(v)}</b></div>
              ))}
              <div style={{ marginTop: 14, padding: 14, borderRadius: 12, background: '#0C1826', color: '#fff' }}>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: 0.8, textTransform: 'uppercase', color: '#F4C71E' }}>En custodia hoy</div>
                <div style={{ display: 'flex', gap: 20, marginTop: 6, flexWrap: 'wrap' }}>
                  <div><div style={{ fontSize: 20, fontWeight: 800 }}>{FQ(k.custodiaGanancias)}</div><div style={{ fontSize: 12, color: 'rgba(255,255,255,.65)' }}>Ganancias retirables</div></div>
                  <div><div style={{ fontSize: 20, fontWeight: 800 }}>{FQ(k.custodiaOperativo)}</div><div style={{ fontSize: 12, color: 'rgba(255,255,255,.65)' }}>Saldo operativo comprado</div></div>
                </div>
              </div>
            </window.Panel>
            <window.Panel title="Crecimiento" sub="Altas en el periodo">
              {[['Clientes nuevos', k.clientesNuevos], ['Transportistas nuevos', k.transportistasNuevos], ['Viajes de retorno publicados', Object.values(d.retornos || {}).reduce((s, r) => s + r.n, 0)], ['Retornos reservados', ((d.retornos || {}).reservado || {}).n || 0]].map(([l, v]) => (
                <div key={l} style={{ display: 'flex', justifyContent: 'space-between', padding: '10px 0', borderBottom: `1px solid ${t.grid}`, fontSize: 14 }}><span style={{ color: t.sec }}>{l}</span><b style={{ fontFamily: t.mono }}>{v || 0}</b></div>
              ))}
            </window.Panel>
          </div>
        </React.Fragment>
      )}
    </div>
  );
}

// ════════════════════════════════ FACTURACIÓN ════════════════════════════════
function FacturacionView() {
  const t = window.WC, I = window.WI;
  const [tab, setTab] = React.useState('resumen');
  const [mes, setMes] = React.useState(mesActual());
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
        <FSeg value={tab} onChange={setTab} options={[['resumen', 'Por facturar'], ['documentos', 'Documentos'], ['config', 'Datos fiscales y certificador']]} />
        <span style={{ flex: 1 }} />
        {tab !== 'config' && <label style={{ display: 'flex', alignItems: 'center', gap: 8 }}><span style={{ ...fLbl(t), marginBottom: 0 }}>Periodo</span><input type="month" value={mes} onChange={(e) => setMes(e.target.value)} style={fInp(t)} /></label>}
        {tab !== 'config' && <FBtn onClick={() => fDescargar(fApi().facLibroUrl(mes), 'libro-ventas-' + mes + '.csv')}><I.Download size={15} /> Libro de ventas</FBtn>}
      </div>
      {tab === 'resumen' && <FacPorFacturar mes={mes} irDocs={() => setTab('documentos')} irConfig={() => setTab('config')} />}
      {tab === 'documentos' && <FacDocumentos mes={mes} />}
      {tab === 'config' && <FacConfig />}
    </div>
  );
}

function FacPorFacturar({ mes, irDocs, irConfig }) {
  const t = window.WC, I = window.WI;
  const q = window.useAsync(() => fApi().facResumen(mes), [mes]);
  const [sel, setSel] = React.useState([]);
  const [busy, setBusy] = React.useState(false);
  const [ec, setEc] = React.useState(null);
  if (q.loading && !q.data) return <window.Loading />;
  if (q.error) return <window.ErrState error={q.error} onRetry={q.reload} />;
  const d = q.data;
  const total = d.porFacturar.reduce((s, p) => s + p.comision, 0);
  const generar = async (modo) => {
    setBusy(true);
    try { const r = await fApi().facGenerar({ mes, modo, usuarios: sel.length ? sel : undefined }); alert(r.creadas + ' borrador(es) creados. Revísalos en Documentos y emítelos.'); setSel([]); q.reload(); irDocs(); }
    catch (e) { alert(e.message); }
    setBusy(false);
  };
  return (
    <React.Fragment>
      {!d.configLista && (
        <div style={{ display: 'flex', gap: 12, alignItems: 'center', padding: '12px 16px', borderRadius: 12, background: '#FFF8E6', border: '1px solid #F4C71E88', fontSize: 13.5 }}>
          <I.Alert size={18} /> <span style={{ flex: 1 }}>Faltan los datos fiscales del emisor (NIT, razón social y dirección). Sin ellos no se pueden generar facturas.</span>
          <FBtn kind="gold" onClick={irConfig}>Completar datos</FBtn>
        </div>
      )}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 14 }}>
        <window.KpiCard hero label={'Por facturar · ' + mes} value={FQ(total)} note={d.porFacturar.length + ' transportistas'} icon="Doc" />
        <window.KpiCard label="Facturado (emitido)" value={FQ(d.emitidas.total)} note={d.emitidas.n + ' documentos'} icon="Check" accent="#1F8A5B" />
        <window.KpiCard label="IVA débito fiscal" value={FQ(d.emitidas.iva)} icon="Percent" accent="#7A5F00" />
        <window.KpiCard label="Comisión por cobrar" value={FQ(d.porCobrar.total)} note={d.porCobrar.n + ' con deuda'} icon="Alert" accent={d.porCobrar.total > 0 ? '#C0392B' : '#1F8A5B'} />
      </div>
      <window.Panel title="Comisiones por facturar" sub={'Comisiones de servicios liquidados en ' + mes + ' que aún no tienen factura · certificador: ' + (d.certificador.activo ? d.certificador.proveedor.toUpperCase() + ' (' + d.certificador.entorno + ')' : 'manual')} pad={0}
        action={<div style={{ display: 'flex', gap: 8 }}><FBtn disabled={busy || !d.porFacturar.length || !d.configLista} onClick={() => generar('por_servicio')}>Una por servicio</FBtn><FBtn kind="gold" disabled={busy || !d.porFacturar.length || !d.configLista} onClick={() => generar('mensual')}><I.Doc size={15} /> {busy ? 'Generando…' : sel.length ? 'Generar ' + sel.length + ' consolidadas' : 'Generar consolidadas del mes'}</FBtn></div>}>
        {d.porFacturar.length === 0 ? <div style={{ padding: 40, textAlign: 'center', color: t.sec }}>Todo lo de este periodo ya está facturado.</div> : (
          <div style={{ overflowX: 'auto', WebkitOverflowScrolling: 'touch' }}><table style={{ width: '100%', minWidth: 640, borderCollapse: 'collapse', fontSize: 13.5 }}>
            <thead><tr style={{ background: '#FAFBFC' }}>{['', 'Transportista', 'NIT', 'Servicios', 'Volumen', 'Comisión', ''].map((h, i) => <th key={i} style={{ textAlign: i >= 3 && i <= 5 ? 'right' : 'left', padding: '10px 16px', fontSize: 10.5, fontFamily: t.mono, color: t.ter, textTransform: 'uppercase', letterSpacing: 0.8, borderBottom: `1px solid ${t.border}` }}>{h}</th>)}</tr></thead>
            <tbody>{d.porFacturar.map((p) => { const on = sel.includes(p.usuarioId); return (
              <tr key={p.usuarioId} style={{ borderBottom: `1px solid ${t.grid}` }}>
                <td style={{ padding: '10px 16px', width: 30 }}><input type="checkbox" checked={on} onChange={() => setSel(on ? sel.filter((x) => x !== p.usuarioId) : [...sel, p.usuarioId])} /></td>
                <td style={{ padding: '10px 16px', fontWeight: 650 }}>{p.nombre}</td>
                <td style={{ padding: '10px 16px', fontFamily: t.mono }}>{p.nit === 'CF' ? <FChip tone="warn">CF · sin NIT</FChip> : p.nit}</td>
                <td style={{ padding: '10px 16px', textAlign: 'right', fontFamily: t.mono }}>{p.servicios}</td>
                <td style={{ padding: '10px 16px', textAlign: 'right', fontFamily: t.mono, color: t.sec }}>{FQ(p.gmv)}</td>
                <td style={{ padding: '10px 16px', textAlign: 'right', fontFamily: t.mono, fontWeight: 800 }}>{FQ(p.comision)}</td>
                <td style={{ padding: '10px 16px', textAlign: 'right' }}><button onClick={() => setEc(p)} style={{ background: 'none', border: 'none', color: t.brand, fontWeight: 700, cursor: 'pointer', fontFamily: t.font, fontSize: 13 }}>Estado de cuenta</button></td>
              </tr>
            ); })}</tbody>
          </table></div>
        )}
      </window.Panel>
      {ec && <EstadoCuentaModal usuario={ec} mes={mes} onClose={() => setEc(null)} />}
    </React.Fragment>
  );
}

function EstadoCuentaModal({ usuario, mes, onClose }) {
  const t = window.WC;
  const q = window.useAsync(() => fApi().facEstadoCuenta(usuario.usuarioId, mes), [usuario.usuarioId, mes]);
  return (
    <window.Modal title={'Estado de cuenta · ' + usuario.nombre} sub={'Periodo ' + mes + ' · NIT ' + usuario.nit} width={860} onClose={onClose} footer={<div style={{ display: 'flex', gap: 8 }}><FBtn onClick={() => window.print()}>Imprimir / PDF</FBtn><FBtn onClick={onClose}>Cerrar</FBtn></div>}>
      {q.loading ? <window.Loading /> : q.error ? <window.ErrState error={q.error} onRetry={q.reload} /> : (() => {
        const r = q.data.resumen;
        return (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: 10 }}>
              {[['Saldo operativo inicial', r.saldoOperativoInicial], ['Recargas', r.recargas], ['Comisiones cobradas', r.comisiones], ['Saldo operativo actual', r.saldoOperativoFinal], ['Ganancias inicial', r.gananciasInicial], ['Retiros', r.retiros], ['Comisión por cobrar', r.comisionPendiente], ['Ganancias actual', r.gananciasFinal]].map(([l, v]) => (
                <div key={l} style={{ padding: '10px 12px', borderRadius: 10, background: '#F6F7F9' }}><div style={{ fontSize: 10.5, color: t.ter, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.4 }}>{l}</div><div style={{ fontSize: 16, fontWeight: 800, fontFamily: t.mono, marginTop: 2, color: l.includes('por cobrar') && v > 0 ? t.danger : t.text }}>{FQ(v)}</div></div>
              ))}
            </div>
            <div style={{ border: `1px solid ${t.border}`, borderRadius: 12, overflow: 'hidden', maxHeight: 340, overflowY: 'auto' }}>
              {q.data.movimientos.length === 0 ? <div style={{ padding: 24, textAlign: 'center', color: t.sec }}>Sin movimientos en el periodo.</div> : q.data.movimientos.map((m, i) => (
                <div key={m.id} style={{ display: 'grid', gridTemplateColumns: fCols('110px 1fr 90px 100px 110px'), gap: 10, padding: '9px 14px', borderTop: i ? `1px solid ${t.grid}` : 'none', fontSize: 12.5, alignItems: 'center' }}>
                  <span style={{ fontFamily: t.mono, color: t.sec }}>{FD(m.createdAt)}</span>
                  <span style={{ minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}><b>{m.tipo.replace(/_/g, ' ')}</b> · {m.descripcion}</span>
                  <span style={{ color: t.ter }}>{m.cuenta}</span>
                  <FChip tone={m.estado === 'pendiente' ? 'warn' : ['rechazado', 'cancelado'].includes(m.estado) ? 'bad' : 'ok'}>{m.estado.replace('_', ' ')}</FChip>
                  <span style={{ fontFamily: t.mono, fontWeight: 800, textAlign: 'right', color: m.signo === '+' ? '#1F8A5B' : t.text }}>{m.signo}{FQ(m.monto)}</span>
                </div>
              ))}
            </div>
            {q.data.facturas.length > 0 && <div style={{ fontSize: 13, color: t.sec }}>Facturas del periodo: {q.data.facturas.map((f) => `${f.serieInterna}-${f.numeroInterno} (${f.estado}, ${FQ(f.total)})`).join(' · ')}</div>}
          </div>
        );
      })()}
    </window.Modal>
  );
}

function FacDocumentos({ mes }) {
  const t = window.WC, I = window.WI;
  const [estado, setEstado] = React.useState('');
  const [busca, setBusca] = React.useState('');
  const q = window.useAsync(() => fApi().facDocs({ mes, estado, q: busca }), [mes, estado, busca]);
  const [sel, setSel] = React.useState(null);
  const tone = { borrador: 'info', emitida: 'ok', anulada: 'neutral', error: 'bad' };
  return (
    <window.Panel title="Documentos" sub={'Facturas del periodo ' + mes} pad={0}
      action={<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}><input placeholder="Buscar nombre, NIT o autorización" value={busca} onChange={(e) => setBusca(e.target.value)} style={{ ...fInp(t), width: 240 }} /><FSeg value={estado} onChange={setEstado} options={[['', 'Todos'], ['borrador', 'Borradores'], ['emitida', 'Emitidas'], ['anulada', 'Anuladas'], ['error', 'Con error']]} /></div>}>
      {q.loading && !q.data ? <window.Loading /> : q.error ? <window.ErrState error={q.error} onRetry={q.reload} /> : q.data.items.length === 0 ? <div style={{ padding: 40, textAlign: 'center', color: t.sec }}>No hay documentos con estos filtros.</div> : (
        <div style={{ overflowX: 'auto', WebkitOverflowScrolling: 'touch' }}><table style={{ width: '100%', minWidth: 640, borderCollapse: 'collapse', fontSize: 13.5 }}>
          <thead><tr style={{ background: '#FAFBFC' }}>{['Número', 'Receptor', 'NIT', 'Base', 'IVA', 'Total', 'Estado', 'Autorización SAT'].map((h, i) => <th key={h} style={{ textAlign: i >= 3 && i <= 5 ? 'right' : 'left', padding: '10px 16px', fontSize: 10.5, fontFamily: t.mono, color: t.ter, textTransform: 'uppercase', letterSpacing: 0.8, borderBottom: `1px solid ${t.border}` }}>{h}</th>)}</tr></thead>
          <tbody>{q.data.items.map((f) => (
            <tr key={f.id} onClick={() => setSel(f.id)} style={{ borderBottom: `1px solid ${t.grid}`, cursor: 'pointer' }}>
              <td style={{ padding: '10px 16px', fontFamily: t.mono, fontWeight: 700 }}>{f.serieInterna}-{String(f.numeroInterno).padStart(5, '0')}</td>
              <td style={{ padding: '10px 16px', fontWeight: 600 }}>{f.receptor.nombre}</td>
              <td style={{ padding: '10px 16px', fontFamily: t.mono }}>{f.receptor.nit}</td>
              <td style={{ padding: '10px 16px', textAlign: 'right', fontFamily: t.mono, color: t.sec }}>{FQ(f.subtotal)}</td>
              <td style={{ padding: '10px 16px', textAlign: 'right', fontFamily: t.mono, color: t.sec }}>{FQ(f.iva)}</td>
              <td style={{ padding: '10px 16px', textAlign: 'right', fontFamily: t.mono, fontWeight: 800 }}>{FQ(f.total)}</td>
              <td style={{ padding: '10px 16px' }}><FChip tone={tone[f.estado]}>{f.estado}</FChip></td>
              <td style={{ padding: '10px 16px', fontFamily: t.mono, fontSize: 11.5, color: t.sec }}>{f.fel && f.fel.uuid ? f.fel.uuid.slice(0, 18) + '…' : '—'}</td>
            </tr>
          ))}</tbody>
        </table></div>
      )}
      {sel && <FacturaModal id={sel} onClose={() => setSel(null)} onChanged={() => { q.reload(); }} />}
    </window.Panel>
  );
}

function FacturaModal({ id, onClose, onChanged }) {
  const t = window.WC, I = window.WI;
  const q = window.useAsync(() => fApi().facDoc(id), [id]);
  const [man, setMan] = React.useState({ uuid: '', serie: '', numero: '' });
  const [rec, setRec] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const run = async (fn) => { setBusy(true); try { await fn(); q.reload(); onChanged(); } catch (e) { alert(e.message); } setBusy(false); };
  return (
    <window.Modal title="Factura" sub="Comisión de intermediación · FEL Guatemala" width={780} onClose={onClose}
      footer={q.data && (() => { const f = q.data.factura; return (
        <div style={{ display: 'flex', gap: 8, width: '100%', flexWrap: 'wrap' }}>
          <FBtn onClick={() => fDescargar(fApi().facXmlUrl(f.id), 'FEL-' + f.serieInterna + '-' + f.numeroInterno + '.xml')}><I.Download size={15} /> XML</FBtn>
          {['borrador', 'error'].includes(f.estado) && <FBtn kind="danger" disabled={busy} onClick={() => window.confirm('¿Borrar el borrador? Las comisiones vuelven a "por facturar".') && run(async () => { await fApi().facBorrar(f.id); onClose(); })}>Borrar</FBtn>}
          <span style={{ flex: 1 }} />
          {f.estado === 'emitida' && <FBtn kind="danger" disabled={busy} onClick={() => { const m = window.prompt('Motivo de anulación (queda registrado ante la SAT):'); if (m) run(() => fApi().facAnular(f.id, { motivo: m })); }}>Anular</FBtn>}
          {['borrador', 'error'].includes(f.estado) && <FBtn kind="gold" disabled={busy} onClick={() => run(() => fApi().facEmitir(f.id, man.uuid ? man : {}))}>{busy ? 'Emitiendo…' : 'Emitir factura'}</FBtn>}
          <FBtn onClick={onClose}>Cerrar</FBtn>
        </div>
      ); })()}>
      {q.loading ? <window.Loading /> : q.error ? <window.ErrState error={q.error} onRetry={q.reload} /> : (() => {
        const f = q.data.factura, edit = ['borrador', 'error'].includes(f.estado);
        const r = rec || f.receptor;
        return (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
              <b style={{ fontFamily: t.mono, fontSize: 18 }}>{f.serieInterna}-{String(f.numeroInterno).padStart(5, '0')}</b>
              <FChip tone={{ borrador: 'info', emitida: 'ok', anulada: 'neutral', error: 'bad' }[f.estado]}>{f.estado}</FChip>
              <span style={{ color: t.sec, fontSize: 13 }}>Periodo {f.periodo}</span>
            </div>
            {f.fel && f.fel.error && <div style={{ padding: '10px 14px', borderRadius: 10, background: '#FDF0EF', border: '1px solid #F2C4C0', color: t.danger, fontSize: 13 }}>{f.fel.error}</div>}
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 180px), 1fr))', gap: 10 }}>
              {[['nit', 'NIT receptor'], ['nombre', 'Nombre'], ['correo', 'Correo']].map(([k2, l]) => (
                <label key={k2}><span style={fLbl(t)}>{l}</span><input disabled={!edit} value={r[k2] || ''} onChange={(e) => setRec({ ...r, [k2]: e.target.value })} style={{ ...fInp(t), width: '100%', fontFamily: k2 === 'nit' ? t.mono : t.font }} /></label>
              ))}
            </div>
            {edit && rec && <div><FBtn kind="primary" disabled={busy} onClick={() => run(async () => { await fApi().facActualizar(f.id, { receptor: rec }); setRec(null); })}>Guardar receptor</FBtn></div>}
            <div style={{ border: `1px solid ${t.border}`, borderRadius: 12, overflow: 'hidden' }}>
              {f.items.map((it, i) => (
                <div key={i} style={{ display: 'grid', gridTemplateColumns: fCols('1fr 90px 90px 110px'), gap: 10, padding: '10px 14px', borderTop: i ? `1px solid ${t.grid}` : 'none', fontSize: 13 }}>
                  <span>{it.descripcion}</span><span style={{ textAlign: 'right', fontFamily: t.mono, color: t.sec }}>{FQ(it.base)}</span><span style={{ textAlign: 'right', fontFamily: t.mono, color: t.sec }}>{FQ(it.iva)}</span><b style={{ textAlign: 'right', fontFamily: t.mono }}>{FQ(it.total)}</b>
                </div>
              ))}
              <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 22, padding: '12px 14px', background: '#FAFBFC', fontSize: 13.5 }}>
                <span>Base <b style={{ fontFamily: t.mono }}>{FQ(f.subtotal)}</b></span><span>IVA <b style={{ fontFamily: t.mono }}>{FQ(f.iva)}</b></span><span>Total <b style={{ fontFamily: t.mono, fontSize: 16 }}>{FQ(f.total)}</b></span>
              </div>
            </div>
            <div style={{ fontSize: 12.5, color: t.sec }}>{q.data.pagos.length} servicio(s) incluidos: {q.data.pagos.slice(0, 6).map((p) => (p.servicio && p.servicio.titulo) || 'servicio').join(' · ')}{q.data.pagos.length > 6 ? '…' : ''}</div>
            {f.estado === 'emitida' && <div style={{ padding: 12, borderRadius: 10, background: '#0C1826', color: '#fff', fontSize: 12.5 }}><div style={{ color: '#F4C71E', fontWeight: 700, fontSize: 11, textTransform: 'uppercase', letterSpacing: 0.8 }}>Autorización SAT</div><div style={{ fontFamily: t.mono, fontSize: 13.5, marginTop: 4, wordBreak: 'break-all' }}>{f.fel.uuid}</div><div style={{ color: 'rgba(255,255,255,.65)', marginTop: 4 }}>Serie {f.fel.serie || '—'} · Número {f.fel.numero || '—'} · {FD(f.fel.fechaCertificacion)} · {f.fel.certificador}</div></div>}
            {f.estado === 'anulada' && <div style={{ fontSize: 13, color: t.sec }}>Anulada el {FD(f.anulacion.fecha)} · {f.anulacion.motivo}</div>}
            {edit && (
              <div style={{ padding: 14, borderRadius: 12, background: '#FFF8E6', border: '1px solid #F4C71E66' }}>
                <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 4 }}>Sin certificador automático</div>
                <div style={{ fontSize: 12.5, color: t.sec, marginBottom: 10 }}>Descarga el XML, certifícalo en el portal de tu certificador FEL y pega aquí el número de autorización. Si configuraste un certificador con API, deja esto vacío: “Emitir factura” lo certifica solo.</div>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 160px), 1fr))', gap: 10 }}>
                  {[['uuid', 'Autorización (UUID)'], ['serie', 'Serie'], ['numero', 'Número']].map(([k2, l]) => <label key={k2}><span style={fLbl(t)}>{l}</span><input value={man[k2]} onChange={(e) => setMan({ ...man, [k2]: e.target.value })} style={{ ...fInp(t), width: '100%', fontFamily: t.mono }} /></label>)}
                </div>
              </div>
            )}
          </div>
        );
      })()}
    </window.Modal>
  );
}

function FacConfig() {
  const t = window.WC, I = window.WI;
  const q = window.useAsync(() => fApi().facConfig(), []);
  const [f, setF] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => { if (q.data) setF({ ...q.data.config, certificador: { ...q.data.config.certificador, credenciales: {} } }); }, [q.data]);
  if (q.loading || !f) return q.error ? <window.ErrState error={q.error} onRetry={q.reload} /> : <window.Loading />;
  const cat = q.data.config.catalogo, defC = cat[f.certificador.proveedor] || cat.manual, credsGuardadas = q.data.config.certificador.credenciales || {};
  const setE = (k, v) => setF({ ...f, emisor: { ...f.emisor, [k]: v } });
  const setC = (patch) => setF({ ...f, certificador: { ...f.certificador, ...patch } });
  const guardar = async () => { setBusy(true); try { await fApi().facGuardarConfig(f); q.reload(); alert('Guardado.'); } catch (e) { alert(e.message); } setBusy(false); };
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 380px), 1fr))', gap: 18 }}>
      <window.Panel title="Emisor (LOLA)" sub="Datos tal como están inscritos en la SAT (RTU)">
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          {[['nit', 'NIT'], ['codigoEstablecimiento', 'Código de establecimiento'], ['nombre', 'Razón social'], ['nombreComercial', 'Nombre comercial'], ['direccion', 'Dirección fiscal'], ['municipio', 'Municipio'], ['departamento', 'Departamento'], ['codigoPostal', 'Código postal'], ['correo', 'Correo de facturación']].map(([k, l]) => (
            <label key={k} style={{ gridColumn: ['direccion', 'nombre'].includes(k) ? '1 / -1' : 'auto' }}><span style={fLbl(t)}>{l}</span><input value={f.emisor[k] || ''} onChange={(e) => setE(k, e.target.value)} style={{ ...fInp(t), width: '100%' }} /></label>
          ))}
          <label><span style={fLbl(t)}>Régimen de IVA</span><select value={f.afiliacionIva} onChange={(e) => setF({ ...f, afiliacionIva: e.target.value })} style={{ ...fInp(t), width: '100%' }}><option value="GEN">General (12% IVA)</option><option value="PEQ">Pequeño contribuyente (5% sin IVA desglosado)</option></select></label>
          <label><span style={fLbl(t)}>Siguiente número interno</span><input type="number" min="1" value={f.siguienteNumero} onChange={(e) => setF({ ...f, siguienteNumero: e.target.value })} style={{ ...fInp(t), width: '100%', fontFamily: t.mono }} /></label>
          <label style={{ gridColumn: '1 / -1' }}><span style={fLbl(t)}>Descripción del servicio facturado</span><input value={f.descripcionItem || ''} onChange={(e) => setF({ ...f, descripcionItem: e.target.value })} style={{ ...fInp(t), width: '100%' }} /></label>
        </div>
      </window.Panel>
      <window.Panel title="Certificador FEL" sub="Quién certifica las facturas ante la SAT">
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          <label><span style={fLbl(t)}>Certificador</span><select value={f.certificador.proveedor} onChange={(e) => setC({ proveedor: e.target.value })} style={{ ...fInp(t), width: '100%' }}>{Object.entries(cat).map(([k, v]) => <option key={k} value={k}>{v.nombre}</option>)}</select></label>
          {f.certificador.proveedor !== 'manual' && (
            <React.Fragment>
              <FSeg value={f.certificador.entorno} onChange={(v) => setC({ entorno: v })} options={[['pruebas', 'Pruebas'], ['produccion', 'Producción']]} />
              {defC.credenciales.map((c) => <label key={c.k}><span style={fLbl(t)}>{c.label}{credsGuardadas[c.k] && credsGuardadas[c.k].definida ? ' · guardada ' + credsGuardadas[c.k].mascara : ''}</span><input type="password" autoComplete="off" placeholder={credsGuardadas[c.k] && credsGuardadas[c.k].definida ? 'Deja vacío para conservarla' : ''} value={(f.certificador.credenciales || {})[c.k] || ''} onChange={(e) => setC({ credenciales: { ...(f.certificador.credenciales || {}), [c.k]: e.target.value } })} style={{ ...fInp(t), width: '100%', fontFamily: t.mono }} /></label>)}
              {defC.parametros.map((p) => <label key={p.k}><span style={fLbl(t)}>{p.label}</span><input placeholder={p.def} value={(f.certificador.parametros || {})[p.k] || ''} onChange={(e) => setC({ parametros: { ...(f.certificador.parametros || {}), [p.k]: e.target.value } })} style={{ ...fInp(t), width: '100%', fontFamily: t.mono, fontSize: 12.5 }} /></label>)}
              <label style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 14, fontWeight: 650 }}><input type="checkbox" checked={!!f.certificador.activo} onChange={(e) => setC({ activo: e.target.checked })} /> Certificar automáticamente al emitir</label>
            </React.Fragment>
          )}
          <div style={{ fontSize: 12.5, color: t.sec, lineHeight: 1.55, padding: 12, borderRadius: 10, background: '#F6F7F9' }}>
            <b>Cómo empezar:</b> contrata un certificador FEL autorizado por la SAT (INFILE, Digifact, G&T, Megaprint, Guatefacturas…). Con cualquiera puedes trabajar en modo <b>Manual</b>: LOLA genera el XML y tú pegas la autorización. Con <b>INFILE</b> la certificación es automática: pega tus llaves de firma y de API. Las llaves se guardan cifradas y nunca vuelven completas a esta pantalla.
          </div>
        </div>
      </window.Panel>
      <div style={{ gridColumn: '1 / -1', display: 'flex', justifyContent: 'flex-end' }}><FBtn kind="gold" disabled={busy} onClick={guardar}><I.Check size={15} /> {busy ? 'Guardando…' : 'Guardar datos fiscales'}</FBtn></div>
    </div>
  );
}

// ════════════════════════════════ CONCILIACIÓN ════════════════════════════════
const BANCOS = ['Banco Industrial', 'Banrural', 'BAM', 'G&T Continental', 'BAC Credomatic', 'Promerica', 'Bantrab', 'Banco Inmobiliario', 'Otro'];
function ConciliacionView() {
  const t = window.WC, I = window.WI;
  const q = window.useAsync(() => fApi().conLotes(), []);
  const [lote, setLote] = React.useState(null);
  const [subir, setSubir] = React.useState(false);
  if (lote) return <ConLote id={lote} onBack={() => { setLote(null); q.reload(); }} />;
  if (q.loading && !q.data) return <window.Loading />;
  if (q.error) return <window.ErrState error={q.error} onRetry={q.reload} />;
  const ab = q.data.abiertas;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 14 }}>
        <window.KpiCard hero label="Extractos conciliados" value={String(q.data.items.length)} icon="Building" />
        <window.KpiCard label="Recargas sin depósito (+48 h)" value={String(ab.recargas.length)} note={FQ(ab.recargas.reduce((s, m) => s + m.monto, 0))} icon="Alert" accent={ab.recargas.length ? '#B7791F' : '#1F8A5B'} />
        <window.KpiCard label="Retiros sin pagar (+48 h)" value={String(ab.retiros.length)} note={FQ(ab.retiros.reduce((s, m) => s + m.monto, 0))} icon="Clock" accent={ab.retiros.length ? '#C0392B' : '#1F8A5B'} />
      </div>
      <window.Panel title="Extractos bancarios" sub="Sube el estado de cuenta de tu banca en línea (CSV o TXT) y LOLA lo cruza con recargas, retiros y depósitos de pasarelas" pad={0} action={<FBtn kind="gold" onClick={() => setSubir(true)}><I.Doc size={15} /> Subir extracto</FBtn>}>
        {q.data.items.length === 0 ? <div style={{ padding: 40, textAlign: 'center', color: t.sec }}>Aún no has subido ningún extracto.</div> : q.data.items.map((l, i) => (
          <button key={l.id} onClick={() => setLote(l.id)} style={{ width: '100%', display: 'grid', gridTemplateColumns: fCols('1.4fr 1fr 110px 110px 1.2fr'), gap: 12, alignItems: 'center', padding: '13px 18px', border: 'none', borderTop: i ? `1px solid ${t.grid}` : 'none', background: '#fff', cursor: 'pointer', textAlign: 'left', fontFamily: t.font, fontSize: 13.5 }}>
            <span><b>{l.banco}</b>{l.cuenta ? ' · ' + l.cuenta : ''}<div style={{ fontSize: 12, color: t.ter }}>{l.nombreArchivo || 'extracto'} · subido {FD(l.createdAt)}</div></span>
            <span style={{ color: t.sec }}>{FD(l.desde)} – {FD(l.hasta)}</span>
            <span style={{ fontFamily: t.mono, color: '#1F8A5B', textAlign: 'right' }}>+{FQk(l.totales.creditos)}</span>
            <span style={{ fontFamily: t.mono, color: t.danger, textAlign: 'right' }}>−{FQk(l.totales.debitos)}</span>
            <span style={{ display: 'flex', gap: 6, justifyContent: 'flex-end', flexWrap: 'wrap' }}><FChip tone="ok">{l.totales.conciliadas} conciliadas</FChip>{l.totales.sugeridas > 0 && <FChip tone="gold">{l.totales.sugeridas} por revisar</FChip>}{l.totales.pendientes > 0 && <FChip tone="neutral">{l.totales.pendientes} sin coincidencia</FChip>}</span>
          </button>
        ))}
      </window.Panel>
      {(ab.recargas.length > 0 || ab.retiros.length > 0) && (
        <window.Panel title="Partidas abiertas" sub="Lo que LOLA espera ver en el banco y todavía no aparece en ningún extracto" pad={0}>
          {[...ab.recargas.map((m) => ['Recarga por transferencia', m]), ...ab.retiros.map((m) => ['Retiro por pagar', m])].map(([tipo, m], i) => (
            <div key={m.id} style={{ display: 'grid', gridTemplateColumns: fCols('160px 1fr 140px 120px'), gap: 12, padding: '11px 18px', borderTop: i ? `1px solid ${t.grid}` : 'none', fontSize: 13.5, alignItems: 'center' }}>
              <FChip tone={tipo.startsWith('Retiro') ? 'bad' : 'warn'}>{tipo}</FChip>
              <span>{(m.usuario && (m.usuario.empresa || m.usuario.nombre)) || m.usuarioNombre}{m.referencia ? <span style={{ fontFamily: t.mono, color: t.sec }}> · {m.referencia}</span> : null}</span>
              <span style={{ color: t.sec }}>{FD(m.createdAt)}</span>
              <b style={{ fontFamily: t.mono, textAlign: 'right' }}>{FQ(m.monto)}</b>
            </div>
          ))}
        </window.Panel>
      )}
      {subir && <ConSubirModal onClose={() => setSubir(false)} onDone={(id) => { setSubir(false); setLote(id); }} />}
    </div>
  );
}

function ConSubirModal({ onClose, onDone }) {
  const t = window.WC, I = window.WI;
  const [banco, setBanco] = React.useState(BANCOS[0]);
  const [cuenta, setCuenta] = React.useState('');
  const [archivo, setArchivo] = React.useState(null);
  const [texto, setTexto] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const leer = (file) => { const r = new FileReader(); r.onload = () => { setTexto(String(r.result || '')); setArchivo(file.name); }; r.readAsText(file, 'utf-8'); };
  const enviar = async () => { setBusy(true); setErr(''); try { const r = await fApi().conSubir({ banco, cuenta, nombreArchivo: archivo || 'pegado.csv', contenido: texto }); onDone(r.lote.id); } catch (e) { setErr(e.message); } setBusy(false); };
  return (
    <window.Modal title="Subir extracto bancario" sub="Banca en línea → Estado de cuenta → Exportar (CSV, TXT o “Excel separado por comas”)" width={720} onClose={onClose}
      footer={<div style={{ display: 'flex', gap: 8 }}><FBtn onClick={onClose}>Cancelar</FBtn><FBtn kind="gold" disabled={busy || texto.length < 20} onClick={enviar}>{busy ? 'Analizando…' : 'Conciliar'}</FBtn></div>}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {err && <div style={{ padding: '10px 14px', borderRadius: 10, background: '#FDF0EF', border: '1px solid #F2C4C0', color: t.danger, fontSize: 13 }}>{err}</div>}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          <label><span style={fLbl(t)}>Banco</span><select value={banco} onChange={(e) => setBanco(e.target.value)} style={{ ...fInp(t), width: '100%' }}>{BANCOS.map((b) => <option key={b}>{b}</option>)}</select></label>
          <label><span style={fLbl(t)}>Cuenta (opcional)</span><input value={cuenta} onChange={(e) => setCuenta(e.target.value)} placeholder="Ej. 123-456789-0" style={{ ...fInp(t), width: '100%', fontFamily: t.mono }} /></label>
        </div>
        <label style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10, padding: 22, borderRadius: 14, border: `2px dashed ${t.border}`, cursor: 'pointer', background: '#FAFBFC', fontSize: 14, fontWeight: 650, color: t.text }}>
          <I.Doc size={20} /> {archivo ? archivo + ' · ' + texto.split('\n').length + ' líneas' : 'Elegir archivo .csv / .txt'}
          <input type="file" accept=".csv,.txt,text/csv,text/plain" hidden onChange={(e) => e.target.files[0] && leer(e.target.files[0])} />
        </label>
        <label><span style={fLbl(t)}>…o pega aquí el contenido</span><textarea value={texto} onChange={(e) => { setTexto(e.target.value); setArchivo(null); }} rows={6} placeholder={'Fecha,Descripción,Documento,Débito,Crédito,Saldo\n15/10/2026,DEPOSITO TRF-AB12CD34,123456,,1250.00,10250.00'} style={{ ...fInp(t), width: '100%', height: 'auto', padding: 10, fontFamily: t.mono, fontSize: 12, resize: 'vertical' }} /></label>
        <div style={{ fontSize: 12.5, color: t.sec, lineHeight: 1.55 }}>LOLA reconoce las columnas solas (Fecha, Descripción/Concepto, Documento/Referencia, Débito/Cargo, Crédito/Abono o Monto con signo, Saldo). Las recargas por transferencia se identifican por su referencia <b style={{ fontFamily: t.mono }}>TRF-…</b>; los depósitos de pasarelas, por su nombre en la descripción. Nada se acredita hasta que lo confirmes.</div>
      </div>
    </window.Modal>
  );
}

function ConLote({ id, onBack }) {
  const t = window.WC, I = window.WI;
  const q = window.useAsync(() => fApi().conLote(id), [id]);
  const [filtro, setFiltro] = React.useState('accion');
  const [busy, setBusy] = React.useState('');
  const [manual, setManual] = React.useState(null);
  const [lote, setLoteData] = React.useState(null);
  React.useEffect(() => { if (q.data) setLoteData(q.data.lote); }, [q.data]);
  if (!lote) return q.error ? <window.ErrState error={q.error} onRetry={q.reload} /> : <window.Loading />;
  const act = async (k, fn) => { setBusy(k); try { const r = await fn(); if (r && r.lote) setLoteData(r.lote); if (r && r.confirmadas != null) alert(r.confirmadas + ' líneas confirmadas.'); } catch (e) { alert(e.message); } setBusy(''); };
  const tone = { conciliada: 'ok', sugerida: 'gold', revisar: 'warn', pendiente: 'neutral', ignorada: 'neutral' };
  const lineas = lote.lineas.filter((l) => filtro === 'todas' || (filtro === 'accion' ? ['sugerida', 'revisar', 'pendiente'].includes(l.estado) : l.estado === filtro));
  const altas = lote.lineas.filter((l) => l.estado === 'sugerida' && l.match.confianza === 'alta').length;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
        <FBtn onClick={onBack}>‹ Extractos</FBtn>
        <b style={{ fontSize: 16 }}>{lote.banco}{lote.cuenta ? ' · ' + lote.cuenta : ''}</b><span style={{ color: t.sec, fontSize: 13 }}>{FD(lote.desde)} – {FD(lote.hasta)} · {lote.lineas.length} movimientos</span>
        <span style={{ flex: 1 }} />
        <FBtn disabled={!!busy} onClick={() => act('re', () => fApi().conReemparejar(id))}><I.Repeat size={15} /> Volver a cruzar</FBtn>
        <FBtn kind="gold" disabled={!!busy || !altas} onClick={() => window.confirm('Se confirmarán ' + altas + ' coincidencias de confianza alta (referencia y monto exactos). ¿Continuar?') && act('alta', () => fApi().conConfirmarAlta(id))}><I.Check size={15} /> Confirmar {altas} de confianza alta</FBtn>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 12 }}>
        {[['Créditos', '+' + FQ(lote.totales.creditos), '#1F8A5B'], ['Débitos', '−' + FQ(lote.totales.debitos), t.danger], ['Conciliadas', lote.totales.conciliadas, '#1F8A5B'], ['Por revisar', lote.totales.sugeridas, '#B7791F'], ['Sin coincidencia', lote.totales.pendientes, t.sec]].map(([l, v, c]) => (
          <div key={l} style={{ background: '#fff', border: `1px solid ${t.border}`, borderRadius: 14, padding: '12px 16px' }}><div style={fLbl(t)}>{l}</div><div style={{ fontSize: 20, fontWeight: 800, color: c, fontFamily: t.mono }}>{v}</div></div>
        ))}
      </div>
      <window.Panel title="Movimientos del extracto" pad={0} action={<FSeg value={filtro} onChange={setFiltro} options={[['accion', 'Requieren acción'], ['conciliada', 'Conciliadas'], ['ignorada', 'Ignoradas'], ['todas', 'Todas']]} />}>
        {lineas.length === 0 ? <div style={{ padding: 40, textAlign: 'center', color: t.sec }}>Nada pendiente aquí.</div> : lineas.map((l, i) => (
          <div key={l.id} style={{ display: 'grid', gridTemplateColumns: fCols('92px minmax(0,1.5fr) 110px minmax(0,1.6fr) auto'), gap: 12, padding: '12px 18px', borderTop: i ? `1px solid ${t.grid}` : 'none', fontSize: 13, alignItems: 'center', background: l.estado === 'revisar' ? '#FFFBF0' : '#fff' }}>
            <span style={{ fontFamily: t.mono, color: t.sec }}>{FD(l.fecha)}</span>
            <span style={{ minWidth: 0 }}><div style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', fontWeight: 600 }}>{l.descripcion}</div>{l.referencia && <div style={{ fontFamily: t.mono, fontSize: 11.5, color: t.ter }}>Doc. {l.referencia}</div>}</span>
            <b style={{ fontFamily: t.mono, textAlign: 'right', color: l.credito ? '#1F8A5B' : t.danger }}>{l.credito ? '+' + FQ(l.credito) : '−' + FQ(l.debito)}</b>
            <span style={{ minWidth: 0 }}>
              <span style={{ display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap' }}><FChip tone={tone[l.estado]}>{l.estado}</FChip>{l.match.tipo && <span style={{ fontWeight: 650 }}>{({ recarga: 'Recarga', retiro: 'Retiro', liquidacion_pasarela: 'Depósito de pasarela' })[l.match.tipo]}{l.match.usuarioNombre ? ' · ' + l.match.usuarioNombre : ''}</span>}{l.match.confianza && l.estado !== 'conciliada' && <span style={{ fontSize: 11.5, color: t.ter }}>confianza {l.match.confianza}</span>}</span>
              {l.match.motivo && <div style={{ fontSize: 12, color: t.sec, marginTop: 3 }}>{l.match.motivo}</div>}
            </span>
            <span style={{ display: 'flex', gap: 6, justifyContent: 'flex-end' }}>
              {['sugerida', 'revisar'].includes(l.estado) && l.match.tipo && l.match.ids.length > 0 && <FBtn kind="primary" style={{ height: 32 }} disabled={!!busy} onClick={() => act(l.id, () => fApi().conConfirmar(id, l.id))}>Confirmar</FBtn>}
              {!['conciliada', 'ignorada'].includes(l.estado) && <FBtn style={{ height: 32 }} disabled={!!busy} onClick={() => setManual(l)}>Elegir</FBtn>}
              {!['conciliada', 'ignorada'].includes(l.estado) && <FBtn style={{ height: 32 }} disabled={!!busy} onClick={() => act(l.id, () => fApi().conIgnorar(id, l.id, { motivo: window.prompt('¿Por qué no corresponde a LOLA? (opcional)') || '' }))}>Ignorar</FBtn>}
            </span>
          </div>
        ))}
      </window.Panel>
      {manual && <ConManualModal loteId={id} linea={manual} onClose={() => setManual(null)} onDone={(l) => { setManual(null); setLoteData(l); }} />}
    </div>
  );
}

function ConManualModal({ loteId, linea, onClose, onDone }) {
  const t = window.WC;
  const q = window.useAsync(() => fApi().conCandidatos(loteId, linea.id), [linea.id]);
  const [busy, setBusy] = React.useState(false);
  const elegir = async (m) => { setBusy(true); try { const r = await fApi().conConfirmar(loteId, linea.id, { tipo: q.data.tipo, id: m.id }); onDone(r.lote); } catch (e) { alert(e.message); } setBusy(false); };
  return (
    <window.Modal title="Elegir coincidencia" sub={`${FD(linea.fecha)} · ${linea.credito ? '+' + FQ(linea.credito) : '−' + FQ(linea.debito)} · ${linea.descripcion}`} width={720} onClose={onClose} footer={<FBtn onClick={onClose}>Cerrar</FBtn>}>
      {q.loading ? <window.Loading /> : q.error ? <window.ErrState error={q.error} onRetry={q.reload} /> : q.data.items.length === 0 ? <div style={{ color: t.sec, padding: 20, textAlign: 'center' }}>No hay {q.data.tipo === 'recarga' ? 'recargas' : 'retiros'} pendientes en ±30 días.</div> : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {q.data.items.map((m) => { const igual = Math.abs(m.monto - (linea.credito || linea.debito)) < 0.01; return (
            <button key={m.id} disabled={busy} onClick={() => window.confirm('¿Confirmar este ' + q.data.tipo + ' contra la línea del banco? ' + (q.data.tipo === 'recarga' ? 'Se acreditará el saldo al usuario.' : 'Se marcará como pagado.')) && elegir(m)} style={{ display: 'grid', gridTemplateColumns: fCols('1fr 120px 120px'), gap: 10, padding: '11px 14px', borderRadius: 10, border: `1px solid ${igual ? '#1F8A5B66' : t.border}`, background: igual ? '#EAF7F0' : '#fff', cursor: 'pointer', textAlign: 'left', fontFamily: t.font, fontSize: 13.5 }}>
              <span><b>{(m.usuario && (m.usuario.empresa || m.usuario.nombre)) || m.usuarioNombre}</b>{m.referencia ? <span style={{ fontFamily: t.mono, color: t.sec }}> · {m.referencia}</span> : null}</span>
              <span style={{ color: t.sec }}>{FD(m.createdAt)}</span>
              <b style={{ fontFamily: t.mono, textAlign: 'right' }}>{FQ(m.monto)}</b>
            </button>
          ); })}
        </div>
      )}
    </window.Modal>
  );
}

Object.assign(window, { ReportesView, FacturacionView, ConciliacionView });
