import { Icon } from "@iconify/react";
import { useCallback, useEffect, useState } from "react";
import { toast } from "sonner";
import styled, { keyframes } from "styled-components";
import {
  createPromesaPago,
  getCuotasCobrador,
  getHistorialClienteCobrador,
  getPromesasPorFactura,
  getResumenCobrador,
  updateEstadoPromesa,
} from "../api/cobranzas.service";
import { getDetalleClientePanelCobrador, getReciboPdf } from "../api/cobros.service";
import { AccesoRestringido } from "../components/common/AccesoRestringido";
import { RegistrarGestionModal } from "../components/cobranzas/gestiones/RegistrarGestionModal";
import { usePermission } from "../hooks/usePermission";
import { useSubmodulo } from "../hooks/useSubmodulo";
import { Device } from "../styles/breakpoints";
import { hoyISO } from "../utils/fecha";

// ─── Helpers ─────────────────────────────────────────────────────────────────
const fmtGs = (n, moneda) => {
  const num = Number(n || 0);
  const simbolo = moneda?.simbolo || moneda?.codigo || "Gs.";
  if (!moneda || moneda?.codigo === "PYG" || !moneda?.codigo) {
    return `Gs. ${num.toLocaleString("es-PY", { maximumFractionDigits: 0 })}`;
  }
  return `${simbolo} ${num.toLocaleString("es-PY", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
};

const fmtFecha = (str) => {
  if (!str) return "—";
  try {
    return new Date(str.includes("T") ? str : str + "T12:00:00").toLocaleDateString("es-PY", {
      day: "2-digit", month: "2-digit", year: "numeric",
    });
  } catch { return str; }
};

const urgenciaConfig = {
  critica:  { color: "#fca5a5", bg: "#ef444420", label: "Crítica",   icon: "lucide:alert-triangle" },
  vencida:  { color: "#f87171", bg: "#ef444415", label: "Vencida",   icon: "lucide:clock" },
  hoy:      { color: "#fbbf24", bg: "#f59e0b18", label: "Vence hoy", icon: "lucide:zap" },
  proxima:  { color: "#4ade80", bg: "#22c55e15", label: "Al día",    icon: "lucide:check-circle" },
};

const TODAY = hoyISO();

// Chips: solo definen fecha_hasta. Las vencidas anteriores siempre se incluyen (backend).
const calcDateRange = (chip) => {
  const d = new Date();
  d.setHours(0, 0, 0, 0);
  const fmt = (x) => x.toISOString().split("T")[0];
  if (chip === "hoy")    return { hasta: fmt(d) };
  if (chip === "manana") { const t = new Date(d); t.setDate(t.getDate() + 1); return { hasta: fmt(t) }; }
  if (chip === "semana") { const t = new Date(d); t.setDate(t.getDate() + 6); return { hasta: fmt(t) }; }
  if (chip === "15d")    { const t = new Date(d); t.setDate(t.getDate() + 14); return { hasta: fmt(t) }; }
  if (chip === "30d")    { const t = new Date(d); t.setDate(t.getDate() + 29); return { hasta: fmt(t) }; }
  return { hasta: null };
};

// ─── Componente Principal ─────────────────────────────────────────────────────
export function PanelCobrador() {
  const { hasAccess: tieneModuloMora } = useSubmodulo("ADM_INTERES_MORA");
  const { can: canCobranzas } = usePermission("COBRANZAS");
  const puedeVerPanel = canCobranzas("COB_CBR_PANEL_COBRADOR_VER");
  const puedeRegistrarGestion = canCobranzas("COB_WFL_GESTION_REGISTRAR");
  const [gestionTarget, setGestionTarget] = useState(null); // { cliente, factura? }

  const [resumen, setResumen] = useState(null);
  const [cuotas, setCuotas] = useState([]);
  const [loading, setLoading] = useState(false);
  // Paginación por CLIENTE (no por cuota)
  const [clientePage, setClientePage] = useState(1);
  const CLIENT_PAGE_SIZE = 10;
  const [filtroEstado, setFiltroEstado] = useState("todas");
  const [busqueda, setBusqueda] = useState("");
  const [busquedaDebounced, setBusquedaDebounced] = useState("");
  useEffect(() => {
    const t = setTimeout(() => setBusquedaDebounced(busqueda.trim()), 400);
    return () => clearTimeout(t);
  }, [busqueda]);

  // Chips de fecha rápida
  const [chipFecha, setChipFecha] = useState(null); // null | "hoy" | "manana" | "semana" | "15d" | "30d"

  // Rango de fecha manual (cuando no hay chip activo)
  const [fechaDesdeManual, setFechaDesdeManual] = useState("");
  const [fechaHastaManual, setFechaHastaManual] = useState("");

  // Toggle ver todas las cuotas de la empresa
  const [verTodas, setVerTodas] = useState(false);

  // Promesas de pago
  const [modalPromesa, setModalPromesa] = useState(null);
  const [fechaPromesa, setFechaPromesa] = useState("");
  const [montoPromesa, setMontoPromesa] = useState("");
  const [notasPromesa, setNotasPromesa] = useState("");
  const [guardandoPromesa, setGuardandoPromesa] = useState(false);
  const [promesasFactura, setPromesasFactura] = useState([]);
  const [loadingPromesas, setLoadingPromesas] = useState(false);

  // Historial cliente (solo lectura)
  const [historialCliente, setHistorialCliente] = useState(null); // { cliente, recibos, page, totalPages }
  const [loadingHistorial, setLoadingHistorial] = useState(false);
  const [historialPage, setHistorialPage] = useState(1);

  // PDF preview
  const [loadingPdf, setLoadingPdf] = useState(null);
  const [pdfPreview, setPdfPreview] = useState(null);

  // ── Carga de datos ──────────────────────────────────────────────────────────
  const cargarResumen = useCallback(async () => {
    try {
      const data = await getResumenCobrador({ ver_todas: verTodas ? "true" : undefined });
      setResumen(data);
    } catch { /* silencioso */ }
  }, [verTodas]);

  const cargarCuotas = useCallback(async () => {
    setLoading(true);
    try {
      const tieneBusqueda = busquedaDebounced.length > 0;
      const range = chipFecha ? calcDateRange(chipFecha) : {};
      const params = tieneBusqueda
        ? {
            page: 1,
            pageSize: 500,
            ver_todas: verTodas ? "true" : undefined,
            search: busquedaDebounced,
          }
        : {
            page: 1,
            pageSize: 500, // traer todo — paginamos por cliente en frontend
            estado: filtroEstado,
            ver_todas: verTodas ? "true" : undefined,
            ...(range.hasta && { fecha_hasta: range.hasta }),
            ...(!chipFecha && fechaDesdeManual && { fecha_desde: fechaDesdeManual }),
            ...(!chipFecha && fechaHastaManual && { fecha_hasta: fechaHastaManual }),
          };
      const data = await getCuotasCobrador(params);
      setCuotas(data.data || []);
      setClientePage(1); // reset al recargar
    } catch {
      toast.error("Error al cargar cuotas");
    } finally {
      setLoading(false);
    }
  }, [filtroEstado, chipFecha, verTodas, fechaDesdeManual, fechaHastaManual, busquedaDebounced]);

  useEffect(() => { cargarResumen(); }, [cargarResumen]);
  useEffect(() => { cargarCuotas(); }, [cargarCuotas]);

  // ── Promesas ────────────────────────────────────────────────────────────────
  const abrirModalPromesa = async (cuota) => {
    setModalPromesa(cuota);
    setFechaPromesa("");
    setMontoPromesa(String(Math.round(Number(cuota.monto_cuota || 0))));
    setNotasPromesa("");
    setPromesasFactura([]);
    setLoadingPromesas(true);
    try {
      const data = await getPromesasPorFactura(cuota.factura_id);
      setPromesasFactura(Array.isArray(data) ? data : []);
    } catch { /* silencioso */ }
    finally { setLoadingPromesas(false); }
  };

  const guardarPromesa = async () => {
    if (!fechaPromesa) { toast.error("Ingrese la fecha prometida"); return; }
    setGuardandoPromesa(true);
    try {
      await createPromesaPago({
        factura_cab_id: modalPromesa.factura_id,
        cliente_id: modalPromesa.factura?.clientes?.id,
        cuota_id: modalPromesa.id,
        fecha_prometida: fechaPromesa,
        monto_prometido: montoPromesa ? Number(montoPromesa) : undefined,
        notas: notasPromesa || undefined,
      });
      toast.success("Promesa de pago registrada");
      setModalPromesa(null);
      cargarCuotas();
    } catch (err) {
      toast.error(err?.response?.data?.message || "Error al registrar la promesa");
    } finally { setGuardandoPromesa(false); }
  };

  const marcarPromesa = async (promesaId, estado) => {
    try {
      await updateEstadoPromesa(promesaId, { estado });
      toast.success(`Promesa marcada como ${estado}`);
      setPromesasFactura((prev) => prev.map((p) => (p.id === promesaId ? { ...p, estado } : p)));
    } catch (err) {
      toast.error(err?.response?.data?.message || "Error al actualizar la promesa");
    }
  };

  // ── Historial cliente ───────────────────────────────────────────────────────
  const abrirHistorial = async (cliente, pg = 1) => {
    setLoadingHistorial(true);
    setHistorialPage(pg);
    try {
      const data = await getHistorialClienteCobrador(cliente.id, pg);
      setHistorialCliente({ cliente, recibos: data.data || [], totalPages: data.totalPages || 1, page: pg });
    } catch {
      toast.error("Error al cargar historial del cliente");
    } finally { setLoadingHistorial(false); }
  };

  const cambiarHistorialPage = async (pg) => {
    if (!historialCliente?.cliente) return;
    await abrirHistorial(historialCliente.cliente, pg);
  };

  // ── PDF ─────────────────────────────────────────────────────────────────────
  const handlePreviewPdf = async (reciboId, formato = "a4") => {
    setLoadingPdf(reciboId + formato);
    try {
      const blob = await getReciboPdf(reciboId, { formato });
      const url = URL.createObjectURL(blob);
      setPdfPreview({ url, formato, reciboId });
    } catch {
      toast.error("Error al obtener el PDF");
    } finally { setLoadingPdf(null); }
  };

  const cerrarPdf = () => {
    if (pdfPreview?.url) URL.revokeObjectURL(pdfPreview.url);
    setPdfPreview(null);
  };

  // Expand/collapse clientes en la lista agrupada
  const [expandidos, setExpandidos] = useState({});
  const [detallesCliente, setDetallesCliente] = useState({});
  const toggleExpandir = (clienteId) => {
    setExpandidos((prev) => {
      const next = !prev[clienteId];
      if (next && !detallesCliente[clienteId]) {
        getDetalleClientePanelCobrador(clienteId)
          .then((data) => setDetallesCliente((d) => ({ ...d, [clienteId]: data })))
          .catch(() => {});
      }
      return { ...prev, [clienteId]: next };
    });
  };

  // Backend ya devuelve resultados filtrados cuando hay búsqueda; no filtramos localmente
  const cuotasFiltradas = cuotas;

  // ── Agrupar cuotas por cliente → factura ───────────────────────────────────
  const urgOrder = { critica: 4, vencida: 3, hoy: 2, proxima: 1 };
  const clientesMap = {};
  cuotasFiltradas.forEach((cuota) => {
    const clienteId = cuota.factura?.clientes?.id;
    if (!clienteId) return;
    if (!clientesMap[clienteId]) {
      clientesMap[clienteId] = {
        cliente: cuota.factura.clientes,
        facturas: {},
        urgencia_max: cuota.urgencia,
        dias_vencido_max: cuota.dias_vencido,
        total_cuotas: 0,
        total_monto: 0,
        total_mora: 0,
      };
    }
    const entry = clientesMap[clienteId];
    if ((urgOrder[cuota.urgencia] || 0) > (urgOrder[entry.urgencia_max] || 0)) {
      entry.urgencia_max = cuota.urgencia;
      entry.dias_vencido_max = cuota.dias_vencido;
    }
    entry.total_cuotas += 1;
    entry.total_monto += Number(cuota.dmoncuota || 0);
    entry.total_mora += Number(cuota.mora_estimada || 0);
    const facturaId = cuota.factura_id;
    if (!entry.facturas[facturaId]) {
      entry.facturas[facturaId] = {
        id: facturaId,
        dnumdoc: cuota.factura.dnumdoc,
        dest: cuota.factura.dest,
        dpunexp: cuota.factura.dpunexp,
        total_factura: cuota.factura.total_factura,
        saldo_pendiente: cuota.factura.saldo_pendiente,
        cuotas: [],
      };
    }
    entry.facturas[facturaId].cuotas.push(cuota);
  });
  const clientesAgrupados = Object.values(clientesMap);

  // Paginación por cliente (frontend)
  const totalClientes = clientesAgrupados.length;
  const totalClientePages = Math.max(1, Math.ceil(totalClientes / CLIENT_PAGE_SIZE));
  const clientesEnPagina = clientesAgrupados.slice(
    (clientePage - 1) * CLIENT_PAGE_SIZE,
    clientePage * CLIENT_PAGE_SIZE,
  );

  const dateChips = [
    { v: "hoy",    label: "Hoy" },
    { v: "manana", label: "Mañana" },
    { v: "semana", label: "Semana" },
    { v: "15d",    label: "15 días" },
    { v: "30d",    label: "30 días" },
  ];

  // ─────────────────────────────────────────────────────────────────────────────
  if (!puedeVerPanel) return <AccesoRestringido modulo="COBRANZAS" />;

  return (
    <PageWrap>
      {/* Cabecera */}
      <Header>
        <HeaderLeft>
          <Icon icon="lucide:briefcase" width={22} />
          <div>
            <h1>Panel Cobrador</h1>
            {resumen?.cobrador && (
              <span>{resumen.cobrador.nombre} {resumen.cobrador.apellido}</span>
            )}
          </div>
        </HeaderLeft>
        <RefreshBtn onClick={() => { setClientePage(1); cargarResumen(); cargarCuotas(); }}>
          <Icon icon="lucide:refresh-cw" />
        </RefreshBtn>
      </Header>

      {/* KPIs */}
      {resumen?.resumen && (
        <KpiGrid>
          <KpiCard $color="#ef4444">
            <KpiNum>{resumen.resumen.vencidas}</KpiNum>
            <KpiLabel>Vencidas</KpiLabel>
          </KpiCard>
          <KpiCard $color="#f59e0b">
            <KpiNum>{resumen.resumen.proximas_7_dias}</KpiNum>
            <KpiLabel>7 días</KpiLabel>
          </KpiCard>
          <KpiCard $color="#6b7280">
            <KpiNum>{resumen.resumen.total_pendientes}</KpiNum>
            <KpiLabel>Pendientes</KpiLabel>
          </KpiCard>
        </KpiGrid>
      )}

      {/* Chips de fecha rápida */}
      <DateChipRow>
        <DateChipLabel>Vencimiento:</DateChipLabel>
        {dateChips.map(({ v, label }) => (
          <DateChip
            key={v}
            $active={chipFecha === v}
            onClick={() => { setChipFecha(chipFecha === v ? null : v); setClientePage(1); }}
          >
            {label}
          </DateChip>
        ))}
      </DateChipRow>

      {/* Rango manual — visible cuando no hay chip activo */}
      {!chipFecha && (
        <DateRangeRow>
          <DateRangeLabel>Desde</DateRangeLabel>
          <DateInput
            type="date"
            value={fechaDesdeManual}
            onChange={(e) => { setFechaDesdeManual(e.target.value); setClientePage(1); }}
          />
          <DateRangeLabel>Hasta</DateRangeLabel>
          <DateInput
            type="date"
            value={fechaHastaManual}
            onChange={(e) => { setFechaHastaManual(e.target.value); setClientePage(1); }}
          />
          {(fechaDesdeManual || fechaHastaManual) && (
            <ClearRangeBtn onClick={() => { setFechaDesdeManual(""); setFechaHastaManual(""); setClientePage(1); }}>
              <Icon icon="lucide:x" width={12} />
            </ClearRangeBtn>
          )}
        </DateRangeRow>
      )}

      {/* Filtros */}
      <FilterBar>
        <SearchInput
          placeholder="Buscar cliente o factura..."
          value={busqueda}
          onChange={(e) => { setBusqueda(e.target.value); setClientePage(1); }}
        />
        <FilterTabs>
          {[
            { v: "todas",    label: "Todas" },
            { v: "vencida",  label: "Vencidas" },
            { v: "pendiente",label: "Al día" },
            { v: "cobrado",  label: "Cobrado" },
          ].map(({ v, label }) => (
            <FilterTab
              key={v}
              $active={filtroEstado === v}
              $cobrado={v === "cobrado"}
              onClick={() => { setFiltroEstado(v); setClientePage(1); }}
            >
              {label}
            </FilterTab>
          ))}
        </FilterTabs>
      </FilterBar>

      {/* Toggle Ver todas */}
      <VerTodasRow>
        <VerTodasToggle $active={verTodas} onClick={() => { setVerTodas(!verTodas); setClientePage(1); }}>
          <Icon icon={verTodas ? "lucide:users" : "lucide:user"} width={13} />
          {verTodas ? "Viendo todas las cuotas" : "Solo mis asignadas"}
        </VerTodasToggle>
      </VerTodasRow>

      {/* Lista agrupada por cliente */}
      {loading ? (
        <LoadingMsg>
          <Icon icon="lucide:loader-circle" className="spin" />
          Cargando...
        </LoadingMsg>
      ) : clientesAgrupados.length === 0 ? (
        <EmptyMsg>
          <Icon icon={filtroEstado === "cobrado" ? "lucide:check-circle-2" : "lucide:inbox"} width={40} />
          <p>{filtroEstado === "cobrado" ? "Sin cobros en este rango" : "No hay cuotas pendientes"}</p>
        </EmptyMsg>
      ) : (
        <CuotasList>
          {clientesEnPagina.map(({ cliente, facturas, urgencia_max, dias_vencido_max, total_cuotas, total_monto, total_mora }) => {
            const cfg = urgenciaConfig[urgencia_max] || urgenciaConfig.proxima;
            const nombreCliente = cliente?.nombre_fantasia || cliente?.personas?.razon_social || "—";
            const telefono = cliente?.personas?.celular || cliente?.personas?.telefono;
            const esCobrado = filtroEstado === "cobrado";
            const expandido = expandidos[cliente.id] === true; // default colapsado
            const facturasArr = Object.values(facturas);

            // Próxima cuota por FACTURA (visible sin expandir) — 1 box por factura
            // Ordenar facturas por urgencia de su cuota más crítica
            const facturasConProxima = facturasArr.map((f) => {
              const cuotasOrdenadas = [...f.cuotas].sort((a, b) => {
                const d = (urgOrder[b.urgencia] || 0) - (urgOrder[a.urgencia] || 0);
                if (d !== 0) return d;
                return new Date(a.fecha_vencimiento) - new Date(b.fecha_vencimiento);
              });
              return {
                factura: { ...f, clientes: cliente },
                proxima: cuotasOrdenadas[0],
                resto: cuotasOrdenadas.slice(1),
              };
            }).sort((a, b) => {
              const ua = urgOrder[a.proxima?.urgencia] || 0;
              const ub = urgOrder[b.proxima?.urgencia] || 0;
              return ub - ua;
            });
            const totalResto = facturasConProxima.reduce((s, f) => s + f.resto.length, 0);

            return (
              <ClienteCard key={cliente.id} $urgencia={urgencia_max} $cobrado={esCobrado}>
                {/* Cabecera: nombre + contador + toggle */}
                <ClienteHeader onClick={() => toggleExpandir(cliente.id)}>
                  <ClienteHeaderLeft>
                    {!esCobrado ? (
                      <UrgenciaBadge $color={cfg.color} $bg={cfg.bg}>
                        <Icon icon={cfg.icon} width={11} />
                        {cfg.label}
                        {dias_vencido_max > 0 && ` · ${dias_vencido_max}d`}
                      </UrgenciaBadge>
                    ) : (
                      <UrgenciaBadge $color="#4ade80" $bg="#22c55e15">
                        <Icon icon="lucide:check-circle" width={11} />
                        Cobrado
                      </UrgenciaBadge>
                    )}
                    <ClienteNombreH>{nombreCliente}</ClienteNombreH>
                  </ClienteHeaderLeft>
                  <ClienteHeaderRight>
                    {totalResto > 0 && (
                      <ClienteSummary>+{totalResto} más</ClienteSummary>
                    )}
                    <Icon icon={expandido ? "lucide:chevron-up" : "lucide:chevron-down"} width={16} />
                  </ClienteHeaderRight>
                </ClienteHeader>

                {/* Contacto siempre visible */}
                <ContactRow>
                  {cliente?.personas?.direccion && (
                    <ContactItem>
                      <Icon icon="lucide:map-pin" width={11} />
                      {cliente.personas.direccion}
                    </ContactItem>
                  )}
                  {cliente?.referencia_domicilio && (
                    <ContactItem $ref>
                      <Icon icon="lucide:milestone" width={11} />
                      {cliente.referencia_domicilio}
                    </ContactItem>
                  )}
                  {telefono && (
                    <ContactLink href={`tel:${telefono}`}>
                      <Icon icon="lucide:phone" width={11} />
                      {telefono}
                    </ContactLink>
                  )}
                </ContactRow>

                {/* Una NextCuotaBox por factura (siempre visible) */}
                {facturasConProxima.map(({ factura: fac, proxima: prx }) => prx && (
                  <NextCuotaBox key={fac.id} $urgencia={prx.urgencia} $cobrado={esCobrado}>
                    <NextCuotaLeft>
                      <NextCuotaNum>C.{prx.numero_cuota}</NextCuotaNum>
                      <NextCuotaInfo>
                        <span>Fac. {fac.dpunexp}-{fac.dnumdoc}</span>
                        <NextCuotaFecha>
                          <Icon icon="lucide:calendar" width={11} />
                          {fmtFecha(prx.fecha_vencimiento)}
                        </NextCuotaFecha>
                        {!esCobrado && prx.dias_vencido > 0 && (
                          <CuotaRowVenc $critica={prx.urgencia === "critica"}>
                            {prx.dias_vencido}d vencida
                          </CuotaRowVenc>
                        )}
                      </NextCuotaInfo>
                    </NextCuotaLeft>
                    <NextCuotaRight>
                      <NextCuotaMonto>{fmtGs(prx.dmoncuota)}</NextCuotaMonto>
                      {tieneModuloMora && !esCobrado && prx.mora_estimada > 0 && (
                        <MoraBadge>+{fmtGs(prx.mora_estimada)}</MoraBadge>
                      )}
                      {!esCobrado && (
                        <CuotaPromesaBtn onClick={(e) => { e.stopPropagation(); abrirModalPromesa({ ...prx, factura: fac }); }}>
                          <Icon icon="lucide:calendar-plus" width={12} />
                        </CuotaPromesaBtn>
                      )}
                    </NextCuotaRight>
                  </NextCuotaBox>
                ))}

                {/* Resto de cuotas por factura — expandible */}
                {expandido && totalResto > 0 && (
                  <FacturasWrap>
                    {facturasConProxima.map(({ factura: fac, resto }) => {
                      if (resto.length === 0) return null;
                      return (
                        <FacturaGroup key={fac.id}>
                          <FacturaLabel>
                            <Icon icon="lucide:file-text" width={12} />
                            Fac. {fac.dpunexp}-{fac.dnumdoc}
                            {fac.saldo_pendiente > 0 && (
                              <FacturaSaldo>saldo {fmtGs(fac.saldo_pendiente)}</FacturaSaldo>
                            )}
                          </FacturaLabel>
                          {resto.map((cuota) => (
                            <CuotaRow key={cuota.id} $urgencia={cuota.urgencia} $cobrado={esCobrado}>
                              <CuotaRowLeft>
                                <CuotaRowNum>C.{cuota.numero_cuota}</CuotaRowNum>
                                <CuotaRowFecha>{fmtFecha(cuota.fecha_vencimiento)}</CuotaRowFecha>
                                {!esCobrado && cuota.dias_vencido > 0 && (
                                  <CuotaRowVenc $critica={cuota.urgencia === "critica"}>
                                    {cuota.dias_vencido}d
                                  </CuotaRowVenc>
                                )}
                              </CuotaRowLeft>
                              <CuotaRowRight>
                                <CuotaRowMonto>{fmtGs(cuota.dmoncuota)}</CuotaRowMonto>
                                {tieneModuloMora && !esCobrado && cuota.mora_estimada > 0 && (
                                  <MoraBadge>+{fmtGs(cuota.mora_estimada)}</MoraBadge>
                                )}
                                {!esCobrado && (
                                  <CuotaPromesaBtn onClick={(e) => { e.stopPropagation(); abrirModalPromesa({ ...cuota, factura: fac }); }}>
                                    <Icon icon="lucide:calendar-plus" width={12} />
                                  </CuotaPromesaBtn>
                                )}
                              </CuotaRowRight>
                            </CuotaRow>
                          ))}
                        </FacturaGroup>
                      );
                    })}
                  </FacturasWrap>
                )}

                {/* Detalle expandido: productos, promesa, día cobro */}
                {expandido && (() => {
                  const det = detallesCliente[cliente.id];
                  if (!det) return null;
                  return (
                    <DetallePanel>
                      {det.dia_cobro && (
                        <DetalleItem>
                          <Icon icon="lucide:calendar-clock" width={12} />
                          <span>Cobro: <strong>{det.dia_cobro}</strong></span>
                        </DetalleItem>
                      )}
                      {det.ultima_promesa && (
                        <DetalleItem $promesa>
                          <Icon icon="lucide:handshake" width={12} />
                          <span>
                            Promesa {det.ultima_promesa.fecha} — {fmtGs(det.ultima_promesa.monto)}
                            {det.ultima_promesa.notas && ` · "${det.ultima_promesa.notas}"`}
                          </span>
                        </DetalleItem>
                      )}
                      {det.productos?.length > 0 && (
                        <DetalleProductos>
                          {det.productos.map((p, i) => (
                            <DetalleProductoChip key={i}>
                              {p.descripcion} ×{p.cantidad}
                            </DetalleProductoChip>
                          ))}
                        </DetalleProductos>
                      )}
                    </DetallePanel>
                  );
                })()}

                {/* Totales + acciones */}
                <ClienteFooter>
                  {!esCobrado && (
                    <ClienteTotales>
                      <span>Total: <strong>{fmtGs(total_monto)}</strong></span>
                      {tieneModuloMora && total_mora > 0 && (
                        <span style={{ color: "#fbbf24" }}>+mora {fmtGs(total_mora)}</span>
                      )}
                    </ClienteTotales>
                  )}
                  <ClienteAcciones>
                    <AccionBtn $secondary onClick={() => abrirHistorial(cliente)}>
                      <Icon icon="lucide:history" width={13} />
                      Historial
                    </AccionBtn>
                    {puedeRegistrarGestion && (
                      <AccionBtn
                        $secondary
                        onClick={() => setGestionTarget({ cliente, factura: facturasConProxima[0]?.factura ?? null })}
                      >
                        <Icon icon="lucide:clipboard-check" width={13} />
                        Gestión
                      </AccionBtn>
                    )}
                    {telefono && (
                      <AccionBtn
                        as="a"
                        href={`https://wa.me/${telefono.replace(/\D/g, "")}?text=${encodeURIComponent(
                          `Hola ${nombreCliente}, le recordamos que tiene ${total_cuotas} cuota${total_cuotas !== 1 ? "s" : ""} pendiente${total_cuotas !== 1 ? "s" : ""} por ${fmtGs(total_monto)}. Gracias.`
                        )}`}
                        target="_blank"
                        $wa
                      >
                        <Icon icon="logos:whatsapp-icon" width={13} />
                        WhatsApp
                      </AccionBtn>
                    )}
                  </ClienteAcciones>
                </ClienteFooter>
              </ClienteCard>
            );
          })}
        </CuotasList>
      )}

      {/* Paginación por cliente */}
      {totalClientePages > 1 && (
        <Pagination>
          <PageBtn disabled={clientePage === 1} onClick={() => setClientePage((p) => p - 1)}>
            <Icon icon="lucide:chevron-left" />
          </PageBtn>
          <PageInfo>
            {(clientePage - 1) * CLIENT_PAGE_SIZE + 1}–{Math.min(clientePage * CLIENT_PAGE_SIZE, totalClientes)} de {totalClientes} clientes
          </PageInfo>
          <PageBtn disabled={clientePage === totalClientePages} onClick={() => setClientePage((p) => p + 1)}>
            <Icon icon="lucide:chevron-right" />
          </PageBtn>
        </Pagination>
      )}

      {/* ── Drawer historial cliente (solo lectura) ── */}
      {(historialCliente || loadingHistorial) && (
        <DrawerOverlay onClick={() => { setHistorialCliente(null); }}>
          <DrawerPanel onClick={(e) => e.stopPropagation()}>
            <DrawerHeader>
              <DrawerTitle>
                <Icon icon="lucide:history" />
                Historial de Cobros
              </DrawerTitle>
              <DrawerSub>
                {historialCliente?.cliente?.nombre_fantasia ||
                  historialCliente?.cliente?.personas?.razon_social || "Cliente"}
              </DrawerSub>
              <DrawerClose onClick={() => setHistorialCliente(null)}>
                <Icon icon="lucide:x" />
              </DrawerClose>
            </DrawerHeader>

            <DrawerBody>
              {loadingHistorial ? (
                <LoadingMsg style={{ padding: "32px 0" }}>
                  <Icon icon="lucide:loader-circle" className="spin" />
                  Cargando historial...
                </LoadingMsg>
              ) : historialCliente?.recibos?.length === 0 ? (
                <EmptyMsg>
                  <Icon icon="lucide:inbox" width={36} />
                  <p>Sin cobros registrados</p>
                </EmptyMsg>
              ) : (
                historialCliente?.recibos?.map((recibo) => (
                  <ReciboCard key={recibo.id} $anulado={recibo.estado === "anulado"}>
                    <ReciboCardHeader>
                      <ReciboNum>Rec. #{recibo.numero_recibo}</ReciboNum>
                      <ReciboEstado $estado={recibo.estado}>{recibo.estado}</ReciboEstado>
                    </ReciboCardHeader>
                    <ReciboFecha>{fmtFecha(recibo.fecha_emision)}</ReciboFecha>
                    <ReciboMontos>
                      <span>Total: <strong>{fmtGs(recibo.monto_total, recibo.moneda)}</strong></span>
                      {Number(recibo.descuento_monto) > 0 && (
                        <span style={{ color: "#ef4444" }}>Desc: -{fmtGs(recibo.descuento_monto, recibo.moneda)}</span>
                      )}
                      {tieneModuloMora && Number(recibo.mora_total) > 0 && (
                        <span style={{ color: "#f59e0b" }}>Mora: +{fmtGs(recibo.mora_total, recibo.moneda)}</span>
                      )}
                    </ReciboMontos>

                    {/* Detalle de cuotas */}
                    {recibo.recibo_cobro_detalle?.length > 0 && (
                      <CuotasDetalle>
                        {recibo.recibo_cobro_detalle.map((d, idx) => (
                          <CuotaDetalleItem key={idx}>
                            <CuotaDetalleLeft>
                              <span>
                                {d.factura_cuotas?.factura_cab?.dpunexp}-{d.factura_cuotas?.factura_cab?.dnumdoc}
                                {" · "}C.{d.factura_cuotas?.nro_cuota}
                              </span>
                              <span style={{ color: "#6b7280", fontSize: 11 }}>
                                Venc: {fmtFecha(d.factura_cuotas?.dvenccuo)}
                              </span>
                            </CuotaDetalleLeft>
                            <CuotaDetalleRight>
                              <strong>{fmtGs(d.monto_pagado, recibo.moneda)}</strong>
                              {d.descuento_aplicado > 0 && (
                                <span style={{ color: "#ef4444", fontSize: 11 }}>
                                  -desc {fmtGs(d.descuento_aplicado, recibo.moneda)}
                                </span>
                              )}
                              {tieneModuloMora && Number(d.mora_monto) > 0 && (
                                <span style={{ color: "#f59e0b", fontSize: 11 }}>
                                  +mora {fmtGs(d.mora_monto, recibo.moneda)}
                                </span>
                              )}
                              {d.es_pago_parcial && (
                                <ParcialBadge>Parcial</ParcialBadge>
                              )}
                            </CuotaDetalleRight>
                          </CuotaDetalleItem>
                        ))}
                      </CuotasDetalle>
                    )}

                    {/* Solo imprimir/ver — SIN anular ni modificar fecha */}
                    {recibo.estado !== "anulado" && (
                      <ReciboPrintRow>
                        <PrintBtn
                          title="Ver A4"
                          disabled={loadingPdf === recibo.id + "a4"}
                          onClick={() => handlePreviewPdf(recibo.id, "a4")}
                        >
                          <Icon icon="lucide:file-text" width={14} />
                          A4
                        </PrintBtn>
                        <PrintBtn
                          title="Ver Ticket"
                          disabled={loadingPdf === recibo.id + "ticket"}
                          onClick={() => handlePreviewPdf(recibo.id, "ticket")}
                        >
                          <Icon icon="lucide:receipt" width={14} />
                          Ticket
                        </PrintBtn>
                      </ReciboPrintRow>
                    )}
                  </ReciboCard>
                ))
              )}
            </DrawerBody>

            {/* Paginación del historial */}
            {(historialCliente?.totalPages || 0) > 1 && (
              <DrawerFooter>
                <PageBtn
                  disabled={historialPage === 1}
                  onClick={() => cambiarHistorialPage(historialPage - 1)}
                >
                  <Icon icon="lucide:chevron-left" />
                </PageBtn>
                <PageInfo>Página {historialPage} de {historialCliente.totalPages}</PageInfo>
                <PageBtn
                  disabled={historialPage === historialCliente.totalPages}
                  onClick={() => cambiarHistorialPage(historialPage + 1)}
                >
                  <Icon icon="lucide:chevron-right" />
                </PageBtn>
              </DrawerFooter>
            )}
          </DrawerPanel>
        </DrawerOverlay>
      )}

      {/* Modal PDF preview */}
      {pdfPreview && (
        <DrawerOverlay onClick={cerrarPdf}>
          <PdfModal onClick={(e) => e.stopPropagation()}>
            <DrawerHeader>
              <DrawerTitle>
                <Icon icon={pdfPreview.formato === "ticket" ? "lucide:receipt" : "lucide:file-text"} />
                Recibo — {pdfPreview.formato === "ticket" ? "Ticket" : "A4"}
              </DrawerTitle>
              <DrawerClose onClick={cerrarPdf}><Icon icon="lucide:x" /></DrawerClose>
            </DrawerHeader>
            <iframe
              src={pdfPreview.url}
              style={{ flex: 1, border: "none", width: "100%", height: "100%", minHeight: 0 }}
              title="Recibo"
            />
            <DrawerFooter>
              <PrintBtn
                style={{ flex: 1, justifyContent: "center" }}
                onClick={() => { const a = document.createElement("a"); a.href = pdfPreview.url; a.target = "_blank"; a.click(); }}
              >
                <Icon icon="lucide:external-link" width={14} />
                Abrir / Imprimir
              </PrintBtn>
            </DrawerFooter>
          </PdfModal>
        </DrawerOverlay>
      )}

      {/* Modal: Registrar promesa de pago */}
      {modalPromesa && (
        <DrawerOverlay onClick={() => !guardandoPromesa && setModalPromesa(null)}>
          <ModalBox onClick={(e) => e.stopPropagation()}>
            <ModalHead>
              <span>
                <Icon icon="lucide:calendar-plus" />
                Promesa de Pago
              </span>
              <button onClick={() => setModalPromesa(null)}>
                <Icon icon="lucide:x" />
              </button>
            </ModalHead>
            <ModalBody>
              <ClienteNombre>
                {modalPromesa.factura?.clientes?.nombre_fantasia ||
                  modalPromesa.factura?.clientes?.personas?.razon_social}
              </ClienteNombre>
              <FieldLabel>Fecha prometida *</FieldLabel>
              <FieldInput
                type="date"
                value={fechaPromesa}
                min={TODAY}
                onChange={(e) => setFechaPromesa(e.target.value)}
              />
              <FieldLabel>Monto prometido (opcional)</FieldLabel>
              <FieldInput
                type="text"
                inputMode="numeric"
                value={montoPromesa}
                onChange={(e) => /^\d*$/.test(e.target.value) && setMontoPromesa(e.target.value)}
                placeholder="Gs."
              />
              <FieldLabel>Notas</FieldLabel>
              <FieldTextarea
                rows={2}
                value={notasPromesa}
                onChange={(e) => setNotasPromesa(e.target.value)}
                placeholder="Ej: El cliente dijo que paga el viernes..."
              />

              {loadingPromesas ? (
                <PromesasHist>Cargando historial...</PromesasHist>
              ) : promesasFactura.length > 0 && (
                <>
                  <PromesaHistTitle>Historial de promesas</PromesaHistTitle>
                  <PromesasHist>
                    {promesasFactura.map((p) => (
                      <PromesaHistItem key={p.id} $estado={p.estado}>
                        <span>
                          {fmtFecha(p.fecha_prometida)}
                          {p.monto_prometido && ` — ${fmtGs(p.monto_prometido)}`}
                        </span>
                        <PromesaEstadoBadge $estado={p.estado}>{p.estado}</PromesaEstadoBadge>
                        {p.estado === "pendiente" && (
                          <button onClick={() => marcarPromesa(p.id, "cancelada")} title="Cancelar">
                            <Icon icon="lucide:x" width={12} />
                          </button>
                        )}
                      </PromesaHistItem>
                    ))}
                  </PromesasHist>
                </>
              )}

              <ModalFooter>
                <CancelBtn onClick={() => setModalPromesa(null)} disabled={guardandoPromesa}>
                  Cancelar
                </CancelBtn>
                <SaveBtn onClick={guardarPromesa} disabled={guardandoPromesa || !fechaPromesa}>
                  {guardandoPromesa ? "Guardando..." : "Guardar promesa"}
                </SaveBtn>
              </ModalFooter>
            </ModalBody>
          </ModalBox>
        </DrawerOverlay>
      )}

      <RegistrarGestionModal
        open={!!gestionTarget}
        onClose={() => setGestionTarget(null)}
        cliente={gestionTarget?.cliente}
        factura={gestionTarget?.factura}
        onSuccess={() => { cargarCuotas(); cargarResumen(); }}
      />
    </PageWrap>
  );
}

// ─── Styled Components ────────────────────────────────────────────────────────
const PageWrap = styled.div`
  max-width: 600px;
  margin: 0 auto;
  padding: 16px;
  @media ${Device.mobile} { padding: 8px; }
