// Pagos & depósitos — branchea según rol
function Pagos() {
  const u = DesasfAPI.getCurrentUser() || {};
  if (u.rol === 'cliente') return <ClientePagos/>;
  return <PagosFinanzas/>;
}

function PagosFinanzas() {
  const u = DesasfAPI.getCurrentUser() || {};
  const verBancos = DesasfAPI.hasPerm('pagos.bancos.ver', { strict: true });
  const [data,    setData]    = React.useState({ rows: [], totales: {}, total: 0 });
  const [bancos,  setBancos]  = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [filtro,  setFiltro]  = React.useState('por_validar');
  const [bancoFil, setBancoFil] = React.useState('');
  const [periodo,  setPeriodo]  = React.useState('');
  const [menuOpen, setMenuOpen] = React.useState(null);

  function fetchPagos(estado = filtro) {
    setLoading(true);
    const q = estado ? `?estado=${estado}` : '';
    Promise.all([
      DesasfAPI.get('/pagos' + q),
      verBancos ? DesasfAPI.get('/pagos/bancos').catch(() => []) : Promise.resolve([]),
    ]).then(([d, b]) => {
      setData(d); setBancos(Array.isArray(b) ? b : []);
    }).catch(console.error)
      .finally(() => setLoading(false));
  }

  React.useEffect(() => { fetchPagos(filtro); }, [filtro]);

  React.useEffect(() => {
    window._desasfRefreshPagos = () => fetchPagos(filtro);
    return () => { delete window._desasfRefreshPagos; };
  });

  const t = data.totales || {};
  const montoPend  = Number(t.monto_pendiente   || 0);
  const montoConf  = Number(t.monto_confirmado  || 0);
  const nPend      = Number(t.vouchers_pendientes  || 0);
  const nConf      = Number(t.vouchers_confirmados || 0);
  const nRej       = Number(t.vouchers_rechazados  || 0);

  function fmt(n) {
    if (n >= 1_000_000) return 'S/ ' + (n / 1_000_000).toFixed(2) + 'M';
    if (n >= 1_000)     return 'S/ ' + (n / 1_000).toFixed(1) + 'k';
    return 'S/ ' + n.toLocaleString('es-PE');
  }

  const filteredRaw = data.rows.filter(r => {
    if (bancoFil && r.banco !== bancoFil) return false;
    if (periodo) {
      const dias = { '7d': 7, '30d': 30, '90d': 90 }[periodo];
      if (dias) {
        const cut = Date.now() - dias * 86400000;
        if (new Date(r.cargado_at).getTime() < cut) return false;
      }
    }
    return true;
  });

  const rows = filteredRaw.map(r => ({
    v:   r.voucher_ref,
    c:   r.cliente_nombre,
    s:   r.ticket,
    b:   r.banco || '—',
    op:  r.operacion_num || '—',
    m:   'S/ ' + Number(r.monto).toLocaleString('es-PE'),
    t:   formatRelativeTime(r.cargado_at),
    st:  r.estado === 'confirmado' ? 'ok' : r.estado === 'rechazado' ? 'danger' : 'review',
    stl: r.estado === 'confirmado' ? 'Confirmado' : r.estado === 'rechazado' ? 'Rechazado' : 'Por validar',
    raw: r.estado,
    _id: r.id,
  }));

  const filtros = [
    { k: 'por_validar', l: 'Por validar', n: nPend },
    { k: 'confirmado',  l: 'Confirmados', n: nConf },
    { k: 'rechazado',   l: 'Rechazados',  n: nRej  },
    { k: '',            l: 'Todos',       n: nPend + nConf + nRej },
  ];

  return (
    <div className="app">
      <Sidebar active="pagos"/>
      <Topbar crumbs={['Operación', 'Pagos y depósitos']}/>
      <main className="main">
        <div className="page-head">
          <div>
            <h1>Pagos y depósitos</h1>
            <p>{nPend} vouchers por validar · {fmt(montoPend)} en bandeja · {nConf} pagos confirmados</p>
          </div>
          <div className="row">
            <button className="btn btn--ghost" onClick={() => exportCSV(
              'conciliacion-' + new Date().toISOString().slice(0,10) + '.csv',
              filteredRaw,
              [
                { label: 'Voucher',      value: 'voucher_ref' },
                { label: 'Cliente',      value: 'cliente_nombre' },
                { label: 'Solicitud',    value: 'ticket' },
                { label: 'Banco',        value: r => r.banco || '' },
                { label: 'N° operación', value: r => r.operacion_num || '' },
                { label: 'Monto',        value: r => Number(r.monto || 0) },
                { label: 'Estado',       value: 'estado' },
                { label: 'Cargado',      value: r => new Date(r.cargado_at).toLocaleString('es-PE') },
              ]
            )}>{Icon.download}<span>Conciliación</span></button>
            <button className="btn btn--ghost" onClick={() => fetchPagos(filtro)}>
              {Icon.refresh || Icon.upload}<span>{loading ? 'Cargando…' : 'Refrescar'}</span>
            </button>
            <button className="btn btn--accent" onClick={() => {
              window._desasfCurrentSolicitud = null;
              openModal('subir-voucher');
            }}>{Icon.upload}<span>Cargar voucher</span></button>
          </div>
        </div>

        <div className="kpi-grid">
          <div className="kpi">
            <div className="kpi__label">Por validar</div>
            <div className="kpi__value">{fmt(montoPend)}</div>
            <span className="kpi__delta down">{nPend} vouchers</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, nPend * 12) + '%' }}/></div>
          </div>
          <div className="kpi kpi--ok">
            <div className="kpi__label">Confirmados</div>
            <div className="kpi__value">{fmt(montoConf)}</div>
            <span className="kpi__delta up">{nConf} pagos validados</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, nConf * 8) + '%' }}/></div>
          </div>
          <div className="kpi kpi--accent">
            <div className="kpi__label">Rechazados</div>
            <div className="kpi__value">{nRej}</div>
            <span className="kpi__delta down">vouchers no aprobados</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, nRej * 20) + '%' }}/></div>
          </div>
          <div className="kpi kpi--review">
            <div className="kpi__label">Bancos activos</div>
            <div className="kpi__value">{bancos.length}</div>
            <span className="kpi__delta up">{bancos.map(b => b.banco).join(' · ') || '—'}</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, bancos.length * 25) + '%' }}/></div>
          </div>
        </div>

        <div className="filterbar">
          {filtros.map(f => (
            <span key={f.k || 'todos'}
                  className={'chip' + (filtro === f.k ? ' is-active' : '')}
                  style={{ cursor: 'pointer' }}
                  onClick={() => setFiltro(f.k)}>
              {f.l} <span className="n">{f.n}</span>
            </span>
          ))}
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 8, alignItems: 'center' }}>
            <select className="input" style={{ height: 28, padding: '0 8px', fontSize: 12 }}
              value={bancoFil} onChange={e => setBancoFil(e.target.value)}>
              <option value="">Banco: todos</option>
              {bancos.map(b => <option key={b.banco} value={b.banco}>{b.banco}</option>)}
            </select>
            <select className="input" style={{ height: 28, padding: '0 8px', fontSize: 12 }}
              value={periodo} onChange={e => setPeriodo(e.target.value)}>
              <option value="">Periodo: todos</option>
              <option value="7d">Últimos 7 días</option>
              <option value="30d">Últimos 30 días</option>
              <option value="90d">Últimos 90 días</option>
            </select>
          </div>
        </div>

        <div className="card" style={{ borderRadius: '0 0 var(--r-2) var(--r-2)', borderTop: 0 }}>
          <table className="tbl">
            <thead><tr>
              <th>Voucher</th><th>Cliente / Solicitud</th><th>Banco</th><th>N° operación</th>
              <th style={{ textAlign: 'right' }}>Monto</th><th>Recibido</th><th>Estado</th><th></th>
            </tr></thead>
            <tbody>
              {rows.map(r => (
                <tr key={r.v}>
                  <td className="id" style={{ fontWeight: 600, color: 'var(--ink-1)' }}>{r.v}</td>
                  <td><b>{r.c}</b><div className="muted" style={{ fontSize: 11 }}>{r.s}</div></td>
                  <td>{r.b}</td>
                  <td className="k">{r.op}</td>
                  <td className="num" style={{ textAlign: 'right', fontWeight: 600 }}>{r.m}</td>
                  <td className="num">{r.t}</td>
                  <td><Pill kind={r.st}>{r.stl}</Pill></td>
                  <td style={{ position: 'relative' }}>
                    <div className="row" style={{ gap: 4 }}>
                      {r.raw === 'por_validar' ? (
                        <button className="btn btn--sm btn--accent" onClick={() => {
                          window._desasfCurrentPago = r._id;
                          openModal('validar-voucher');
                        }}>Validar</button>
                      ) : (
                        <button className="btn btn--sm btn--ghost" onClick={() => {
                          window._desasfCurrentPago = r._id;
                          openModal('validar-voucher');
                        }}>Ver</button>
                      )}
                      <div className="icon-btn" onClick={() => setMenuOpen(menuOpen === r._id ? null : r._id)}>{Icon.more}</div>
                    </div>
                    {menuOpen === r._id && (
                      <div style={{ position: 'absolute', top: '100%', right: 0, zIndex: 200, background: 'var(--surface)', border: '1px solid var(--line)', borderRadius: 8, padding: '4px 0', minWidth: 200, boxShadow: '0 4px 16px rgba(0,0,0,.18)' }}>
                        <div className="menu-item" onClick={() => {
                          setMenuOpen(null);
                          window._desasfCurrentPago = r._id;
                          openModal('validar-voucher');
                        }}>{Icon.doc}<span>Ver detalle</span></div>
                        <a className="menu-item" style={{ textDecoration: 'none', color: 'inherit' }}
                          onClick={(e) => {
                            e.preventDefault();
                            setMenuOpen(null);
                            const tk = DesasfAPI.getToken();
                            fetch('/api/pagos/' + r._id + '/descargar', { headers: { Authorization: 'Bearer ' + tk } })
                              .then(rr => rr.ok ? rr.blob() : Promise.reject(new Error('Sin archivo')))
                              .then(b => {
                                const u = URL.createObjectURL(b);
                                const a = document.createElement('a');
                                a.href = u; a.download = r.v + '.pdf';
                                document.body.appendChild(a); a.click(); a.remove();
                                URL.revokeObjectURL(u);
                              })
                              .catch(() => alert('No hay archivo de voucher para descargar'));
                          }}>{Icon.download}<span>Descargar voucher</span></a>
                      </div>
                    )}
                  </td>
                </tr>
              ))}
              {!loading && rows.length === 0 && (
                <tr><td colSpan={8} style={{ padding: 32, textAlign: 'center', color: 'var(--ink-5)' }}>
                  Sin pagos para el filtro seleccionado.
                </td></tr>
              )}
            </tbody>
          </table>
          {menuOpen && <div style={{ position: 'fixed', inset: 0, zIndex: 199 }} onClick={() => setMenuOpen(null)}/>}
        </div>

        {verBancos && <div className="sect-h"><h2>Conciliación bancaria</h2></div>}
        {verBancos && <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
          {bancos.length > 0 ? bancos.map(b => {
            const conf = Number(b.confirmado || 0);
            const pend = Number(b.pendiente  || 0);
            const tot  = conf + pend;
            const pct  = tot > 0 ? Math.round((conf / tot) * 100) : 0;
            return (
              <div className="card" key={b.banco}>
                <div className="card__body">
                  <div className="row" style={{ justifyContent: 'space-between', marginBottom: 8 }}>
                    <span style={{ fontFamily: 'var(--cond)', fontWeight: 700, fontSize: 14 }}>{b.banco}</span>
                    <Pill kind={pct >= 80 ? 'ok' : pct >= 50 ? 'review' : 'pending'}>{pct}% OK</Pill>
                  </div>
                  <div className="muted k" style={{ fontSize: 11 }}>{b.total_vouchers} voucher{b.total_vouchers === 1 ? '' : 's'}</div>
                  <div style={{ fontFamily: 'var(--cond)', fontSize: 22, fontWeight: 700, marginTop: 8 }}>{fmt(conf)}</div>
                  <div style={{ height: 4, background: 'var(--ink-9)', borderRadius: 2, marginTop: 8, overflow: 'hidden' }}>
                    <div style={{ width: pct + '%', height: '100%', background: 'var(--ink-0)' }}/>
                  </div>
                </div>
              </div>
            );
          }) : (
            <div className="card" style={{ gridColumn: '1 / -1' }}>
              <div className="card__body" style={{ color: 'var(--ink-5)', fontSize: 13 }}>
                Sin movimientos bancarios registrados aún.
              </div>
            </div>
          )}
        </div>}
      </main>
    </div>
  );
}

