import { Icon } from "@iconify/react";
import {
  Box,
  Button,
  Card,
  CardContent,
  Chip,
  Paper,
  Skeleton,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  TableSortLabel,
  TextField,
  Tooltip,
  Typography,
  useMediaQuery,
  useTheme,
} from "@mui/material";
import { useQuery } from "@tanstack/react-query";
import { useEffect, useMemo, useState } from "react";
import PropTypes from "prop-types";
import { useNavigate } from "react-router-dom";
import { toast } from "sonner";
import * as XLSX from "xlsx";
import api from "../api/api.config";
import { fmtMoneda } from "../components/_standards/enums/monedas";
import { AccesoRestringido } from "../components/common/AccesoRestringido";
import { usePermission } from "../hooks/usePermission";
import { EmptyState, ListToolbar, ScreenGuia, TablePaginationBar, sanitizeRecords } from "../components/_standards";
import { fmtFechaCalendario, hoyISO, toFechaCalendarioISO } from "../utils/fecha";

// ==================== API ====================

const getReporteClientes = async (params) => {
  const query = new URLSearchParams();
  if (params.fecha_desde) query.set("fecha_desde", params.fecha_desde);
  if (params.fecha_hasta) query.set("fecha_hasta", params.fecha_hasta);
  const res = await api.get(`/facturas/reporte/clientes?${query.toString()}`);
  return res.data;
};

// ==================== HELPERS ====================

const getHoy = () => hoyISO();
const getInicioMes = () => {
  const d = new Date();
  d.setDate(1);
  return d.toISOString().split("T")[0];
};

const normalizar = (s) =>
  String(s || "")
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase();

// Segmentos rápidos: cada uno responde una pregunta concreta del análisis.
const SEGMENTOS = [
  { value: "todos", label: "Todos", test: () => true },
  { value: "vencidos", label: "Con deuda vencida", test: (c) => c.saldo_vencido > 0 },
  { value: "nuevos", label: "Clientes nuevos", test: (c) => c.es_nuevo },
  { value: "solo_pagos", label: "Solo pagaron (sin compras)", test: (c) => c.cant_facturas === 0 },
];

// Los montos se ordenan por su equivalente en la moneda base (`*_base`, con la
// cotización de cada documento): comparar el número crudo ponía USD 5.000
// debajo de Gs. 700.000.
const COLUMNAS_ORDEN = {
  razon_social: (c) => normalizar(c.razon_social),
  cant_facturas: (c) => c.cant_facturas,
  total_facturado: (c) => c.facturado_base,
  ticket_promedio: (c) => (c.cant_facturas > 0 ? c.facturado_base / c.cant_facturas : 0),
  ultima_compra: (c) => toFechaCalendarioISO(c.ultima_compra) || "",
  total_nc: (c) => c.nc_base,
  total_cobrado: (c) => c.cobrado_base,
  saldo_pendiente: (c) => c.saldo_pendiente_base,
  saldo_vencido: (c) => c.saldo_vencido_base,
};

// Suma un campo por moneda: { PYG: n, USD: m }.
const sumarPorMoneda = (filas, campo) =>
  filas.reduce((acc, f) => {
    acc[f.moneda] = (acc[f.moneda] || 0) + (Number(f[campo]) || 0);
    return acc;
  }, {});

// Un renglón por moneda. Si hay más de una, abajo el total convertido a la
// moneda base (`equivalente` = { monto, moneda }).
const MontosPorMoneda = ({ montos, variant = "h6", color, monedaBase, equivalente }) => {
  const entradas = Object.entries(montos);
  if (entradas.length === 0) {
    return (
      <Typography variant={variant} fontWeight={700} color={color}>
        {fmtMoneda(0, monedaBase)}
      </Typography>
    );
  }
  return (
    <>
      {entradas.map(([moneda, total]) => (
        <Typography key={moneda} variant={variant} fontWeight={700} color={color}>
          {fmtMoneda(total, moneda)}
        </Typography>
      ))}
      {entradas.length > 1 && equivalente && (
        <Tooltip title="Total convertido con la cotización de cada documento">
          <Typography variant="caption" fontWeight={600} color="text.secondary" sx={{ display: "block" }}>
            ≈ {fmtMoneda(equivalente, monedaBase)} en total
          </Typography>
        </Tooltip>
      )}
    </>
  );
};

MontosPorMoneda.propTypes = {
  montos: PropTypes.object.isRequired,
  variant: PropTypes.string,
  color: PropTypes.string,
  monedaBase: PropTypes.string.isRequired,
  equivalente: PropTypes.number,
};