`;

const Header = styled.div`
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
  h1 { font-size: 18px; font-weight: 700; margin: 0; color: ${({ theme }) => theme.text}; }
  span { font-size: 13px; color: ${({ theme }) => theme.textSub || "#9ca3af"}; }
`;
const HeaderLeft = styled.div`
  display: flex;
  align-items: center;
  gap: 10px;
  color: ${({ theme }) => theme.text};
`;
const RefreshBtn = styled.button`
  width: 36px; height: 36px;
  border-radius: 8px;
  border: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  background: transparent;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: ${({ theme }) => theme.text};
  &:hover { background: ${({ theme }) => theme.bg3 || "#f3f4f6"}; }
`;
const KpiGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 14px;
`;
const KpiCard = styled.div`
  background: ${({ $color }) => $color + "0d"};
  border: 1px solid ${({ $color }) => $color + "25"};
  border-radius: 10px;
  padding: 12px;
  text-align: center;
`;
const KpiNum = styled.div`
  font-size: 24px; font-weight: 700;
  color: ${({ theme }) => theme.text};
`;
const KpiLabel = styled.div`
  font-size: 11px; color: ${({ theme }) => theme.textSub || "#9ca3af"}; margin-top: 2px;
`;

// Date chips
const DateChipRow = styled.div`
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
  flex-wrap: wrap;
`;
const DateChipLabel = styled.span`
  font-size: 12px;
  color: ${({ theme }) => theme.textSub || "#9ca3af"};
  flex-shrink: 0;
`;
const DateChip = styled.button`
  padding: 5px 11px;
  border-radius: 20px;
  border: 1px solid ${({ $active, theme }) => $active ? "#3b82f6" : (theme.bg4 || "#374151")};
  background: ${({ $active }) => $active ? "#3b82f620" : "transparent"};
  color: ${({ $active, theme }) => $active ? "#60a5fa" : (theme.textSub || "#9ca3af")};
  font-size: 12px;
  font-weight: ${({ $active }) => ($active ? "600" : "400")};
  cursor: pointer;
  transition: all 0.15s;
  &:hover { border-color: #3b82f6; color: #60a5fa; }
`;