// Vista cliente — solo lectura + subir voucher
function ClientePagos() {
  const [data,    setData]    = React.useState({ rows: [], totales: {}, total: 0 });
  const [loading, setLoading] = React.useState(true);
  const [filtro,  setFiltro]  = React.useState('');
  const [menuOpen, setMenuOpen] = React.useState(null);

  function fetchPagos(estado = filtro) {
    setLoading(true);
    const q = estado ? `?estado=${estado}` : '';
    DesasfAPI.get('/pagos' + q)
      .then(d => setData(d))
      .catch(console.error)
      .finally(() => setLoading(false));
  }

  React.useEffect(() => { fetchPagos(filtro); }, [filtro]);
  React.useEffect(() => {
    window._desasfRefreshPagos = () => fetchPagos(filtro);
    return () => { delete window._desasfRefreshPagos; };
  });

  const t = data.totales || {};
  const montoPend = Number(t.monto_pendiente  || 0);
  const montoConf = Number(t.monto_confirmado || 0);
  const nPend = Number(t.vouchers_pendientes  || 0);
  const nConf = Number(t.vouchers_confirmados || 0);
  const nRej  = Number(t.vouchers_rechazados  || 0);

  function fmt(n) {
    if (n >= 1_000_000) return 'S/ ' + (n / 1_000_000).toFixed(2) + 'M';
    if (n >= 1_000)     return 'S/ ' + (n / 1_000).toFixed(1) + 'k';
    return 'S/ ' + n.toLocaleString('es-PE');
  }

  async function descargarVoucher(id, ref) {
    try {
      const tk = DesasfAPI.getToken();
      const res = await fetch('/api/pagos/' + id + '/descargar', { headers: { Authorization: 'Bearer ' + tk } });
      if (!res.ok) throw new Error('Sin archivo');
      const b = await res.blob();
      const u2 = URL.createObjectURL(b);
      const a = document.createElement('a');
      a.href = u2; a.download = (ref || 'voucher') + '.pdf';
      document.body.appendChild(a); a.click(); a.remove();
      URL.revokeObjectURL(u2);
    } catch { alert('No hay archivo de voucher para descargar'); }
  }

  const filtros = [
    { k: '',            l: 'Todos',       n: nPend + nConf + nRej },
    { k: 'por_validar', l: 'En revisión', n: nPend },
    { k: 'confirmado',  l: 'Confirmados', n: nConf },
    { k: 'rechazado',   l: 'Rechazados',  n: nRej  },
  ];

  return (
    <div className="app">
      <CliSidebar active="pagos"/>
      <Topbar crumbs={['Mi cuenta', 'Pagos y vouchers']}/>
      <main className="main">
        <div className="page-head">
          <div>
            <h1>Pagos y vouchers</h1>
            <p>Sube la constancia de tu depósito — el equipo de Desasf validará el pago manualmente.</p>
          </div>
          <div className="row">
            <button className="btn btn--ghost" onClick={() => fetchPagos(filtro)}>
              {Icon.refresh || Icon.upload}<span>{loading ? 'Cargando…' : 'Refrescar'}</span>
            </button>
            <button className="btn btn--accent" onClick={() => {
              window._desasfCurrentSolicitud = null;
              openModal('subir-voucher');
            }}>{Icon.upload}<span>Subir voucher</span></button>
          </div>
        </div>

        <div className="kpi-grid">
          <div className="kpi kpi--review">
            <div className="kpi__label">En revisión</div>
            <div className="kpi__value">{fmt(montoPend)}</div>
            <span className="kpi__delta">{nPend} voucher{nPend === 1 ? '' : 's'}</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, nPend * 14) + '%' }}/></div>
          </div>
          <div className="kpi kpi--ok">
            <div className="kpi__label">Confirmados</div>
            <div className="kpi__value">{fmt(montoConf)}</div>
            <span className="kpi__delta up">{nConf} pago{nConf === 1 ? '' : 's'} validados</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, nConf * 8) + '%' }}/></div>
          </div>
          <div className="kpi kpi--accent">
            <div className="kpi__label">Rechazados</div>
            <div className="kpi__value">{nRej}</div>
            <span className="kpi__delta down">{nRej > 0 ? 'Revisa el motivo' : 'sin rechazos'}</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, nRej * 25) + '%' }}/></div>
          </div>
        </div>

        <div className="filterbar">
          {filtros.map(f => (
            <span key={f.k || 'todos'}
              className={'chip' + (filtro === f.k ? ' is-active' : '')}
              style={{ cursor: 'pointer' }}
              onClick={() => setFiltro(f.k)}>
              {f.l} <span className="n">{f.n}</span>
            </span>
          ))}
        </div>

        <div className="card" style={{ borderRadius: '0 0 var(--r-2) var(--r-2)', borderTop: 0 }}>
          <table className="tbl">
            <thead><tr>
              <th>Voucher</th><th>Solicitud</th><th>Banco</th><th>N° operación</th>
              <th style={{ textAlign: 'right' }}>Monto</th><th>Recibido</th><th>Estado</th><th></th>
            </tr></thead>
            <tbody>
              {data.rows.map(r => {
                const stKind = r.estado === 'confirmado' ? 'ok' : r.estado === 'rechazado' ? 'danger' : 'review';
                const stLbl  = r.estado === 'confirmado' ? 'Confirmado' : r.estado === 'rechazado' ? 'Rechazado' : 'En revisión';
                return (
                  <tr key={r.id}>
                    <td className="id" style={{ fontWeight: 600, color: 'var(--ink-1)' }}>{r.voucher_ref}</td>
                    <td><b>{r.ticket}</b></td>
                    <td>{r.banco || '—'}</td>
                    <td className="k">{r.operacion_num || '—'}</td>
                    <td className="num" style={{ textAlign: 'right', fontWeight: 600 }}>S/ {Number(r.monto).toLocaleString('es-PE')}</td>
                    <td className="num">{formatRelativeTime(r.cargado_at)}</td>
                    <td><Pill kind={stKind}>{stLbl}</Pill></td>
                    <td style={{ position: 'relative' }}>
                      <div className="row" style={{ gap: 4 }}>
                        <button className="btn btn--sm btn--ghost" onClick={() => {
                          window._desasfCurrentPago = r.id;
                          openModal('validar-voucher');
                        }}>Ver detalle</button>
                        <div className="icon-btn" onClick={() => setMenuOpen(menuOpen === r.id ? null : r.id)}>{Icon.more}</div>
                      </div>
                      {menuOpen === r.id && (
                        <div style={{ position: 'absolute', top: '100%', right: 0, zIndex: 200, background: 'var(--surface)', border: '1px solid var(--line)', borderRadius: 8, padding: '4px 0', minWidth: 200, boxShadow: '0 4px 16px rgba(0,0,0,.18)' }}>
                          <div className="menu-item" onClick={() => {
                            setMenuOpen(null);
                            window._desasfCurrentPago = r.id;
                            openModal('validar-voucher');
                          }}>{Icon.doc}<span>Ver detalle</span></div>
                          <div className="menu-item" onClick={() => { setMenuOpen(null); descargarVoucher(r.id, r.voucher_ref); }}>
                            {Icon.download}<span>Descargar voucher</span>
                          </div>
                        </div>
                      )}
                    </td>
                  </tr>
                );
              })}
              {!loading && data.rows.length === 0 && (
                <tr><td colSpan={8} style={{ padding: 32, textAlign: 'center', color: 'var(--ink-5)' }}>
                  Aún no tienes vouchers cargados. Pulsa "Subir voucher" para enviar tu primer pago.
                </td></tr>
              )}
            </tbody>
          </table>
          {menuOpen && <div style={{ position: 'fixed', inset: 0, zIndex: 199 }} onClick={() => setMenuOpen(null)}/>}
        </div>

        <div style={{ marginTop: 16, padding: 14, background: 'var(--ink-9)', borderRadius: 8, fontSize: 12, color: 'var(--ink-4)', lineHeight: 1.6 }}>
          <b style={{ color: 'var(--ink-1)' }}>Cómo funciona</b>
          <ol style={{ margin: '6px 0 0 18px', padding: 0 }}>
            <li>Pulsa <b>"Subir voucher"</b> y selecciona la solicitud que estás pagando.</li>
            <li>Completa banco, N° de operación, monto y adjunta la imagen o PDF del depósito.</li>
            <li>El voucher quedará <b>en revisión</b>. Finanzas validará el pago y verás aquí el cambio de estado.</li>
            <li>Si necesitas ayuda, contáctanos desde <a onClick={() => navigate('ayuda')} style={{ cursor: 'pointer', color: 'var(--ink-1)', fontWeight: 600 }}>Centro de ayuda</a>.</li>
          </ol>
        </div>
      </main>
    </div>
  );
}