// "≈ Gs. X" bajo un monto en moneda extranjera; "estimado" si algún documento
// no tenía cotización guardada y se usó la vigente.
const Equivalente = ({ monto, moneda, monedaBase, estimada }) => {
  if (moneda === monedaBase || !monto) return null;
  return (
    <Tooltip title={estimada ? "Algún documento no tenía cotización guardada: se usó la vigente" : "Convertido con la cotización de cada documento"}>
      <Typography variant="caption" color="text.secondary" sx={{ display: "block", whiteSpace: "nowrap" }}>
        ≈ {fmtMoneda(monto, monedaBase)}
        {estimada ? " (estimado)" : ""}
      </Typography>
    </Tooltip>
  );
};

Equivalente.propTypes = {
  monto: PropTypes.number,
  moneda: PropTypes.string.isRequired,
  monedaBase: PropTypes.string.isRequired,
  estimada: PropTypes.bool,
};

// Encabezado de columna ordenable. Texto asc por defecto; números y fechas desc
// (lo primero que se busca es "quién compró / debe más").
const Encabezado = ({ campo, children, align, orden, onOrdenar }) => {
  const activo = orden.campo === campo;
  const dirInicial = campo === "razon_social" ? "asc" : "desc";
  return (
    <TableCell align={align} sx={{ fontWeight: 600, whiteSpace: "nowrap" }} sortDirection={activo ? orden.dir : false}>
      <TableSortLabel active={activo} direction={activo ? orden.dir : dirInicial} onClick={() => onOrdenar(campo)}>
        {children}
      </TableSortLabel>
    </TableCell>
  );
};

Encabezado.propTypes = {
  campo: PropTypes.string.isRequired,
  children: PropTypes.node,
  align: PropTypes.string,
  orden: PropTypes.shape({ campo: PropTypes.string, dir: PropTypes.string }).isRequired,
  onOrdenar: PropTypes.func.isRequired,
};

// ==================== EXCEL EXPORT ====================

const exportExcel = (clientes, fechaDesde, fechaHasta, monedaBase) => {
  if (!clientes?.length) return;
  // Los montos van como NÚMERO para que Excel los pueda operar; el backend puede
  // serializar Decimal como string, así que se fuerza Number(...).
  const num = (v) => (v === null || v === undefined || v === "" ? 0 : Number(v) || 0);
  const variasMonedas = new Set(clientes.map((c) => c.moneda)).size > 1;
  const hayNc = clientes.some((c) => c.cant_nc > 0);
  const base = (titulo) => `${titulo} (${monedaBase})`;

  // Columnas de monto: en la moneda de la fila o en la moneda base. Definen el
  // formato numérico por encabezado, no por posición.
  const MONTO_FILA = ["Total facturado", "Notas de crédito", "Facturado neto", "Ticket promedio", "Total cobrado", "Saldo pendiente", "Saldo vencido"];
  const MONTO_BASE = ["Facturado", "Notas de crédito", "Facturado neto", "Cobrado", "Saldo pendiente", "Saldo vencido"].map(base);

  const rows = clientes.map((c) => ({
    "Cliente": c.razon_social,
    "Nombre fantasía": c.nombre_fantasia || "",
    "RUC/Documento": c.ruc || "",
    "Código cliente": c.cod_cliente || "",
    "Teléfono": c.telefono || "",
    "Moneda": c.moneda,
    "Facturas": c.cant_facturas,
    "Total facturado": num(c.total_facturado),
    ...(hayNc && {
      "Cant. NC": c.cant_nc || 0,
      "Notas de crédito": num(c.total_nc),
      "Facturado neto": num(c.facturado_neto),
    }),
    "Ticket promedio": num(c.ticket_promedio),
    "Última compra": c.ultima_compra ? fmtFechaCalendario(c.ultima_compra) : "",
    "Cliente desde": c.primera_compra ? fmtFechaCalendario(c.primera_compra) : "",
    "Nuevo en el período": c.es_nuevo ? "Sí" : "No",
    "Total cobrado": num(c.total_cobrado),
    "Saldo pendiente": num(c.saldo_pendiente),
    "Saldo vencido": num(c.saldo_vencido),
    "Días de atraso": c.dias_atraso || 0,
    // Con varias monedas, los equivalentes permiten sumar la planilla entera.
    ...(variasMonedas && {
      [base("Facturado")]: num(c.facturado_base),
      ...(hayNc && {
        [base("Notas de crédito")]: num(c.nc_base),
        [base("Facturado neto")]: num(c.facturado_neto_base),
      }),
      [base("Cobrado")]: num(c.cobrado_base),
      [base("Saldo pendiente")]: num(c.saldo_pendiente_base),
      [base("Saldo vencido")]: num(c.saldo_vencido_base),
      "Cotización estimada": c.cotizacion_estimada ? "Sí" : "No",
    }),
  }));
  const headers = Object.keys(rows[0]);
  const ws = XLSX.utils.json_to_sheet(sanitizeRecords(rows), { header: headers });
  ws["!cols"] = headers.map((h) => ({ wch: h === "Cliente" ? 30 : Math.max(10, Math.min(24, h.length + 4)) }));

  // Formato de miles; los montos en moneda extranjera conservan los decimales.
  headers.forEach((h, C) => {
    const enBase = MONTO_BASE.includes(h);
    if (!enBase && !MONTO_FILA.includes(h)) return;
    clientes.forEach((c, i) => {
      const cell = ws[XLSX.utils.encode_cell({ r: i + 1, c: C })];
      if (cell && typeof cell.v === "number") {
        cell.t = "n";
        cell.z = enBase || c.moneda === monedaBase ? "#,##0" : "#,##0.00";
      }
    });
  });

  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, "Análisis de clientes");
  XLSX.writeFile(wb, `analisis_clientes_${fechaDesde}_a_${fechaHasta}.xlsx`);
  toast.success("Reporte exportado correctamente");
};