// Rango de fecha manual
const DateRangeRow = styled.div`
  display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap;
`;
const DateRangeLabel = styled.span`
  font-size: 12px; color: ${({ theme }) => theme.textSub || "#9ca3af"};
`;
const DateInput = styled.input`
  padding: 5px 8px; border-radius: 6px; border: 1px solid ${({ theme }) => theme.bg4 || "#334155"};
  background: ${({ theme }) => theme.body}; color: ${({ theme }) => theme.text};
  font-size: 12px;
  &:focus { outline: none; border-color: #3b82f6; }
`;
const ClearRangeBtn = styled.button`
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%; border: 1px solid #374151;
  background: transparent; color: #6b7280; cursor: pointer;
  &:hover { background: #374151; color: #f9fafb; }
`;

// Toggle ver todas
const VerTodasRow = styled.div`
  display: flex; margin-bottom: 10px;
`;
const VerTodasToggle = styled.button`
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 500; cursor: pointer;
  border: 1px solid ${({ $active }) => $active ? "#3b82f6" : "#374151"};
  background: ${({ $active }) => $active ? "#1d4ed820" : "transparent"};
  color: ${({ $active }) => $active ? "#60a5fa" : "#9ca3af"};
  transition: all 0.15s;
  &:hover { border-color: #3b82f6; color: #60a5fa; }
`;

