import { useEffect, useMemo, useState, useCallback, useRef } from "react";
import { Autocomplete, Box, Button, Chip, Container, Grid, IconButton, LinearProgress, Menu, MenuItem, Paper, Stack, Table, TableBody, TableCell, TableHead, TablePagination, TableRow, TextField, Tooltip, Typography } from "@mui/material";
import { Icon } from "@iconify/react";
import { useSearchParams } from "react-router-dom";
import { toast } from "sonner";
import { EmptyState, ScreenGuia } from "../../components/_standards";
import { fmtMoneda } from "../../components/_standards/enums/monedas";
import { COB_PROMESA_ESTADO, COB_PROMESA_EVIDENCIA_META, COB_PROMESA_PAGADOR, COB_PROMESA_PAGADOR_META, getCobPromesaEstadoChip } from "../../components/_standards/enums/estados";
import { getPromesasPago, updateEstadoPromesa } from "../../api/cobranzas.service";
import { getCobradoresActivos } from "../../api/vendedores-cobradores.service";
import { getCliente, searchClientes } from "../../api/clientes.service";
import { fmtFechaCalendario } from "../../utils/fecha";
import { PromesaPagoFormModal } from "../../components/cobranzas/promesas/PromesaPagoFormModal";
import { CancelarPromesaModal } from "../../components/cobranzas/promesas/CancelarPromesaModal";
import { RenegociarPromesaModal } from "../../components/cobranzas/promesas/RenegociarPromesaModal";
import { ClienteHistorialDrawer } from "../../components/organismos/ClientesDesign/ClienteHistorialDrawer";
import { usePermission } from "../../hooks/usePermission";
import { useConfirmDialog } from "../../hooks/useConfirmDialog";

const pad = (v, n) => String(v ?? "").padStart(n, "0");
const formatNroFactura = (fc) => {
  if (!fc) return "—";
  return `${pad(fc.dest, 3)}-${pad(fc.dpunexp, 3)}-${pad(fc.dnumdoc, 7)}`;
};

const nombreCliente = (c) => c?.personas?.razon_social || c?.nombre_fantasia || "—";

const nombreCobrador = (cb) => (cb ? [cb.nombre, cb.apellido].filter(Boolean).join(" ") : "—");

const monedaCodigo = (r) => r?.factura?.moneda?.codigo || "PYG";

// Rangos compactos de cuotas: [1,2,3,5,7,8] → "1–3, 5, 7–8"
const rangosCuotas = (nros) => {
  const ordenados = [...new Set(nros.filter((n) => n != null))].sort((a, b) => a - b);
  const partes = [];
  for (let i = 0; i < ordenados.length; ) {
    let j = i;
    while (j + 1 < ordenados.length && ordenados[j + 1] === ordenados[j] + 1) j++;
    partes.push(j > i ? `${ordenados[i]}–${ordenados[j]}` : `${ordenados[i]}`);
    i = j + 1;
  }
  return partes.join(", ");
};

// Qué cubre la promesa: una cuota (legacy), o los objetivos por factura/cuota.
// Devuelve [{ nro: "111-111-0002289", detalle: "cuotas 1–10" }].
const objetivosPromesa = (p) => {
  const objetivos = Array.isArray(p.facturas_objetivo) ? p.facturas_objetivo : [];
  if (objetivos.length === 0) {
    return [{ nro: formatNroFactura(p.factura), detalle: p.cuota_id ? `Cuota #${p.cuota?.dcuotas ?? p.cuota?.nro_cuota ?? "—"}` : null }];
  }
  const porFactura = new Map();
  for (const o of objetivos) {
    const entry = porFactura.get(o.factura_cab_id) ?? { nro: formatNroFactura(o.factura ?? p.factura), cuotas: [], completa: false };
    if (o.cuota_id) entry.cuotas.push(o.cuota?.nro_cuota);
    else entry.completa = true;
    porFactura.set(o.factura_cab_id, entry);
  }
  return [...porFactura.values()].map((f) => ({
    nro: f.nro,
    detalle: f.cuotas.length === 0 ? (f.completa ? "Factura completa" : null) : f.cuotas.length === 1 ? `Cuota #${f.cuotas[0]}` : `Cuotas ${rangosCuotas(f.cuotas)}`,
  }));
};