// Catálogo de productos
function Catalogo() {
  const [productos, setProductos] = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);
  const [search,    setSearch]    = React.useState('');
  const [catFil,    setCatFil]    = React.useState('');
  const [showFilters, setShowFilters] = React.useState(false);

  const u = DesasfAPI.getCurrentUser() || {};
  const canManage = DesasfAPI.hasPerm('catalogo.editar');
  const isCliente = u.rol === 'cliente';

  React.useEffect(() => {
    DesasfAPI.get('/catalogo?limit=100')
      .then(r => setProductos(r.rows || []))
      .catch(console.error)
      .finally(() => setLoading(false));
  }, []);

  const colorByCategoria = {
    asfalto_modificado: 'var(--ink-0)',
    cemento_asfaltico:  'var(--status-prod)',
    emulsion:           'var(--signal)',
    slurry:             'var(--hi-vis-deep)',
    reciclado:          'var(--status-ok)',
    aditivo:            'var(--status-review)',
    otro:               'var(--ink-4)',
  };

  const categorias = Array.from(new Set(productos.map(p => p.categoria))).filter(Boolean);
  const catLbl = { asfalto_modificado: 'Asfaltos modif.', cemento_asfaltico: 'Cementos', emulsion: 'Emulsiones', slurry: 'Tratamientos', reciclado: 'Reciclados', aditivo: 'Aditivos', otro: 'Otros' };

  const filtered = productos.filter(p => {
    if (search) {
      const q = search.toLowerCase();
      const hit = p.nombre.toLowerCase().includes(q) ||
                  (p.codigo || '').toLowerCase().includes(q) ||
                  (p.descripcion || '').toLowerCase().includes(q);
      if (!hit) return false;
    }
    if (catFil && p.categoria !== catFil) return false;
    return true;
  });

  return (
    <div className="app">
      <AppSidebar active="catalogo"/>
      <Topbar crumbs={['Información', 'Catálogo de productos']}/>
      <main className="main">
        <div className="page-head">
          <div>
            <h1>Catálogo de productos</h1>
            <p>{productos.length} productos {loading ? '· cargando…' : 'activos'}</p>
          </div>
          <div className="row">
            <button className="btn btn--ghost" onClick={() => exportCSV(
              'tarifa-' + new Date().toISOString().slice(0,10) + '.csv',
              filtered,
              [
                { label: 'Código',       value: 'codigo' },
                { label: 'Nombre',       value: 'nombre' },
                { label: 'Categoría',    value: r => catLbl[r.categoria] || r.categoria },
                { label: 'Especificación', value: r => r.especificacion || '' },
                { label: 'Unidad',       value: 'unidad' },
                { label: 'Precio',       value: r => Number(r.precio_base) },
              ]
            )}>{Icon.download}<span>Exportar tarifa</span></button>
            {canManage && <button className="btn btn--accent" onClick={() => navigate('catalogador')}>{Icon.plus}<span>Gestionar catálogo</span></button>}
          </div>
        </div>

        <div className="filterbar">
          <input
            className="input"
            type="text"
            placeholder="Buscar por nombre, código o descripción…"
            value={search}
            onChange={e => setSearch(e.target.value)}
            style={{ flex: 1, maxWidth: 360, height: 32, fontSize: 13 }}
          />
          <span className="chip">{filtered.length} resultados</span>
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 8, alignItems: 'center' }}>
            <span className={`chip ${showFilters ? 'is-active' : ''}`} style={{ cursor: 'pointer' }}
              onClick={() => setShowFilters(v => !v)}>{Icon.filter} Filtros</span>
          </div>
        </div>
        {showFilters && (
          <div className="card" style={{ borderRadius: 'var(--r-2)', marginBottom: 14 }}>
            <div className="card__body" style={{ display: 'flex', gap: 14, alignItems: 'center', flexWrap: 'wrap' }}>
              <div>
                <label className="label" style={{ marginBottom: 4 }}>Categoría</label>
                <select className="input" style={{ height: 30, padding: '0 8px', fontSize: 12 }}
                  value={catFil} onChange={e => setCatFil(e.target.value)}>
                  <option value="">Todas</option>
                  {categorias.map(c => <option key={c} value={c}>{catLbl[c] || c}</option>)}
                </select>
              </div>
              <button className="btn btn--ghost btn--sm" onClick={() => { setSearch(''); setCatFil(''); }}>Limpiar filtros</button>
            </div>
          </div>
        )}

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14, marginTop: 14 }}>
          {canManage && (
            <div className="card" onClick={() => {
              window._desasfCatalogadorNuevo = true;
              navigate('catalogador');
            }}
              style={{
                cursor: 'pointer',
                border: '2px dashed var(--line-strong, var(--line))',
                background: 'var(--ink-9)',
                display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                minHeight: 250, padding: 18, textAlign: 'center',
                transition: 'background 0.15s, border-color 0.15s',
              }}
              onMouseEnter={e => { e.currentTarget.style.background = 'var(--ink-9, #f3f4f6)'; e.currentTarget.style.borderColor = 'var(--hi-vis)'; }}
              onMouseLeave={e => { e.currentTarget.style.background = 'var(--ink-9)'; e.currentTarget.style.borderColor = 'var(--line-strong, var(--line))'; }}>
              <div style={{ width: 56, height: 56, borderRadius: 12, background: 'var(--hi-vis)', color: 'var(--ink-0)', display: 'grid', placeItems: 'center', marginBottom: 12 }}>
                {Icon.plus}
              </div>
              <div style={{ fontFamily: 'var(--cond)', fontSize: 16, fontWeight: 700, marginBottom: 4 }}>Agregar producto</div>
              <div className="muted" style={{ fontSize: 12, lineHeight: 1.4 }}>Crea un nuevo producto en el catálogo: nombre, categoría, precio, especificación y archivos.</div>
            </div>
          )}
          {filtered.map(p => {
            const col = colorByCategoria[p.categoria] || 'var(--ink-4)';
            const tk  = DesasfAPI.getToken();
            const imgUrl = p.imagen_filename
              ? `/uploads/catalogo/${p.id}/${p.imagen_filename}?token=${encodeURIComponent(tk)}`
              : null;
            return (
              <div className="card" key={p.id} style={{ overflow: 'hidden' }}>
                <div style={{ height: 140, background: col, display: 'grid', placeItems: 'center', position: 'relative', overflow: 'hidden' }}>
                  {imgUrl ? (
                    <img src={imgUrl} alt={p.nombre}
                      style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}/>
                  ) : (
                    <>
                      <svg viewBox="0 0 200 100" width="100%" height="100%" style={{ position: 'absolute', inset: 0, opacity: 0.18 }}>
                        <defs>
                          <pattern id={`pat-${p.id}`} width="20" height="20" patternUnits="userSpaceOnUse">
                            <path d="M0 10 L20 10 M10 0 L10 20" stroke="white" strokeWidth="1"/>
                          </pattern>
                        </defs>
                        <rect width="200" height="100" fill={`url(#pat-${p.id})`}/>
                      </svg>
                      <div style={{ width: 56, height: 56, borderRadius: 8, background: 'rgba(255,255,255,0.12)', backdropFilter: 'blur(4px)', display: 'grid', placeItems: 'center', color: '#fff', position: 'relative', border: '1px solid rgba(255,255,255,0.2)' }}>
                        {Icon.beaker}
                      </div>
                    </>
                  )}
                  {p.tag_especial && (
                    <span style={{ position: 'absolute', top: 8, right: 8, background: '#fff', color: 'var(--ink-1)', fontSize: 10, fontWeight: 700, padding: '3px 7px', borderRadius: 3, letterSpacing: '0.06em', textTransform: 'uppercase', zIndex: 1 }}>{p.tag_especial}</span>
                  )}
                </div>
                <div style={{ padding: 14 }}>
                  <div style={{ fontSize: 10, fontFamily: 'var(--mono)', color: 'var(--ink-5)', marginBottom: 2 }}>{p.codigo}</div>
                  <div style={{ fontWeight: 600, fontSize: 13, marginBottom: 2 }}>{p.nombre}</div>
                  <div className="muted" style={{ fontSize: 11, marginBottom: 10 }}>{p.especificacion || p.descripcion || '—'}</div>
                  <div className="row" style={{ justifyContent: 'space-between', marginBottom: 10 }}>
                    <span style={{ fontFamily: 'var(--mono)', fontSize: 13, fontWeight: 600 }}>S/ {Number(p.precio_base).toLocaleString('es-PE')}/{p.unidad || 'kg'}</span>
                  </div>
                  <div style={{ display: 'grid', gap: 6 }}>
                    <button className="btn btn--ghost btn--sm" style={{ width: '100%', justifyContent: 'center' }}
                      onClick={() => openModal('producto-detalle', { productoId: p.id })}>
                      {Icon.doc}<span>Ver características</span>
                    </button>
                    {canManage && (
                      <button className="btn btn--accent btn--sm" style={{ width: '100%', justifyContent: 'center' }}
                        onClick={() => navigate('catalogador', { productoId: p.id })}>
                        {Icon.settings}<span>Gestionar producto</span>
                      </button>
                    )}
                  </div>
                </div>
              </div>
            );
          })}
          {!loading && filtered.length === 0 && (
            <div className="card" style={{ gridColumn: '1 / -1' }}>
              <div className="card__body" style={{ color: 'var(--ink-5)', fontSize: 13, textAlign: 'center' }}>
                {search ? `Sin resultados para "${search}"` : 'No hay productos disponibles.'}
              </div>
            </div>
          )}
        </div>
      </main>
    </div>
  );
}