const FilterBar = styled.div`
  display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px;
`;
const SearchInput = styled.input`
  width: 100%; padding: 9px 12px; border-radius: 8px;
  border: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  background: ${({ theme }) => theme.body};
  color: ${({ theme }) => theme.text};
  font-size: 14px; box-sizing: border-box;
  &::placeholder { color: #9ca3af; }
`;
const FilterTabs = styled.div`
  display: flex; gap: 5px;
`;
const FilterTab = styled.button`
  flex: 1; padding: 7px 4px; border-radius: 8px;
  border: 1px solid ${({ $active, $cobrado, theme }) =>
    $active ? ($cobrado ? "#22c55e60" : "#3b82f660") : (theme.bg4 || "#374151")};
  background: ${({ $active, $cobrado }) =>
    $active ? ($cobrado ? "#22c55e15" : "#3b82f615") : "transparent"};
  color: ${({ $active, $cobrado, theme }) =>
    $active ? ($cobrado ? "#4ade80" : "#60a5fa") : (theme.textSub || "#9ca3af")};
  font-size: 12px; font-weight: ${({ $active }) => ($active ? 600 : 400)};
  cursor: pointer;
`;

const CuotasList = styled.div`
  display: flex; flex-direction: column; gap: 10px;
`;

// ── Cliente card (agrupado) ───────────────────────────────────────────────────
const ClienteCard = styled.div`
  background: ${({ theme }) => theme.body};
  border: 1px solid ${({ $cobrado, theme }) => $cobrado ? "#22c55e30" : (theme.bg4 || "#e5e7eb")};
  border-left: 4px solid ${({ $urgencia, $cobrado }) => {
    if ($cobrado) return "#22c55e";
    const c = { critica: "#991b1b", vencida: "#ef4444", hoy: "#f59e0b", proxima: "#22c55e" };
    return c[$urgencia] || "#6b7280";
  }};
  border-radius: 10px; padding: 12px; box-shadow: 0 1px 3px #0002;
`;
const ClienteHeader = styled.div`
  display: flex; align-items: center; justify-content: space-between;
  cursor: pointer; margin-bottom: 4px; gap: 8px;
`;
const ClienteHeaderLeft = styled.div`
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap; flex: 1; min-width: 0;
`;
const ClienteNombreH = styled.span`
  font-size: 15px; font-weight: 700; color: ${({ theme }) => theme.text};
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
`;
const ClienteHeaderRight = styled.div`
  display: flex; align-items: center; gap: 5px;
  flex-shrink: 0; color: ${({ theme }) => theme.textSub || "#6b7280"};
`;
const ClienteSummary = styled.span`
  font-size: 11px; color: ${({ theme }) => theme.textSub || "#6b7280"}; white-space: nowrap;
`;