// Progreso de cobro sobre lo prometido (0–100). Null si todavía no se cobró nada.
const progresoCobro = (p) => {
  const cobrado = Number(p.monto_cumplido ?? 0);
  const prometido = Number(p.monto_prometido ?? 0);
  if (cobrado <= 0 || prometido <= 0) return null;
  return { cobrado, pct: Math.min(100, Math.round((cobrado / prometido) * 100)) };
};

export default function PromesasListPage() {
  const { can } = usePermission("COBRANZAS");
  const puedeCrear = can("COB_PRM_PROMESA_CREAR");
  const puedeCancelar = can("COB_PRM_PROMESA_CANCELAR");
  const puedeRenegociar = can("COB_PRM_PROMESA_RENEGOCIAR");
  const puedeEditar = can("COB_PRM_PROMESA_EDITAR");

  // `?cliente_id=` llega desde el banner de promesas del wizard de cobros ("Ver detalle")
  // y desde el expediente: la lista arranca filtrada por ese cliente y en "Todas".
  const [searchParams] = useSearchParams();
  const clienteIdInicial = searchParams.get("cliente_id");

  const [estado, setEstado] = useState(clienteIdInicial ? "todas" : COB_PROMESA_ESTADO.PENDIENTE);
  const [fechaDesde, setFechaDesde] = useState("");
  const [fechaHasta, setFechaHasta] = useState("");
  const [cobrador, setCobrador] = useState(null);
  const [cliente, setCliente] = useState(clienteIdInicial ? { id: clienteIdInicial } : null);
  const [cobradoresOpts, setCobradoresOpts] = useState([]);
  const [clienteOpts, setClienteOpts] = useState([]);
  const clienteSearchTimer = useRef(null);

  const [promesas, setPromesas] = useState([]);
  const [loading, setLoading] = useState(false);

  const [nuevaOpen, setNuevaOpen] = useState(false);
  const [cancelarTarget, setCancelarTarget] = useState(null);
  const [renegociarTarget, setRenegociarTarget] = useState(null);
  const [clienteDrawer, setClienteDrawer] = useState(null);
  const [page, setPage] = useState(0);
  const [pageSize, setPageSize] = useState(25);

  const { confirm, dialogs } = useConfirmDialog();

  useEffect(() => {
    getCobradoresActivos()
      .then((data) => setCobradoresOpts(Array.isArray(data) ? data : data?.data || []))
      .catch(() => {});
  }, []);

  // Completa el nombre del cliente preseleccionado por URL (el filtro ya aplica con el id).
  useEffect(() => {
    if (!clienteIdInicial) return;
    getCliente(clienteIdInicial)
      .then((res) => {
        const c = res?.data ?? res;
        if (c?.id) setCliente((prev) => (prev?.id === c.id ? c : prev));
      })
      .catch(() => {});
  }, [clienteIdInicial]);

  const cargar = useCallback(async () => {
    setLoading(true);
    try {
      const params = {
        estado,
        fecha_desde: fechaDesde || undefined,
        fecha_hasta: fechaHasta || undefined,
        cobrador_id: cobrador?.id || undefined,
        cliente_id: cliente?.id || undefined,
      };
      const data = await getPromesasPago(params);
      setPromesas(Array.isArray(data) ? data : data?.data || []);
    } catch (e) {
      toast.error(e?.response?.data?.message || "Error cargando promesas");
    } finally {
      setLoading(false);
    }
  }, [estado, fechaDesde, fechaHasta, cobrador?.id, cliente?.id]);

  useEffect(() => {
    cargar();
  }, [cargar]);

  // Resetea a la primera página cuando cambian los filtros o el dataset.
  useEffect(() => {
    setPage(0);
  }, [estado, fechaDesde, fechaHasta, cobrador?.id, cliente?.id]);

  const buscarCliente = useCallback((q) => {
    if (!q || q.length < 2) return;
    clearTimeout(clienteSearchTimer.current);
    clienteSearchTimer.current = setTimeout(async () => {
      try {
        const res = await searchClientes(q, 15);
        setClienteOpts(Array.isArray(res) ? res : res?.data || []);
      } catch {
        /* silencioso */
      }
    }, 300);
  }, []);

  const handleMarcarCumplida = async (p) => {
    const ok = await confirm({
      title: "Marcar promesa como cumplida",
      message: `Vas a marcar como cumplida la promesa de ${nombreCliente(p.cliente)} por ${fmtMoneda(p.monto_prometido, monedaCodigo(p))}. Esto es para casos en que el cobro ya fue registrado por otra vía.`,
      confirmText: "Marcar cumplida",
      confirmColor: "success",
    });
    if (!ok) return;
    try {
      await updateEstadoPromesa(p.id, { estado: COB_PROMESA_ESTADO.CUMPLIDA });
      toast.success("Promesa marcada como cumplida");
      cargar();
    } catch (e) {
      toast.error(e?.response?.data?.message || "No se pudo actualizar");
    }
  };

  const handleMarcarIncumplida = async (p) => {
    const ok = await confirm({
      title: "Marcar promesa como incumplida",
      message: "El cliente no cumplió con la promesa. Esto afecta su score de cumplimiento.",
      confirmText: "Marcar incumplida",
      confirmColor: "error",
    });
    if (!ok) return;
    try {
      await updateEstadoPromesa(p.id, { estado: COB_PROMESA_ESTADO.INCUMPLIDA });
      toast.success("Promesa marcada como incumplida");
      cargar();
    } catch (e) {
      toast.error(e?.response?.data?.message || "No se pudo actualizar");
    }
  };

  const columns = useMemo(
    () => [
      {
        key: "cliente",
        label: "Cliente",
        primary: true,
        wrap: true,
        render: (r) => (
          <Box>
            <Typography variant="body2" fontWeight={600} sx={{ wordBreak: "break-word" }}>
              {nombreCliente(r.cliente)}
            </Typography>
            {r.cobrador && (
              <Typography variant="caption" color="text.secondary" display="block">
                <Icon icon="mdi:account-tie" width={12} style={{ verticalAlign: "-2px", marginRight: 2 }} />
                {nombreCobrador(r.cobrador)}
              </Typography>
            )}
            {r.pagador_tipo && r.pagador_tipo !== COB_PROMESA_PAGADOR.CLIENTE && (
              <Typography variant="caption" color="text.secondary" display="block">
                Paga: {COB_PROMESA_PAGADOR_META[r.pagador_tipo]?.label || r.pagador_tipo}
                {r.pagador_cliente ? ` (${nombreCliente(r.pagador_cliente)})` : ""}
              </Typography>
            )}
          </Box>
        ),
      },
      {
        key: "factura",
        label: "Factura / Cuota",
        wrap: true,
        render: (r) => (
          <Box>
            <Typography variant="caption" sx={{ fontFamily: "monospace" }}>
              {formatNroFactura(r.factura)}
            </Typography>
            {r.cuota_id && (
              <Typography variant="caption" color="text.secondary" display="block">
                Cuota #{r.cuota?.dcuotas ?? r.cuota?.nro_cuota ?? "—"}
              </Typography>
            )}
          </Box>
        ),
      },
      {
        key: "fecha_prometida",
        label: "Prometido",
        render: (r) => fmtFechaCalendario(r.fecha_prometida),
        sortable: true,
        sortValue: (r) => r.fecha_prometida,
      },
      {
        key: "monto_prometido",
        label: "Monto",
        align: "right",
        highlight: true,
        render: (r) => fmtMoneda(r.monto_prometido, monedaCodigo(r)),
        sortable: true,
        sortValue: (r) => Number(r.monto_prometido) || 0,
      },
      {
        key: "estado",
        label: "Estado",
        chip: true,
        align: "center",
        render: (r) => {
          const meta = getCobPromesaEstadoChip(r.estado, r);
          const evMeta = COB_PROMESA_EVIDENCIA_META[r.tipo_evidencia];
          return (
            <Stack direction="row" spacing={0.5} alignItems="center" justifyContent="center">
              <Tooltip title={meta.tooltip ?? ""} enterDelay={300}>
                <Chip size="small" label={meta.label} color={meta.color} />
              </Tooltip>
              {evMeta && (
                <Tooltip title={evMeta.label} enterDelay={300}>
                  <Box component="span" sx={{ display: "inline-flex", alignItems: "center", color: "text.secondary" }}>
                    <Icon icon={evMeta.icon} width={16} />
                  </Box>
                </Tooltip>
              )}
            </Stack>
          );
        },
      },
    ],
    [],
  );

  const actions = useMemo(
    () => [
      {
        key: "cumplida",
        label: "Marcar cumplida",
        icon: <Icon icon="mdi:check-circle" />,
        color: "success",
        onClick: handleMarcarCumplida,
        show: (r) => puedeEditar && (r.estado === COB_PROMESA_ESTADO.PENDIENTE || r.estado === COB_PROMESA_ESTADO.INCUMPLIDA),
      },
      {
        key: "incumplida",
        label: "Marcar incumplida",
        icon: <Icon icon="mdi:close-octagon" />,
        color: "error",
        onClick: handleMarcarIncumplida,
        show: (r) => puedeEditar && r.estado === COB_PROMESA_ESTADO.PENDIENTE,
      },
      {
        key: "renegociar",
        label: "Renegociar",
        icon: <Icon icon="mdi:handshake-outline" />,
        color: "warning",
        onClick: (r) => setRenegociarTarget(r),
        show: (r) => puedeRenegociar && (r.estado === COB_PROMESA_ESTADO.PENDIENTE || r.estado === COB_PROMESA_ESTADO.INCUMPLIDA),
      },
      {
        key: "cancelar",
        label: "Cancelar",
        icon: <Icon icon="mdi:close-circle-outline" />,
        color: "error",
        onClick: (r) => setCancelarTarget(r),
        show: (r) => puedeCancelar && r.estado === COB_PROMESA_ESTADO.PENDIENTE,
      },
    ],
    [puedeEditar, puedeRenegociar, puedeCancelar],
  );

  // Totalización por moneda (lista puede mezclar PYG/USD/etc.)
  const totalesPorMoneda = useMemo(() => {
    const map = new Map();
    for (const p of promesas) {
      const code = monedaCodigo(p);
      map.set(code, (map.get(code) || 0) + (Number(p.monto_prometido) || 0));
    }
    return Array.from(map.entries());
  }, [promesas]);

  return (
    <Box sx={{ pt: 1, pb: 3 }}>
      <ScreenGuia
        titulo="Promesas de Pago"
        descripcion="Listado operativo de promesas de pago. Filtrá por estado / cobrador / cliente y ejecutá acciones (cancelar, renegociar, marcar cumplida)."
        pasos={[
          "Filtrar por estado (pendiente, cumplida, incumplida, etc.) o rango de fechas.",
          "Las pendientes se pueden marcar como cumplida/incumplida o renegociar.",
          "Para el reporte de tasa de cumplimiento, abrí el menú Reporte de cumplimiento.",
        ]}
      />

      <Stack direction={{ xs: "column", md: "row" }} alignItems={{ xs: "stretch", md: "center" }} justifyContent="space-between" spacing={1.5} mb={2}>
        <Typography variant="h6" fontWeight={600}>
          Promesas de pago
        </Typography>
        {puedeCrear && (
          <Button variant="contained" color="warning" onClick={() => setNuevaOpen(true)} startIcon={<Icon icon="mdi:handshake" />}>
            Nueva promesa
          </Button>
        )}
      </Stack>

      <Paper sx={{ p: 1.5, mb: 2 }}>
        <Stack direction={{ xs: "column", md: "row" }} spacing={1.5} alignItems={{ xs: "stretch", md: "center" }}>
          <TextField select label="Estado" size="small" value={estado} onChange={(e) => setEstado(e.target.value)} sx={{ flex: { md: "0 0 160px" } }}>
            <MenuItem value="todas">Todas</MenuItem>
            <MenuItem value={COB_PROMESA_ESTADO.PENDIENTE}>Pendientes</MenuItem>
            <MenuItem value={COB_PROMESA_ESTADO.CUMPLIDA}>Cumplidas</MenuItem>
            <MenuItem value={COB_PROMESA_ESTADO.CUMPLIDA_PARCIAL}>Parciales</MenuItem>
            <MenuItem value={COB_PROMESA_ESTADO.INCUMPLIDA}>Incumplidas</MenuItem>
            <MenuItem value={COB_PROMESA_ESTADO.CANCELADA}>Canceladas</MenuItem>
            <MenuItem value={COB_PROMESA_ESTADO.RENEGOCIADA}>Renegociadas</MenuItem>
          </TextField>
          <Stack direction="row" spacing={1} sx={{ flex: { md: "0 0 320px" } }}>
            <TextField type="date" label="Desde" size="small" InputLabelProps={{ shrink: true }} value={fechaDesde} onChange={(e) => setFechaDesde(e.target.value)} fullWidth />
            <TextField type="date" label="Hasta" size="small" InputLabelProps={{ shrink: true }} value={fechaHasta} onChange={(e) => setFechaHasta(e.target.value)} fullWidth />
          </Stack>
          <Autocomplete
            size="small"
            sx={{ flex: 1, minWidth: 180 }}
            value={cobrador}
            onChange={(_, v) => setCobrador(v)}
            options={cobradoresOpts}
            getOptionLabel={(o) => [o?.nombre, o?.apellido].filter(Boolean).join(" ") || ""}
            isOptionEqualToValue={(a, b) => a?.id === b?.id}
            renderInput={(p) => <TextField {...p} label="Cobrador" />}
          />
          <Autocomplete
            size="small"
            sx={{ flex: 1.4, minWidth: 220 }}
            value={cliente}
            onChange={(_, v) => setCliente(v)}
            onInputChange={(_, v) => buscarCliente(v)}
            options={clienteOpts}
            filterOptions={(x) => x}
            getOptionLabel={(o) => o?.personas?.razon_social || o?.nombre_fantasia || o?.personas?.ruc || o?.personas?.documento || ""}
            renderOption={(props, o) => {
              const ruc = o?.personas?.ruc || o?.personas?.documento;
              return (
                <li {...props} key={o.id}>
                  <Box>
                    <Typography variant="body2">{nombreCliente(o)}</Typography>
                    {ruc && (
                      <Typography variant="caption" color="text.secondary">
                        {ruc}
                      </Typography>
                    )}
                  </Box>
                </li>
              );
            }}
            isOptionEqualToValue={(a, b) => a?.id === b?.id}
            renderInput={(p) => <TextField {...p} label="Cliente" placeholder="Razón social, RUC o CI…" />}
          />
        </Stack>
      </Paper>

      {loading && <LinearProgress sx={{ mb: 2 }} />}

      <Paper sx={{ p: 2 }}>
        <Stack direction={{ xs: "column", sm: "row" }} alignItems={{ xs: "flex-start", sm: "center" }} justifyContent="space-between" mb={1} gap={1}>
          <Typography variant="subtitle1" fontWeight={600}>
            {promesas.length} promesa{promesas.length === 1 ? "" : "s"}
          </Typography>
          {totalesPorMoneda.length > 0 && (
            <Box sx={{ display: "flex", gap: 1.5, flexWrap: "wrap" }}>
              {totalesPorMoneda.map(([code, total]) => (
                <Typography key={code} variant="body2" color="text.secondary">
                  Total prometido <strong>{fmtMoneda(total, code)}</strong>
                </Typography>
              ))}
            </Box>
          )}
        </Stack>

        <Table size="small" sx={{ width: "100%", tableLayout: "auto" }}>
          <TableHead>
            <TableRow sx={{ bgcolor: "action.hover" }}>
              <TableCell sx={{ fontWeight: 700, fontSize: "0.72rem", textTransform: "uppercase" }}>Cliente</TableCell>
              <TableCell sx={{ fontWeight: 700, fontSize: "0.72rem", textTransform: "uppercase" }}>Factura / Cuota</TableCell>
              <TableCell sx={{ fontWeight: 700, fontSize: "0.72rem", textTransform: "uppercase" }}>Prometido</TableCell>
              <TableCell sx={{ fontWeight: 700, fontSize: "0.72rem", textTransform: "uppercase" }} align="right">Monto</TableCell>
              <TableCell sx={{ fontWeight: 700, fontSize: "0.72rem", textTransform: "uppercase" }} align="center">Estado</TableCell>
              <TableCell sx={{ fontWeight: 700, fontSize: "0.72rem", textTransform: "uppercase", width: 48 }} align="center" />
            </TableRow>
          </TableHead>
          <TableBody>
            {promesas.length === 0 ? (
              <TableRow>
                <TableCell colSpan={6} sx={{ py: 6 }}>
                  <EmptyState title="Sin promesas con esos filtros" description="Probá ajustar el estado o ampliar el rango de fechas." />
                </TableCell>
              </TableRow>
            ) : (
              promesas
                .slice(page * pageSize, page * pageSize + pageSize)
                .map((r) => {
                  const evMeta = COB_PROMESA_EVIDENCIA_META[r.tipo_evidencia];
                  const estadoMeta = getCobPromesaEstadoChip(r.estado, r);
                  return (
                    <PromesaTableRow
                      key={r.id}
                      promesa={r}
                      evMeta={evMeta}
                      estadoMeta={estadoMeta}
                      actions={actions}
                      onOpenCliente={() => r.cliente && setClienteDrawer(r.cliente)}
                    />
                  );
                })
            )}
          </TableBody>
        </Table>
        {promesas.length > 0 && (
          <TablePagination
            component="div"
            count={promesas.length}
            page={page}
            onPageChange={(_, p) => setPage(p)}
            rowsPerPage={pageSize}
            onRowsPerPageChange={(e) => {
              setPageSize(parseInt(e.target.value, 10));
              setPage(0);
            }}
            rowsPerPageOptions={[10, 25, 50, 100]}
            labelRowsPerPage="Filas por página"
            labelDisplayedRows={({ from, to, count }) => `${from}–${to} de ${count}`}
            sx={{
              "& .MuiTablePagination-toolbar": {
                justifyContent: "center",
                pl: 0,
              },
              "& .MuiTablePagination-spacer": {
                display: "none",
              },
            }}
          />
        )}
      </Paper>

      <PromesaPagoFormModal
        open={nuevaOpen}
        onClose={() => setNuevaOpen(false)}
        onSuccess={() => {
          setNuevaOpen(false);
          cargar();
        }}
        permiteSeleccionarCliente
      />

      <CancelarPromesaModal open={!!cancelarTarget} onClose={() => setCancelarTarget(null)} promesa={cancelarTarget} onSuccess={cargar} />

      <RenegociarPromesaModal open={!!renegociarTarget} onClose={() => setRenegociarTarget(null)} promesa={renegociarTarget} onSuccess={cargar} />

      <ClienteHistorialDrawer cliente={clienteDrawer} onClose={() => setClienteDrawer(null)} />

      {dialogs}
    </Box>
  );
}