// Reportes
function Reportes() {
  const [kpis,      setKpis]      = React.useState(null);
  const [ventas,    setVentas]    = React.useState([]);
  const [clientes,  setClientes]  = React.useState([]);
  const [productos, setProductos] = React.useState([]);
  const [logistica, setLogistica] = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);
  const [error,     setError]     = React.useState('');
  const [meses,     setMeses]     = React.useState(6);

  function fetchAll(m = meses) {
    setLoading(true);
    Promise.all([
      DesasfAPI.get('/reportes/kpis'),
      DesasfAPI.get('/reportes/ventas?meses=' + m),
      DesasfAPI.get('/reportes/top-clientes'),
      DesasfAPI.get('/reportes/top-productos'),
      DesasfAPI.get('/reportes/logistica'),
    ]).then(([k, v, c, p, l]) => {
      setKpis(k); setVentas(v); setClientes(c); setProductos(p); setLogistica(l);
    }).catch(err => setError(err.message))
      .finally(() => setLoading(false));
  }

  React.useEffect(() => { fetchAll(meses); }, [meses]);

  function handleExportar() {
    const sheet = [
      'REPORTE INTEGRAL · ' + new Date().toLocaleDateString('es-PE', { day: '2-digit', month: 'long', year: 'numeric' }),
      '',
      'INDICADORES',
      'Métrica,Valor',
      `Facturado del mes,${kpis?.facturado_mes || 0}`,
      `Volumen mensual (m³),${kpis?.volumen_mes || 0}`,
      `Total solicitudes,${kpis?.total_solicitudes || 0}`,
      `Entregas del mes,${kpis?.entregas_mes || 0}`,
      `Pagos pendientes,${kpis?.pagos_pendientes || 0}`,
      '',
      'VENTAS MENSUALES',
      'Mes,Facturado',
      ...ventas.map(v => `${v.mes},${v.facturado || 0}`),
      '',
      'TOP CLIENTES',
      'Razón social,Facturado',
      ...clientes.map(c => `"${c.razon_social}",${c.facturado || 0}`),
      '',
      'TOP PRODUCTOS',
      'Producto,Volumen,Unidad',
      ...productos.map(p => `"${p.nombre}",${p.volumen_total || 0},${p.unidad || 'm³'}`),
      '',
      'LOGÍSTICA (30 días)',
      'Estado,Despachos,Horas promedio',
      ...logistica.map(l => `${l.estado},${l.total},${Number(l.horas_promedio || 0).toFixed(1)}`),
    ].join('\n');
    const blob = new Blob(['﻿' + sheet], { type: 'text/csv;charset=utf-8' });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement('a');
    a.href = url; a.download = 'reporte-' + new Date().toISOString().slice(0,10) + '.csv';
    document.body.appendChild(a); a.click(); a.remove();
    URL.revokeObjectURL(url);
  }

  function handleProgramar() {
    const email = prompt('Email para recibir el reporte programado:', (DesasfAPI.getCurrentUser() || {}).email || '');
    if (!email) return;
    const frec = prompt('Frecuencia: "diaria", "semanal" o "mensual"', 'mensual');
    if (!frec) return;
    alert(`Reporte ${frec} programado para ${email}.\n(Se enviará desde Configuración → Notificaciones cuando el servicio de envíos esté habilitado.)`);
  }

  function fmt(n) {
    n = Number(n || 0);
    if (n >= 1_000_000) return 'S/ ' + (n / 1_000_000).toFixed(2) + 'M';
    if (n >= 1_000)     return 'S/ ' + (n / 1_000).toFixed(1) + 'k';
    return 'S/ ' + n.toLocaleString('es-PE');
  }

  if (loading) return (
    <div className="app">
      <Sidebar active="reportes"/>
      <Topbar crumbs={['Información','Reportes y analítica']}/>
      <main className="main" style={{ display:'grid', placeItems:'center' }}>
        <div style={{ color:'var(--ink-5)' }}>Cargando reportes…</div>
      </main>
    </div>
  );

  if (error) return (
    <div className="app">
      <Sidebar active="reportes"/>
      <Topbar crumbs={['Información','Reportes y analítica']}/>
      <main className="main" style={{ display:'grid', placeItems:'center' }}>
        <div style={{ color:'var(--status-danger)' }}>Error: {error}</div>
      </main>
    </div>
  );

  const facturadoMes = Number(kpis?.facturado_mes || 0);
  const volumenMes   = Number(kpis?.volumen_mes   || 0);
  const totalSols    = Number(kpis?.total_solicitudes || 0);
  const entregasMes  = Number(kpis?.entregas_mes  || 0);
  const conversion   = totalSols > 0 ? Math.round((entregasMes / totalSols) * 100) : 0;

  const maxFact = Math.max(1, ...ventas.map(v => Number(v.facturado || 0)));
  const maxCli  = Math.max(1, ...clientes.map(c => Number(c.facturado || 0)));

  return (
    <div className="app">
      <Sidebar active="reportes"/>
      <Topbar crumbs={['Información', 'Reportes y analítica']}/>
      <main className="main">
        <div className="page-head">
          <div>
            <h1>Reportes y analítica</h1>
            <p>Indicadores comerciales, operativos y de calidad — periodo: {new Date().toLocaleDateString('es-PE',{month:'long',year:'numeric'})}</p>
          </div>
          <div className="row">
            <select className="input" style={{ height: 32, padding: '0 10px', fontSize: 12 }}
              value={meses} onChange={e => setMeses(Number(e.target.value))}>
              <option value={3}>Últimos 3 meses</option>
              <option value={6}>Últimos 6 meses</option>
              <option value={12}>Último año</option>
              <option value={24}>Últimos 2 años</option>
            </select>
            <button className="btn btn--ghost" onClick={handleExportar}>{Icon.download}<span>Exportar</span></button>
            <button className="btn btn--primary" onClick={handleProgramar}>Programar reporte</button>
          </div>
        </div>

        <div className="kpi-grid">
          <div className="kpi">
            <div className="kpi__label">Facturado del mes</div>
            <div className="kpi__value">{fmt(facturadoMes)}</div>
            <span className="kpi__delta up">{entregasMes} entregas</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, facturadoMes/10000) + '%' }}/></div>
          </div>
          <div className="kpi kpi--accent">
            <div className="kpi__label">Conversión</div>
            <div className="kpi__value">{conversion}<span className="unit">%</span></div>
            <span className="kpi__delta up">entregas / solicitudes</span>
            <div className="kpi__bar"><i style={{ width: conversion + '%' }}/></div>
          </div>
          <div className="kpi kpi--ok">
            <div className="kpi__label">m³ entregados</div>
            <div className="kpi__value">{volumenMes.toLocaleString('es-PE')}</div>
            <span className="kpi__delta up">acumulado del mes</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, volumenMes/10) + '%' }}/></div>
          </div>
          <div className="kpi kpi--review">
            <div className="kpi__label">Pagos por validar</div>
            <div className="kpi__value">{kpis?.pagos_pendientes || 0}</div>
            <span className="kpi__delta">vouchers en bandeja</span>
            <div className="kpi__bar"><i style={{ width: Math.min(100, (kpis?.pagos_pendientes || 0) * 12) + '%' }}/></div>
          </div>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 16 }}>
          <div className="card">
            <div className="card__head"><h3>Ventas mensuales — últimos 6 meses</h3><span className="meta">S/ por mes</span></div>
            <div className="card__body">
              {ventas.length > 0 ? (
                <div className="barchart" style={{ height: 220 }}>
                  {ventas.map(v => {
                    const f = Number(v.facturado || 0);
                    const h = Math.max(8, (f / maxFact) * 100);
                    return (
                      <div className="col-c" key={v.mes}>
                        <div className="stack" style={{ width: 32 }}>
                          <i style={{ height: h + '%', background: 'var(--ink-0)' }}/>
                        </div>
                        <div className="lbl">{v.mes.slice(5)}</div>
                      </div>
                    );
                  })}
                </div>
              ) : (
                <div style={{ padding: 24, color: 'var(--ink-5)', fontSize: 13 }}>Aún no hay ventas en los últimos 6 meses.</div>
              )}
            </div>
          </div>

          <div className="card">
            <div className="card__head"><h3>Top clientes</h3><span className="meta">YTD entregados</span></div>
            <div className="card__body" style={{ padding: 0 }}>
              {clientes.length > 0 ? clientes.map(c => {
                const fact = Number(c.facturado || 0);
                const pct  = Math.round((fact / maxCli) * 100);
                return (
                  <div key={c.id} style={{ padding: '10px 16px', borderBottom: '1px solid var(--line)' }}>
                    <div className="row" style={{ justifyContent: 'space-between', fontSize: 12, marginBottom: 4 }}>
                      <span style={{ fontWeight: 600 }}>{c.razon_social}</span>
                      <span className="num" style={{ color: 'var(--ink-3)' }}>{fmt(fact)}</span>
                    </div>
                    <div style={{ height: 4, background: 'var(--ink-9)', borderRadius: 2 }}>
                      <div style={{ width: pct + '%', height: '100%', background: 'var(--ink-0)', borderRadius: 2 }}/>
                    </div>
                  </div>
                );
              }) : (
                <div style={{ padding: 16, color: 'var(--ink-5)', fontSize: 13 }}>Sin clientes con entregas este año.</div>
              )}
            </div>
          </div>
        </div>

        <div className="sect-h"><h2>Top productos · Estado logística</h2></div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
          <div className="card">
            <div className="card__head"><h3>Top productos por volumen</h3></div>
            <div className="card__body" style={{ padding: 0 }}>
              {productos.length > 0 ? productos.map(p => (
                <div key={p.id} style={{ padding: '10px 16px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
                  <span style={{ fontWeight: 600 }}>{p.nombre}</span>
                  <span className="num">{Number(p.volumen_total || 0).toLocaleString('es-PE')} {p.unidad || 'm³'}</span>
                </div>
              )) : <div style={{ padding: 16, color: 'var(--ink-5)', fontSize: 13 }}>Sin datos.</div>}
            </div>
          </div>

          <div className="card">
            <div className="card__head"><h3>Estado de logística (30 días)</h3></div>
            <div className="card__body" style={{ padding: 0 }}>
              {logistica.length > 0 ? logistica.map(l => (
                <div key={l.estado} style={{ padding: '10px 16px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: 12 }}>
                  <span style={{ fontWeight: 600, textTransform: 'capitalize' }}>{l.estado.replace('_',' ')}</span>
                  <span>
                    <span className="num" style={{ marginRight: 8 }}>{l.total} despacho{l.total === 1 ? '' : 's'}</span>
                    <span className="muted" style={{ fontSize: 11 }}>~{Number(l.horas_promedio || 0).toFixed(1)}h prom.</span>
                  </span>
                </div>
              )) : <div style={{ padding: 16, color: 'var(--ink-5)', fontSize: 13 }}>Sin despachos en 30 días.</div>}
            </div>
          </div>
        </div>
      </main>
    </div>
  );
}

window.Pagos = Pagos;
window.Catalogo = Catalogo;
window.Reportes = Reportes;