// Facturas y filas de cuotas dentro del cliente
const FacturasWrap = styled.div`
  margin: 8px 0 4px;
  display: flex; flex-direction: column; gap: 6px;
`;
const FacturaGroup = styled.div`
  background: ${({ theme }) => theme.bg2 || theme.bg3 || "#f3f4f6"};
  border: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  border-radius: 7px; padding: 8px 10px;
  display: flex; flex-direction: column; gap: 4px;
`;
const FacturaLabel = styled.div`
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 600;
  color: ${({ theme }) => theme.textSub || "#6b7280"}; margin-bottom: 4px;
`;
const FacturaSaldo = styled.span`
  font-size: 11px; font-weight: 400;
  color: ${({ theme }) => theme.textSub || "#9ca3af"}; margin-left: 4px;
`;
const CuotaRow = styled.div`
  display: flex; align-items: center; justify-content: space-between;
  gap: 6px; padding: 5px 0;
  border-top: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
`;
const CuotaRowLeft = styled.div`
  display: flex; align-items: center; gap: 6px;
`;
const CuotaRowNum = styled.span`
  font-size: 12px; font-weight: 600; color: ${({ theme }) => theme.text};
  background: ${({ theme }) => theme.bg4 || "#e5e7eb"};
  padding: 1px 6px; border-radius: 4px;
`;
const CuotaRowFecha = styled.span`
  font-size: 12px; color: ${({ theme }) => theme.textSub || "#6b7280"};
`;
const CuotaRowVenc = styled.span`
  font-size: 11px; font-weight: 600;
  color: ${({ $critica }) => $critica ? "#fca5a5" : "#f87171"};
  background: ${({ $critica }) => $critica ? "#7f1d1d40" : "#7f1d1d20"};
  padding: 1px 5px; border-radius: 4px;
`;
const CuotaRowRight = styled.div`
  display: flex; align-items: center; gap: 5px;
`;
const CuotaRowMonto = styled.span`
  font-size: 13px; font-weight: 700; color: ${({ theme }) => theme.text};
`;
const CuotaPromesaBtn = styled.button`
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 6px;
  border: 1px solid #3b82f650; background: #3b82f615;
  color: #60a5fa; cursor: pointer;
  &:hover { background: #3b82f625; }
`;