function PromesaTableRow({ promesa, evMeta, estadoMeta, actions, onOpenCliente }) {
  const [menuAnchor, setMenuAnchor] = useState(null);
  const visibleActions = actions.filter((a) => !a.show || a.show(promesa));
  const progreso = progresoCobro(promesa);

  return (
    <TableRow hover sx={{ cursor: "pointer" }}>
      <TableCell onClick={onOpenCliente}>
        <Typography variant="body2" fontWeight={600} sx={{ wordBreak: "break-word" }}>
          {nombreCliente(promesa.cliente)}
        </Typography>
        {promesa.cobrador && (
          <Typography variant="caption" color="text.secondary" display="block">
            <Icon icon="mdi:account-tie" width={12} style={{ verticalAlign: "-2px", marginRight: 2 }} />
            {nombreCobrador(promesa.cobrador)}
          </Typography>
        )}
        {promesa.pagador_tipo && promesa.pagador_tipo !== COB_PROMESA_PAGADOR.CLIENTE && (
          <Typography variant="caption" color="text.secondary" display="block">
            Paga: {COB_PROMESA_PAGADOR_META[promesa.pagador_tipo]?.label || promesa.pagador_tipo}
            {promesa.pagador_cliente ? ` (${nombreCliente(promesa.pagador_cliente)})` : ""}
          </Typography>
        )}
      </TableCell>
      <TableCell onClick={onOpenCliente}>
        {objetivosPromesa(promesa).map((o) => (
          <Box key={o.nro}>
            <Typography variant="caption" sx={{ fontFamily: "monospace" }}>
              {o.nro}
            </Typography>
            {o.detalle && (
              <Typography variant="caption" color="text.secondary" display="block">
                {o.detalle}
              </Typography>
            )}
          </Box>
        ))}
      </TableCell>
      <TableCell onClick={onOpenCliente}>{fmtFechaCalendario(promesa.fecha_prometida)}</TableCell>
      <TableCell align="right" onClick={onOpenCliente} sx={{ fontWeight: 600, minWidth: 140 }}>
        {fmtMoneda(promesa.monto_prometido, monedaCodigo(promesa))}
        {progreso && (
          <Tooltip title={`Cobrado ${fmtMoneda(progreso.cobrado, monedaCodigo(promesa))} de ${fmtMoneda(promesa.monto_prometido, monedaCodigo(promesa))}`} enterDelay={300}>
            <Box sx={{ mt: 0.5 }}>
              <LinearProgress
                variant="determinate"
                value={progreso.pct}
                color={progreso.pct >= 100 ? "success" : "info"}
                sx={{ height: 4, borderRadius: 2 }}
              />
              <Typography variant="caption" color="text.secondary" sx={{ fontWeight: 400 }}>
                Cobrado {progreso.pct}%
              </Typography>
            </Box>
          </Tooltip>
        )}
      </TableCell>
      <TableCell align="center" onClick={onOpenCliente}>
        <Stack direction="row" spacing={0.5} alignItems="center" justifyContent="center">
          <Tooltip title={estadoMeta.tooltip ?? ""} enterDelay={300}>
            <Chip size="small" label={estadoMeta.label} color={estadoMeta.color} />
          </Tooltip>
          {evMeta && (
            <Tooltip title={evMeta.label} enterDelay={300}>
              <Box component="span" sx={{ display: "inline-flex", alignItems: "center", color: "text.secondary" }}>
                <Icon icon={evMeta.icon} width={16} />
              </Box>
            </Tooltip>
          )}
        </Stack>
      </TableCell>
      <TableCell align="center" sx={{ p: 0.5 }}>
        {visibleActions.length > 0 && (
          <>
            <IconButton
              size="small"
              onClick={(e) => {
                e.stopPropagation();
                setMenuAnchor(e.currentTarget);
              }}
            >
              <Icon icon="mdi:dots-vertical" width={18} />
            </IconButton>
            <Menu
              anchorEl={menuAnchor}
              open={!!menuAnchor}
              onClose={() => setMenuAnchor(null)}
            >
              {visibleActions.map((a) => (
                <MenuItem
                  key={a.key}
                  onClick={(e) => {
                    e.stopPropagation();
                    setMenuAnchor(null);
                    a.onClick(promesa);
                  }}
                  sx={{ color: a.color ? `${a.color}.main` : "inherit", fontSize: "0.85rem" }}
                >
                  <Box sx={{ display: "inline-flex", mr: 1 }}>{a.icon}</Box>
                  {a.label}
                </MenuItem>
              ))}
            </Menu>
          </>
        )}
      </TableCell>
    </TableRow>
  );
}