// ==================== COMPONENT ====================

export function ReporteVentasClientes() {
  const navigate = useNavigate();
  const muiTheme = useTheme();
  const isMobile = useMediaQuery(muiTheme.breakpoints.down("md"));
  const { canRead } = usePermission("REPORTES");
  const { can } = usePermission("VENTAS");
  const canReadFacturas = can("VEN_FAC_FACTURA_VER");

  const [fechaDesde, setFechaDesde] = useState(getInicioMes());
  const [fechaHasta, setFechaHasta] = useState(getHoy());
  const [busqueda, setBusqueda] = useState("");
  const [segmento, setSegmento] = useState("todos");
  const [orden, setOrden] = useState({ campo: null, dir: "desc" });
  const [page, setPage] = useState(1);
  const [rowsPerPage, setRowsPerPage] = useState(20);

  const { data, isLoading } = useQuery({
    queryKey: ["reporte-clientes-movimiento", fechaDesde, fechaHasta],
    queryFn: () =>
      getReporteClientes({
        fecha_desde: fechaDesde || undefined,
        fecha_hasta: fechaHasta || undefined,
      }),
  });

  const clientes = useMemo(() => data?.data || [], [data]);
  const monedaBase = data?.moneda_base || "PYG";

  // Clientes distintos por segmento sobre el período completo (para rotular los
  // chips). Distintos y no filas: con varias monedas un cliente ocupa una fila
  // por moneda.
  const conteoSegmentos = useMemo(
    () =>
      Object.fromEntries(
        SEGMENTOS.map((s) => [s.value, new Set(clientes.filter(s.test).map((c) => c.cliente_id)).size]),
      ),
    [clientes],
  );

  // Total facturado del período en moneda base: base del % de participación,
  // así el porcentaje es comparable entre clientes de distintas monedas.
  const facturadoTotalBase = useMemo(() => clientes.reduce((acc, c) => acc + (c.facturado_base || 0), 0), [clientes]);

  const filtrados = useMemo(() => {
    const seg = SEGMENTOS.find((s) => s.value === segmento) || SEGMENTOS[0];
    const q = normalizar(busqueda.trim());
    let filas = clientes.filter(seg.test);
    if (q) {
      filas = filas.filter((c) =>
        [c.razon_social, c.nombre_fantasia, c.ruc, c.cod_cliente, c.telefono].some((v) => normalizar(v).includes(q)),
      );
    }
    if (orden.campo) {
      const valor = COLUMNAS_ORDEN[orden.campo];
      const signo = orden.dir === "asc" ? 1 : -1;
      filas = [...filas].sort((a, b) => {
        const va = valor(a);
        const vb = valor(b);
        if (va < vb) return -1 * signo;
        if (va > vb) return 1 * signo;
        return 0;
      });
    }
    return filas;
  }, [clientes, segmento, busqueda, orden]);

  // Cualquier cambio de filtro u orden vuelve a la primera página.
  useEffect(() => {
    setPage(1);
  }, [segmento, busqueda, orden, fechaDesde, fechaHasta]);

  const totalPages = Math.max(1, Math.ceil(filtrados.length / rowsPerPage));
  const pagina = filtrados.slice((page - 1) * rowsPerPage, page * rowsPerPage);
  const hayVariasMonedas = new Set(clientes.map((c) => c.moneda)).size > 1;
  // La columna de notas de crédito sólo aparece si alguna fila del período tiene.
  const hayNc = clientes.some((c) => c.cant_nc > 0);

  // KPIs sobre lo filtrado (ver ui-standards: los KPIs reflejan el filtro).
  const kpis = useMemo(() => {
    const facturado = sumarPorMoneda(filtrados, "total_facturado");
    const facturas = filtrados.reduce((acc, c) => acc + (c.cant_facturas || 0), 0);
    const ticket = Object.fromEntries(
      Object.entries(facturado).map(([moneda, total]) => {
        const cant = filtrados.filter((c) => c.moneda === moneda).reduce((a, c) => a + (c.cant_facturas || 0), 0);
        return [moneda, cant > 0 ? total / cant : 0];
      }),
    );
    const sumaBase = (campo) => filtrados.reduce((acc, c) => acc + (Number(c[campo]) || 0), 0);
    const facturadoBase = sumaBase("facturado_base");
    const ncBase = sumaBase("nc_base");
    return {
      facturadoBase,
      nc: sumarPorMoneda(filtrados, "total_nc"),
      ncBase,
      netoBase: facturadoBase - ncBase,
      neto: sumarPorMoneda(filtrados, "facturado_neto"),
      cantNc: filtrados.reduce((acc, c) => acc + (c.cant_nc || 0), 0),
      ticketBase: facturas > 0 ? facturadoBase / facturas : 0,
      cobradoBase: sumaBase("cobrado_base"),
      saldoBase: sumaBase("saldo_pendiente_base"),
      vencidoBase: sumaBase("saldo_vencido_base"),
      clientes: new Set(filtrados.map((c) => c.cliente_id)).size,
      nuevos: new Set(filtrados.filter((c) => c.es_nuevo).map((c) => c.cliente_id)).size,
      facturas,
      facturado,
      ticket,
      cobrado: sumarPorMoneda(filtrados, "total_cobrado"),
      saldo: sumarPorMoneda(filtrados, "saldo_pendiente"),
      vencido: sumarPorMoneda(filtrados, "saldo_vencido"),
      clientesConVencido: new Set(filtrados.filter((c) => c.saldo_vencido > 0).map((c) => c.cliente_id)).size,
    };
  }, [filtrados]);

  const filtrosActivos = (busqueda.trim() ? 1 : 0) + (segmento !== "todos" ? 1 : 0);
  const limpiarFiltros = () => {
    setBusqueda("");
    setSegmento("todos");
  };

  const ordenarPor = (campo) =>
    setOrden((prev) =>
      prev.campo === campo
        ? { campo, dir: prev.dir === "desc" ? "asc" : "desc" }
        : { campo, dir: campo === "razon_social" ? "asc" : "desc" },
    );

  const participacion = (c) => (facturadoTotalBase > 0 ? ((c.facturado_base || 0) / facturadoTotalBase) * 100 : 0);

  const renderSaldo = (c) => (
    <>
      <Typography variant="body2" fontWeight={c.saldo_pendiente > 0 ? 600 : 400} color={c.saldo_pendiente > 0 ? "text.primary" : "text.disabled"}>
        {fmtMoneda(c.saldo_pendiente, c.moneda)}
      </Typography>
      <Equivalente monto={c.saldo_pendiente_base} moneda={c.moneda} monedaBase={monedaBase} estimada={c.cotizacion_estimada} />
      {c.saldo_vencido > 0 && (
        <Typography variant="caption" color="error.main" sx={{ display: "block", whiteSpace: "nowrap", lineHeight: 1.3 }}>
          Vencido {fmtMoneda(c.saldo_vencido, c.moneda)}
          <br />
          {c.dias_atraso} {c.dias_atraso === 1 ? "día" : "días"} de atraso
        </Typography>
      )}
    </>
  );

  const renderCliente = (c) => (
    <>
      <Box sx={{ display: "flex", alignItems: "center", gap: 0.75, flexWrap: "wrap" }}>
        <Typography variant="body2" fontWeight={600}>
          {c.razon_social}
        </Typography>
        {c.es_nuevo && (
          <Tooltip title={`Primera compra: ${fmtFechaCalendario(c.primera_compra)}`}>
            <Chip label="Nuevo" size="small" color="success" variant="outlined" sx={{ height: 18, fontSize: "0.65rem" }} />
          </Tooltip>
        )}
      </Box>
      {c.nombre_fantasia && c.nombre_fantasia !== c.razon_social && (
        <Typography variant="caption" color="text.secondary" sx={{ display: "block" }}>
          {c.nombre_fantasia}
        </Typography>
      )}
      <Typography variant="caption" color="text.secondary" sx={{ display: "flex", alignItems: "center", gap: 0.5, flexWrap: "wrap" }}>
        {c.ruc || "Sin documento"}
        {c.cod_cliente && ` · Cód. ${c.cod_cliente}`}
        {c.telefono && (
          <>
            {" · "}
            <Icon icon="lucide:phone" width={11} />
            {c.telefono}
          </>
        )}
      </Typography>
    </>
  );

  if (!canRead || !canReadFacturas) {
    return <AccesoRestringido mensaje="No tienes permisos para ver este reporte." />;
  }

  // "Gs. X" o, con varias monedas, "Gs. X + US$ Y (≈ Gs. Z)".
  const montosTexto = (porMoneda, enBase) => {
    const entradas = Object.entries(porMoneda).filter(([, v]) => v);
    if (entradas.length === 0) return fmtMoneda(0, monedaBase);
    const texto = entradas.map(([m, v]) => fmtMoneda(v, m)).join(" + ");
    return entradas.length > 1 || entradas[0][0] !== monedaBase ? `${texto} (≈ ${fmtMoneda(enBase, monedaBase)})` : texto;
  };

  const kpiCards = [
    {
      icon: "lucide:users",
      label: "Clientes",
      value: (
        <Typography variant="h6" fontWeight={700} color="primary.main">
          {kpis.clientes}
        </Typography>
      ),
      sub: `${kpis.nuevos} nuevos en el período`,
    },
    {
      icon: "lucide:banknote",
      label: "Total facturado",
      value: <MontosPorMoneda montos={kpis.facturado} color="success.main" monedaBase={monedaBase} equivalente={kpis.facturadoBase} />,
      sub:
        kpis.cantNc > 0 ? (
          <>
            {kpis.facturas} facturas · {kpis.cantNc} NC por {montosTexto(kpis.nc, kpis.ncBase)}
            <br />
            <Box component="span" sx={{ fontWeight: 600, color: "text.primary" }}>
              Neto: {montosTexto(kpis.neto, kpis.netoBase)}
            </Box>
          </>
        ) : (
          `${kpis.facturas} facturas`
        ),
    },
    {
      icon: "lucide:receipt",
      label: "Ticket promedio",
      value: <MontosPorMoneda montos={kpis.ticket} color="text.primary" monedaBase={monedaBase} equivalente={kpis.ticketBase} />,
      sub: "Facturado ÷ facturas",
    },
    {
      icon: "lucide:hand-coins",
      label: "Total cobrado",
      value: <MontosPorMoneda montos={kpis.cobrado} color="info.main" monedaBase={monedaBase} equivalente={kpis.cobradoBase} />,
      sub: "Recibos del período",
    },
    {
      icon: "lucide:wallet",
      label: "Saldo pendiente hoy",
      value: <MontosPorMoneda montos={kpis.saldo} color="warning.main" monedaBase={monedaBase} equivalente={kpis.saldoBase} />,
      sub: Object.values(kpis.vencido).some((v) => v > 0)
        ? `Vencido: ${Object.entries(kpis.vencido)
            .filter(([, v]) => v > 0)
            .map(([m, v]) => fmtMoneda(v, m))
            .join(" + ")}${Object.keys(kpis.vencido).length > 1 ? ` (≈ ${fmtMoneda(kpis.vencidoBase, monedaBase)})` : ""} · ${kpis.clientesConVencido} clientes`
        : "Sin deuda vencida",
      subColor: Object.values(kpis.vencido).some((v) => v > 0) ? "error.main" : "text.secondary",
    },
  ];

  return (
    <Box sx={{ maxWidth: 1400, mx: "auto", p: { xs: 2, md: 3 } }}>
      {/* ── Header ── */}
      <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", mb: 1, flexWrap: "wrap", gap: 1 }}>
        <Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
          <Button size="small" onClick={() => navigate("/reportes")} sx={{ minWidth: 32, p: 0.5 }}>
            <Icon icon="lucide:arrow-left" width={20} />
          </Button>
          <Box>
            <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
              <Icon icon="lucide:users" width={22} />
              <Typography variant="h6" fontWeight={700}>Análisis de Clientes</Typography>
            </Box>
            <Typography variant="caption" color="text.secondary">
              Compras, cobros y deuda de los clientes con movimiento en el período
            </Typography>
          </Box>
        </Box>
        <Button
          variant="outlined"
          size="small"
          startIcon={<Icon icon="lucide:file-spreadsheet" width={16} />}
          onClick={() => exportExcel(filtrados, fechaDesde, fechaHasta, monedaBase)}
          disabled={!filtrados.length}
        >
          Exportar Excel
        </Button>
      </Box>

      <Typography variant="caption" color="text.secondary" sx={{ mb: 2, display: "block" }}>
        <span style={{ cursor: "pointer" }} onClick={() => navigate("/reportes")}>Reportes</span>
        {" > "}Análisis de Clientes
      </Typography>

      <Box sx={{ mb: 2 }}>
        <ScreenGuia
          titulo="¿Cómo leer este análisis?"
          intro="Lista los clientes que compraron o pagaron en el período. Un cliente que no tuvo movimiento no aparece."
          pasos={[
            { icon: "lucide:banknote", color: "#22c55e", titulo: "Compras del período", desc: "Facturas no anuladas del período: cantidad, total, ticket promedio y fecha de la última compra. Las notas de crédito emitidas en el período se restan para dar el facturado neto." },
            { icon: "lucide:hand-coins", color: "#0ea5e9", titulo: "Cobros del período", desc: "Recibos emitidos en el período, aunque paguen facturas de meses anteriores." },
            { icon: "lucide:wallet", color: "#f59e0b", titulo: "Deuda de hoy", desc: "Saldo pendiente de todas sus cuotas a la fecha, sin importar el período. En rojo, lo vencido y los días de atraso de la cuota más vieja." },
            { icon: "lucide:sparkles", color: "#a855f7", titulo: "Clientes nuevos", desc: "Su primera factura en el sistema cae dentro del período elegido." },
          ]}
          notas={[
            { icon: "lucide:arrow-up-down", text: "Hacé clic en el título de una columna para ordenar (por ejemplo, por saldo vencido para armar la lista de cobranza)." },
            { icon: "lucide:percent", text: "La participación es el peso del cliente sobre el total facturado del período (todas las monedas, convertidas a la moneda base)." },
            { icon: "lucide:receipt-text", text: "Facturado, ticket promedio y participación son sobre el total bruto de las facturas; las notas de crédito y el facturado neto se muestran aparte. El saldo de hoy no se ajusta: una nota de crédito aplicada a una cuota ya le bajó el saldo." },
            { icon: "lucide:file-spreadsheet", text: "El Excel exporta lo que estás viendo: respeta la búsqueda, el segmento y el orden." },
            { icon: "lucide:coins", text: "Varias monedas: cada monto se muestra en su moneda original (un cliente que opera en dos monedas tiene una fila por moneda). Para ordenar y totalizar se convierte a la moneda base con la cotización guardada en cada factura y recibo; si falta, se usa la vigente y se marca como estimado." },
          ]}
        />
      </Box>

      {/* ── Filtros ── */}
      <Paper variant="outlined" sx={{ p: 2, pb: 0.5, mb: 2, borderRadius: 2 }}>
        <ListToolbar
          searchText={busqueda}
          onSearchChange={setBusqueda}
          searchPlaceholder="Buscar por nombre, RUC/CI, código o teléfono..."
          showColumns={false}
          quickFilters={SEGMENTOS.map((s) => ({ value: s.value, label: `${s.label} (${conteoSegmentos[s.value] ?? 0})` }))}
          quickFilterActive={segmento}
          onQuickFilter={setSegmento}
          quickFiltersLabel="Ver:"
          filtrosActivos={filtrosActivos}
          onClearFilters={limpiarFiltros}
          extraActions={
            <>
              <TextField
                size="small"
                type="date"
                label="Desde"
                value={fechaDesde}
                onChange={(e) => setFechaDesde(e.target.value)}
                InputLabelProps={{ shrink: true }}
                sx={{ minWidth: 160 }}
              />
              <TextField
                size="small"
                type="date"
                label="Hasta"
                value={fechaHasta}
                onChange={(e) => setFechaHasta(e.target.value)}
                InputLabelProps={{ shrink: true }}
                sx={{ minWidth: 160 }}
              />
            </>
          }
        />
      </Paper>

      {/* ── KPIs ── */}
      <Box sx={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: 2, mb: 2 }}>
        {kpiCards.map((kpi) => (
          <Card key={kpi.label} variant="outlined" sx={{ borderRadius: 2 }}>
            <CardContent sx={{ p: "14px !important", display: "flex", alignItems: "center", gap: 1.5 }}>
              <Icon icon={kpi.icon} width={20} style={{ opacity: 0.6, flexShrink: 0 }} />
              <Box sx={{ minWidth: 0 }}>
                <Typography variant="caption" color="text.secondary">{kpi.label}</Typography>
                {isLoading ? <Skeleton width={120} height={32} /> : kpi.value}
                <Typography variant="caption" color={kpi.subColor || "text.secondary"} sx={{ display: "block", lineHeight: 1.3 }}>
                  {kpi.sub}
                </Typography>
              </Box>
            </CardContent>
          </Card>
        ))}
      </Box>

      {isLoading ? (
        <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
          {[...Array(6)].map((_, i) => <Skeleton key={i} variant="rounded" height={48} />)}
        </Box>
      ) : filtrados.length === 0 ? (
        <Paper variant="outlined" sx={{ borderRadius: 2 }}>
          {clientes.length === 0 ? (
            <EmptyState
              icon="lucide:users-round"
              title="Ningún cliente tuvo movimiento en el período"
              description="Probá con un rango de fechas más amplio: aparecen los clientes con facturas o recibos entre Desde y Hasta."
              ctaLabel="Ver desde inicio de año"
              ctaIcon="lucide:calendar-range"
              onCta={() => setFechaDesde(`${new Date().getFullYear()}-01-01`)}
            />
          ) : (
            <EmptyState
              icon="lucide:search-x"
              title="Ningún cliente coincide con el filtro"
              description="Revisá la búsqueda o elegí otro segmento."
              ctaLabel="Limpiar filtros"
              ctaIcon="lucide:filter-x"
              onCta={limpiarFiltros}
            />
          )}
        </Paper>
      ) : isMobile ? (
        <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
          {pagina.map((c) => (
            <Card key={`${c.cliente_id}-${c.moneda}`} variant="outlined" sx={{ borderRadius: 2 }}>
              <CardContent sx={{ p: "12px !important" }}>
                {renderCliente(c)}
                <Typography variant="caption" color="text.secondary" sx={{ display: "block" }}>
                  {c.ultima_compra ? `Última compra ${fmtFechaCalendario(c.ultima_compra)}` : "Sin compras en el período"}
                </Typography>
                <Box sx={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(130px, 1fr))", gap: 1, mt: 1 }}>
                  <Box>
                    <Typography variant="caption" color="text.secondary">Facturado ({c.cant_facturas})</Typography>
                    <Typography variant="body2" fontWeight={600} color="success.main">{fmtMoneda(c.total_facturado, c.moneda)}</Typography>
                  </Box>
                  {c.cant_nc > 0 && (
                    <Box>
                      <Typography variant="caption" color="text.secondary">Notas de crédito ({c.cant_nc})</Typography>
                      <Typography variant="body2" color="error.main">− {fmtMoneda(c.total_nc, c.moneda)}</Typography>
                    </Box>
                  )}
                  <Box>
                    <Typography variant="caption" color="text.secondary">Cobrado</Typography>
                    <Typography variant="body2">{fmtMoneda(c.total_cobrado, c.moneda)}</Typography>
                  </Box>
                  <Box>
                    <Typography variant="caption" color="text.secondary">Saldo hoy</Typography>
                    {renderSaldo(c)}
                  </Box>
                </Box>
              </CardContent>
            </Card>
          ))}
        </Box>
      ) : (
        <TableContainer component={Paper} variant="outlined" sx={{ borderRadius: 2 }}>
          <Table size="small">
            <TableHead>
              <TableRow>
                <Encabezado orden={orden} onOrdenar={ordenarPor} campo="razon_social">Cliente</Encabezado>
                {hayVariasMonedas && <TableCell sx={{ fontWeight: 600 }}>Moneda</TableCell>}
                <Encabezado orden={orden} onOrdenar={ordenarPor} campo="cant_facturas" align="right">Facturas</Encabezado>
                <Encabezado orden={orden} onOrdenar={ordenarPor} campo="total_facturado" align="right">Facturado</Encabezado>
                {hayNc && (
                  <Encabezado orden={orden} onOrdenar={ordenarPor} campo="total_nc" align="right">Notas de crédito</Encabezado>
                )}
                <Encabezado orden={orden} onOrdenar={ordenarPor} campo="ultima_compra">Última compra</Encabezado>
                <Encabezado orden={orden} onOrdenar={ordenarPor} campo="total_cobrado" align="right">Cobrado</Encabezado>
                <Encabezado orden={orden} onOrdenar={ordenarPor} campo="saldo_pendiente" align="right">Saldo hoy</Encabezado>
              </TableRow>
            </TableHead>
            <TableBody>
              {pagina.map((c) => {
                const part = participacion(c);
                return (
                  <TableRow key={`${c.cliente_id}-${c.moneda}`} hover>
                    <TableCell sx={{ minWidth: 240 }}>{renderCliente(c)}</TableCell>
                    {hayVariasMonedas && <TableCell>{c.moneda}</TableCell>}
                    {/* El ticket promedio va debajo de la cantidad: ahorra una columna. */}
                    <TableCell align="right" sx={{ whiteSpace: "nowrap" }}>
                      <Typography variant="body2" color={c.cant_facturas > 0 ? "text.primary" : "text.disabled"}>
                        {c.cant_facturas}
                      </Typography>
                      {c.cant_facturas > 0 && (
                        <Tooltip title="Ticket promedio (facturado ÷ facturas)">
                          <Typography variant="caption" color="text.secondary">
                            prom. {fmtMoneda(c.ticket_promedio, c.moneda)}
                          </Typography>
                        </Tooltip>
                      )}
                    </TableCell>
                    <TableCell align="right" sx={{ whiteSpace: "nowrap" }}>
                      <Typography variant="body2" fontWeight={600} color={c.total_facturado > 0 ? "success.main" : "text.disabled"}>
                        {fmtMoneda(c.total_facturado, c.moneda)}
                      </Typography>
                      <Equivalente monto={c.facturado_base} moneda={c.moneda} monedaBase={monedaBase} estimada={c.cotizacion_estimada} />
                      {part > 0 && (
                        <Tooltip title="Participación sobre el total facturado del período (todas las monedas, convertidas)">
                          <Typography variant="caption" color="text.secondary">
                            {part.toFixed(1).replace(".", ",")}% del total
                          </Typography>
                        </Tooltip>
                      )}
                    </TableCell>
                    {hayNc && (
                      <TableCell align="right" sx={{ whiteSpace: "nowrap" }}>
                        {c.cant_nc > 0 ? (
                          <>
                            <Typography variant="body2" color="error.main">
                              − {fmtMoneda(c.total_nc, c.moneda)}
                            </Typography>
                            <Typography variant="caption" color="text.secondary" sx={{ display: "block" }}>
                              {c.cant_nc} {c.cant_nc === 1 ? "nota" : "notas"} · Neto {fmtMoneda(c.facturado_neto, c.moneda)}
                            </Typography>
                          </>
                        ) : (
                          <Typography component="span" variant="body2" color="text.disabled">-</Typography>
                        )}
                      </TableCell>
                    )}
                    <TableCell sx={{ whiteSpace: "nowrap" }}>
                      {c.ultima_compra ? fmtFechaCalendario(c.ultima_compra) : <Typography component="span" variant="body2" color="text.disabled">-</Typography>}
                    </TableCell>
                    <TableCell align="right" sx={{ whiteSpace: "nowrap" }}>
                      <Typography variant="body2" color={c.total_cobrado > 0 ? "info.main" : "text.disabled"}>
                        {fmtMoneda(c.total_cobrado, c.moneda)}
                      </Typography>
                    </TableCell>
                    <TableCell align="right">{renderSaldo(c)}</TableCell>
                  </TableRow>
                );
              })}
            </TableBody>
          </Table>
        </TableContainer>
      )}

      {filtrados.length > 0 && (
        <Box sx={{ mt: 2 }}>
          <TablePaginationBar
            page={page}
            totalPages={totalPages}
            onPageChange={setPage}
            total={filtrados.length}
            shown={pagina.length}
            noun={hayVariasMonedas ? "filas (cliente y moneda)" : "clientes"}
            rowsPerPage={rowsPerPage}
            onRowsPerPageChange={(n) => {
              setRowsPerPage(n);
              setPage(1);
            }}
          />
        </Box>
      )}
    </Box>
  );
}