// Pie de cliente card
const ClienteFooter = styled.div`
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  flex-wrap: wrap; gap: 6px;
`;
const ClienteTotales = styled.div`
  display: flex; align-items: center; gap: 10px;
  font-size: 12px; color: ${({ theme }) => theme.text};
  strong { font-size: 14px; }
`;
const ClienteAcciones = styled.div`
  display: flex; gap: 6px;
`;

const UrgenciaBadge = styled.span`
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 20px;
  background: ${({ $bg }) => $bg}; color: ${({ $color }) => $color};
  font-size: 11px; font-weight: 600;
`;
const CuotaDireccion = styled.div`
  display: flex; align-items: center; gap: 4px;
  font-size: 12px; color: #6b7280; margin-bottom: 2px;
`;
const CuotaReferencia = styled.div`
  display: flex; align-items: center; gap: 4px;
  font-size: 11px; color: #f59e0b; margin-bottom: 2px;
  font-style: italic;
`;
const CuotaTelefono = styled.a`
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: #60a5fa; text-decoration: none; margin-bottom: 6px;
  &:hover { text-decoration: underline; }
`;

// Contacto compacto (siempre visible)
const ContactRow = styled.div`
  display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px;
`;
const ContactItem = styled.span`
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px;
  color: ${({ $ref, theme }) => $ref ? "#fbbf24" : (theme.textSub || "#9ca3af")};
  font-style: ${({ $ref }) => $ref ? "italic" : "normal"};
`;
const ContactLink = styled.a`
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; color: #60a5fa; text-decoration: none;
  &:hover { text-decoration: underline; }
`;

// Próxima cuota inline (siempre visible)
const NextCuotaBox = styled.div`
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  background: ${({ $cobrado, $urgencia }) => {
    if ($cobrado) return "#22c55e0a";
    const c = { critica: "#ef444410", vencida: "#ef44440a", hoy: "#f59e0b0a", proxima: "#22c55e0a" };
    return c[$urgencia] || "#3b82f60a";
  }};
  border: 1px solid ${({ $cobrado, $urgencia }) => {
    if ($cobrado) return "#22c55e25";
    const c = { critica: "#ef444430", vencida: "#ef444420", hoy: "#f59e0b25", proxima: "#22c55e20" };
    return c[$urgencia] || "#3b82f620";
  }};
  border-radius: 8px; padding: 8px 10px; margin-bottom: 6px;
`;
const NextCuotaLeft = styled.div`
  display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;
`;
const NextCuotaNum = styled.span`
  font-size: 13px; font-weight: 700; color: ${({ theme }) => theme.text};
  background: ${({ theme }) => theme.bg4 || "#374151"};
  padding: 2px 8px; border-radius: 5px; white-space: nowrap;
`;
const NextCuotaInfo = styled.div`
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
  span:first-child { font-size: 11px; color: ${({ theme }) => theme.textSub || "#9ca3af"}; }
`;
const NextCuotaFecha = styled.span`
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 12px; color: ${({ theme }) => theme.text};
`;
const NextCuotaRight = styled.div`
  display: flex; align-items: center; gap: 6px; flex-shrink: 0;
`;
const NextCuotaMonto = styled.span`
  font-size: 15px; font-weight: 700; color: ${({ theme }) => theme.text};
`;
const MoraBadge = styled.span`
  font-size: 11px; font-weight: 600; color: #fbbf24;
  background: #f59e0b15; border: 1px solid #f59e0b30;
  padding: 1px 5px; border-radius: 10px;
`;
const AccionBtn = styled.a`
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 12px; border-radius: 8px; cursor: pointer;
  font-size: 12px; font-weight: 500; text-decoration: none;
  border: 1px solid ${({ $wa, $secondary, theme }) =>
    $wa ? "#25d366" : $secondary ? (theme.bg4 || "#374151") : "#3b82f650"};
  color: ${({ $wa, $secondary, theme }) =>
    $wa ? "#25d366" : $secondary ? (theme.textSub || "#9ca3af") : "#60a5fa"};
  background: transparent;
  &:hover { opacity: 0.8; }
`;

// Drawer historial
const slideIn = keyframes`
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
`;
const DrawerOverlay = styled.div`
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.45);
  z-index: 200;
  display: flex; align-items: stretch; justify-content: flex-end;
`;
const DrawerPanel = styled.div`
  width: min(420px, 100vw);
  background: ${({ theme }) => theme.body || "#fff"};
  display: flex; flex-direction: column;
  box-shadow: -4px 0 24px #0003;
  animation: ${slideIn} 0.22s ease;
  max-height: 100vh;
`;
const DrawerHeader = styled.div`
  padding: 16px;
  border-bottom: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  display: flex; align-items: flex-start; gap: 8px;
  position: relative;
`;
const DrawerTitle = styled.div`
  display: flex; align-items: center; gap: 8px;
  font-size: 16px; font-weight: 700;
  color: ${({ theme }) => theme.text};
  flex: 1;
`;
const DrawerSub = styled.div`
  font-size: 12px; color: ${({ theme }) => theme.textSub || "#9ca3af"}; margin-top: 2px; flex: 1;
  padding-left: 28px;
`;
const DrawerClose = styled.button`
  position: absolute; right: 12px; top: 12px;
  width: 28px; height: 28px; border-radius: 50%;
  border: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  background: transparent; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: ${({ theme }) => theme.text};
`;
const DrawerBody = styled.div`
  flex: 1; overflow-y: auto; padding: 12px;
  display: flex; flex-direction: column; gap: 10px;
`;
const DrawerFooter = styled.div`
  padding: 10px 16px;
  border-top: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  display: flex; align-items: center; gap: 8px; justify-content: center;
`;

// Recibo cards en historial
const ReciboCard = styled.div`
  background: ${({ theme }) => theme.bg2 || theme.bg3};
  border: 1px solid ${({ $anulado, theme }) => $anulado ? "#ef444430" : (theme.bg4 || "#374151")};
  border-radius: 8px; padding: 10px;
  opacity: ${({ $anulado }) => $anulado ? 0.6 : 1};
`;
const ReciboCardHeader = styled.div`
  display: flex; justify-content: space-between; align-items: center; margin-bottom: 2px;
`;
const ReciboNum = styled.span`
  font-size: 13px; font-weight: 700; color: ${({ theme }) => theme.text};
`;
const ReciboEstado = styled.span`
  font-size: 11px; padding: 2px 8px; border-radius: 12px; font-weight: 600;
  background: ${({ $estado }) => $estado === "anulado" ? "#ef444418" : "#22c55e15"};
  color: ${({ $estado }) => $estado === "anulado" ? "#f87171" : "#4ade80"};
`;
const ReciboFecha = styled.div`
  font-size: 11px; color: ${({ theme }) => theme.textSub || "#9ca3af"}; margin-bottom: 4px;
`;
const ReciboMontos = styled.div`
  display: flex; gap: 10px; flex-wrap: wrap;
  font-size: 12px; margin-bottom: 6px;
  span { display: flex; gap: 3px; }
`;
const CuotasDetalle = styled.div`
  border-top: 1px solid ${({ theme }) => theme.bg4 || "#374151"};
  margin-top: 6px; padding-top: 6px;
  display: flex; flex-direction: column; gap: 6px;
`;
const CuotaDetalleItem = styled.div`
  display: flex; justify-content: space-between; align-items: flex-start;
  font-size: 12px;
`;
const CuotaDetalleLeft = styled.div`
  display: flex; flex-direction: column; gap: 1px; color: ${({ theme }) => theme.text};
`;
const CuotaDetalleRight = styled.div`
  display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  color: ${({ theme }) => theme.text};
`;
const ParcialBadge = styled.span`
  font-size: 10px; color: #fbbf24; background: #f59e0b15;
  border: 1px solid #f59e0b30; padding: 1px 5px; border-radius: 10px;
`;
const ReciboPrintRow = styled.div`
  display: flex; gap: 6px; margin-top: 6px;
`;
const PrintBtn = styled.button`
  display: inline-flex; align-items: center; gap: 4px;
  padding: 5px 10px; border-radius: 7px;
  border: 1px solid ${({ theme }) => theme.bg4 || "#374151"};
  background: transparent;
  font-size: 12px; color: ${({ theme }) => theme.textSub || "#9ca3af"}; cursor: pointer;
  &:hover { background: ${({ theme }) => theme.bg3}; color: ${({ theme }) => theme.text}; }
  &:disabled { opacity: 0.4; cursor: not-allowed; }
`;

// PDF modal
const PdfModal = styled.div`
  width: min(520px, 100vw);
  height: 100vh;
  background: ${({ theme }) => theme.body || "#fff"};
  display: flex; flex-direction: column;
  box-shadow: -4px 0 24px #0003;
  animation: ${slideIn} 0.22s ease;
`;

// Paginación
const Pagination = styled.div`
  display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 14px;
`;
const PageBtn = styled.button`
  width: 32px; height: 32px; border-radius: 8px;
  border: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  background: transparent; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: ${({ theme }) => theme.text};
  &:disabled { opacity: 0.4; cursor: not-allowed; }
`;
const PageInfo = styled.span`
  font-size: 13px; color: ${({ theme }) => theme.textSub || "#9ca3af"};
`;

const LoadingMsg = styled.div`
  display: flex; align-items: center; gap: 8px;
  color: ${({ theme }) => theme.textSub || "#9ca3af"}; font-size: 14px; padding: 24px 0;
  .spin { animation: spin 1s linear infinite; }
  @keyframes spin { to { transform: rotate(360deg); } }
`;
const EmptyMsg = styled.div`
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  color: ${({ theme }) => theme.textSub || "#9ca3af"}; padding: 32px 0;
  p { margin: 0; font-size: 14px; }
`;

// Modal promesa
const ModalBox = styled.div`
  width: min(400px, 95vw);
  background: ${({ theme }) => theme.body || "#fff"};
  border-radius: 14px;
  box-shadow: 0 20px 60px #0004;
  overflow: hidden;
  align-self: flex-end;
  margin: auto;
`;
const ModalHead = styled.div`
  display: flex; justify-content: space-between; align-items: center;
  padding: 14px 16px; font-size: 15px; font-weight: 700;
  border-bottom: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  color: ${({ theme }) => theme.text};
  span { display: flex; align-items: center; gap: 8px; }
  button { border: none; background: none; cursor: pointer; color: #6b7280; padding: 4px; }
`;
const ModalBody = styled.div`
  padding: 16px; display: flex; flex-direction: column; gap: 6px;
`;
const ClienteNombre = styled.div`
  font-size: 14px; font-weight: 600; margin-bottom: 6px;
  color: ${({ theme }) => theme.text};
`;
const FieldLabel = styled.label`
  font-size: 12px; color: ${({ theme }) => theme.textSub || "#9ca3af"}; margin-top: 4px;
`;
const FieldInput = styled.input`
  width: 100%; padding: 9px 12px; border-radius: 8px;
  border: 1px solid ${({ theme }) => theme.bg4 || "#374151"}; font-size: 14px;
  background: ${({ theme }) => theme.bg2 || theme.body}; color: ${({ theme }) => theme.text};
  box-sizing: border-box;
  &:focus { outline: none; border-color: #3b82f660; }
`;
const FieldTextarea = styled.textarea`
  width: 100%; padding: 9px 12px; border-radius: 8px;
  border: 1px solid ${({ theme }) => theme.bg4 || "#374151"}; font-size: 13px; resize: vertical;
  background: ${({ theme }) => theme.bg2 || theme.body}; color: ${({ theme }) => theme.text};
  box-sizing: border-box;
  &:focus { outline: none; border-color: #3b82f660; }
`;
const PromesaHistTitle = styled.div`
  font-size: 12px; color: ${({ theme }) => theme.textSub || "#9ca3af"}; font-weight: 600; margin-top: 8px;
`;
const PromesasHist = styled.div`
  display: flex; flex-direction: column; gap: 4px;
  max-height: 120px; overflow-y: auto;
  font-size: 12px; color: ${({ theme }) => theme.textSub || "#9ca3af"};
`;
const PromesaHistItem = styled.div`
  display: flex; align-items: center; gap: 6px;
  padding: 4px 8px; border-radius: 6px;
  background: ${({ $estado }) =>
    $estado === "cumplida" ? "#22c55e15" : $estado === "cancelada" ? "#ef444415" : "#f59e0b12"};
  span:first-child { flex: 1; }
  button { border: none; background: none; cursor: pointer; color: #f87171; padding: 2px; }
`;
const PromesaEstadoBadge = styled.span`
  font-size: 10px; padding: 2px 6px; border-radius: 10px;
  color: ${({ $estado }) =>
    $estado === "cumplida" ? "#4ade80" : $estado === "cancelada" ? "#f87171" : "#fbbf24"};
  background: ${({ $estado }) =>
    $estado === "cumplida" ? "#22c55e15" : $estado === "cancelada" ? "#ef444415" : "#f59e0b12"};
  font-weight: 600;
`;
const ModalFooter = styled.div`
  display: flex; gap: 8px; justify-content: flex-end; margin-top: 10px;
`;
const CancelBtn = styled.button`
  padding: 8px 16px; border-radius: 8px;
  border: 1px solid ${({ theme }) => theme.bg4 || "#374151"};
  background: transparent; cursor: pointer; font-size: 13px;
  color: ${({ theme }) => theme.textSub || "#9ca3af"};
  &:disabled { opacity: 0.5; }
`;
const SaveBtn = styled.button`
  padding: 8px 16px; border-radius: 8px; border: none;
  background: #3b82f6; color: #fff; cursor: pointer; font-size: 13px; font-weight: 600;
  &:disabled { opacity: 0.5; cursor: not-allowed; }
`;

const DetallePanel = styled.div`
  margin: 6px 12px 2px;
  padding: 8px 12px;
  background: #f8fafc;
  border-radius: 8px;
  border: 1px solid #e2e8f0;
  display: flex;
  flex-direction: column;
  gap: 5px;
`;

const DetalleItem = styled.div`
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font-size: 11.5px;
  color: ${(p) => (p.$promesa ? "#92400e" : "#475569")};
  background: ${(p) => (p.$promesa ? "#fef3c7" : "transparent")};
  border-radius: ${(p) => (p.$promesa ? "5px" : "0")};
  padding: ${(p) => (p.$promesa ? "3px 6px" : "0")};
`;

const DetalleProductos = styled.div`
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 2px;
`;

const DetalleProductoChip = styled.span`
  font-size: 10.5px;
  padding: 2px 7px;
  border-radius: 10px;
  background: #e0e7ff;
  color: #3730a3;
  font-weight: 500;
`;
