import { Icon } from "@iconify/react";
import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Alert,
  Autocomplete,
  Box,
  Button,
  Checkbox,
  Chip,
  CircularProgress,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider,
  FormControlLabel,
  IconButton,
  MenuItem,
  Paper,
  Step,
  StepLabel,
  Stepper,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  TextField,
  Tooltip,
  Typography,
} from "@mui/material";
import { Fragment, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { getCliente, searchClientes } from "../../../api/clientes.service";
import {
  useComprasCompensablesQuery,
  useCreateReciboMultiMutation,
  useFacturasPendientesQuery,
  useGastosDisponiblesQuery,
  useNcDisponiblesQuery,
  usePreviewReciboMultiMutation,
  useRecibosConfigQuery,
  useSaldosFavorQuery,
} from "../../../tanstack/RecibosMultiStack";
import { MonedaInput } from "../../common/MonedaInput";
import { BancoSelect } from "../../_standards";
import { RetencionFormModal } from "./RetencionFormModal";
import { usePermission } from "../../../hooks/usePermission";
import { useMediosPagoEmpresaQuery } from "../../../tanstack/PagosStack";
import { useTesCuentasQuery } from "../../../tanstack/TesoreriaStack";
import { hoyISO } from "../../../utils/fecha";

// ─── Helpers ──────────────────────────────────────────────────────────────────
const fmtGs = (v) =>
  `Gs. ${Number(v || 0).toLocaleString("es-PY", { maximumFractionDigits: 0 })}`;

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

const STEPS = ["Cliente y facturas", "NC y retenciones", "Medios de pago", "Confirmación"];

// Simula la imputación FIFO del backend: dado un monto y las cuotas ordenadas
// por antigüedad, devuelve cuánto se aplica a cada cuota.
function calcularImputacionFIFO(cuotas = [], importe) {
  const total = Number(importe) || 0;
  if (!cuotas.length || total <= 0) return {};
  const orden = [...cuotas].sort((a, b) => {
    const fa = new Date(a.fecha_vencimiento ?? 0).getTime();
    const fb = new Date(b.fecha_vencimiento ?? 0).getTime();
    if (fa !== fb) return fa - fb;
    return (a.nro_cuota ?? 0) - (b.nro_cuota ?? 0);
  });
  let restante = total;
  const out = {};
  for (const c of orden) {
    if (restante <= 0) break;
    const saldo = Number(c.saldo_pendiente) || 0;
    if (saldo <= 0) continue;
    const aplicar = Math.min(saldo, restante);
    out[c.id] = aplicar;
    restante -= aplicar;
  }
  return out;
}

// Mapeo SIFEN código → tipo interno del wizard
// SIFEN: 1=Efectivo, 2=Cheque, 3=Tarjeta crédito, 4=Tarjeta débito, 5=Transferencia,
//        6=Giro, 7=Billetera electrónica, 8=Tarjeta empresarial, 9=Vale,
//        10=Retención, 11=Pago por anticipo, 12=Valor fiscal, 13=Valor comercial
const SIFEN_A_TIPO = {
  1: "EFECTIVO",
  2: "CHEQUE",
  3: "TARJETA",
  4: "TARJETA",
  5: "TRANSFERENCIA",
  7: "TRANSFERENCIA",
  8: "TARJETA",
};
const TIPO_LABELS = {
  EFECTIVO:      "Efectivo",
  TRANSFERENCIA: "Transferencia",
  CHEQUE:        "Cheque",
  TARJETA:       "Tarjeta",
  DEPOSITO:      "Depósito bancario",
  SALDO_FAVOR:   "Saldo a favor del cliente",
};
// Internos: siempre visibles (no dependen de SIFEN)
const TIPOS_INTERNOS = ["DEPOSITO", "SALDO_FAVOR"];

// ─── Paso 1: Cliente + Facturas ───────────────────────────────────────────────
function StepClienteFacturas({ clienteId, setClienteId, clienteOpts, setClienteOpts, selFacturas, setSelFacturas, importesParciales, setImportesParciales }) {
  const timerRef = useRef(null);
  const { data: facturas = [], isLoading: loadingFact } = useFacturasPendientesQuery(clienteId);
  const [expandidas, setExpandidas] = useState({});
  const toggleExpand = (id) => setExpandidas((p) => ({ ...p, [id]: !p[id] }));

  const handleInputChange = (v) => {
    clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      if (!v) return;
      searchClientes(v, 20)
        .then((r) => setClienteOpts(r?.data ?? r ?? []))
        .catch(() => {});
    }, 300);
  };

  const toggleFactura = (f) => {
    setSelFacturas((prev) =>
      prev.find((x) => x.id === f.id)
        ? prev.filter((x) => x.id !== f.id)
        : [...prev, f]
    );
    setImportesParciales((prev) => {
      const next = { ...prev };
      if (next[f.id]) delete next[f.id];
      return next;
    });
  };

  const setParcial = (facturaId, val) =>
    setImportesParciales((prev) => ({ ...prev, [facturaId]: val }));

  return (
    <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
      <Alert severity="info" icon={<Icon icon="mdi:information-outline" />} sx={{ "& .MuiAlert-message": { fontSize: "0.8125rem" } }}>
        <strong>Cómo funciona este paso:</strong> tildá las facturas a cobrar. Opcionalmente ingresá un <em>importe parcial</em> para cobrar menos que el saldo total — si lo dejás vacío, se cobra todo el saldo.
        <br />
        En facturas a crédito, el importe se imputa <strong>FIFO</strong>: primero a la cuota más vencida y desborda a las siguientes. Expandí la factura (▸) para ver en vivo qué cuotas se van a pagar.
      </Alert>

      <Autocomplete
        options={clienteOpts}
        getOptionLabel={(o) => {
          const p = o?.personas ?? {};
          const nombre = p.razon_social ?? o?.nombre_fantasia ?? o?.razon_social ?? "";
          const ruc = p.ruc ? (p.dv ? `${p.ruc}-${p.dv}` : `${p.ruc}`) : null;
          const doc = !ruc && p.nro_documento ? p.nro_documento : null;
          const id = ruc ? `RUC ${ruc}` : doc ? `CI ${doc}` : null;
          return id ? `${nombre} — ${id}` : nombre;
        }}
        renderOption={(props, o) => {
          const p = o?.personas ?? {};
          const nombre = p.razon_social ?? o?.nombre_fantasia ?? o?.razon_social ?? "—";
          const ruc = p.ruc ? (p.dv ? `${p.ruc}-${p.dv}` : `${p.ruc}`) : null;
          const doc = p.nro_documento;
          return (
            <li {...props} key={o.id}>
              <Box sx={{ display: "flex", flexDirection: "column", py: 0.25 }}>
                <Typography variant="body2" fontWeight={600}>{nombre}</Typography>
                <Typography variant="caption" color="text.secondary">
                  {ruc ? `RUC ${ruc}` : doc ? `CI ${doc}` : "Sin identificación"}
                </Typography>
              </Box>
            </li>
          );
        }}
        isOptionEqualToValue={(o, v) => String(o.id) === String(v?.id ?? v)}
        onChange={(_, opt) => { setClienteId(opt?.id ?? null); setSelFacturas([]); setImportesParciales({}); }}
        onInputChange={(_, v, reason) => { if (reason === "input") handleInputChange(v); }}
        filterOptions={(x) => x}
        size="small"
        fullWidth
        renderInput={(params) => (
          <TextField {...params} label="Cliente" placeholder="Buscar por razón social, RUC o documento…" helperText="Podés escribir parte del nombre, RUC o CI" />
        )}
      />

      {clienteId && (
        <>
          <Typography variant="subtitle2">
            Facturas con saldo pendiente
            {loadingFact && <CircularProgress size={14} sx={{ ml: 1 }} />}
          </Typography>

          {!loadingFact && facturas.length === 0 && (
            <Alert severity="info">Este cliente no tiene facturas con saldo pendiente.</Alert>
          )}

          {facturas.length > 0 && (
            <TableContainer component={Paper} variant="outlined">
              <Table size="small">
                <TableHead>
                  <TableRow sx={{ "& th": { fontWeight: 600, bgcolor: "action.hover" } }}>
                    <TableCell padding="checkbox" />
                    <TableCell padding="checkbox" />
                    <TableCell>Factura</TableCell>
                    <TableCell>Fecha emisión</TableCell>
                    <TableCell>Fecha venc.</TableCell>
                    <TableCell align="right">Total</TableCell>
                    <TableCell align="right">Saldo</TableCell>
                    <TableCell align="right">Importe parcial</TableCell>
                  </TableRow>
                </TableHead>
                <TableBody>
                  {facturas.map((f) => {
                    const sel = !!selFacturas.find((x) => x.id === f.id);
                    const tieneCuotas = Array.isArray(f.cuotas) && f.cuotas.length > 0;
                    const esCredito = String(f.condicion_codigo) === "2" || tieneCuotas;
                    const exp = !!expandidas[f.id];
                    return (
                      <Fragment key={f.id}>
                        <TableRow selected={sel} hover sx={{ cursor: "pointer" }} onClick={() => toggleFactura(f)}>
                          <TableCell padding="checkbox" onClick={(e) => e.stopPropagation()}>
                            {esCredito && tieneCuotas && (
                              <IconButton size="small" onClick={() => toggleExpand(f.id)} aria-label={exp ? "Colapsar cuotas" : "Ver cuotas"}>
                                <Icon icon={exp ? "mdi:chevron-down" : "mdi:chevron-right"} width={16} />
                              </IconButton>
                            )}
                          </TableCell>
                          <TableCell padding="checkbox">
                            <Checkbox checked={sel} size="small" onChange={() => toggleFactura(f)} onClick={(e) => e.stopPropagation()} />
                          </TableCell>
                          <TableCell sx={{ fontFamily: "monospace" }}>
                            {f.numero}
                            {esCredito ? (
                              <Chip size="small" label="Crédito" variant="outlined" sx={{ ml: 1, height: 18, fontSize: "0.65rem" }} />
                            ) : f.cobro_diferido ? (
                              <Chip size="small" label="Cont. diferido" color="warning" variant="outlined" sx={{ ml: 1, height: 18, fontSize: "0.65rem" }} />
                            ) : (
                              <Chip size="small" label="Contado" variant="outlined" sx={{ ml: 1, height: 18, fontSize: "0.65rem" }} />
                            )}
                          </TableCell>
                          <TableCell>{fmtFecha(f.fecha_emision ?? f.fecha)}</TableCell>
                          <TableCell>{fmtFecha(f.fecha_vencimiento)}</TableCell>
                          <TableCell align="right">{fmtGs(f.total_factura)}</TableCell>
                          <TableCell align="right" sx={{ fontWeight: 600 }}>{fmtGs(f.saldo_pendiente)}</TableCell>
                          <TableCell align="right" onClick={(e) => e.stopPropagation()}>
                            {sel && (
                              <MonedaInput
                                size="small"
                                value={importesParciales[f.id] ?? ""}
                                onChange={(v) => setParcial(f.id, v)}
                                placeholder={String(f.saldo_pendiente)}
                                helperText={
                                  importesParciales[f.id]
                                    ? `Imputa ${fmtGs(importesParciales[f.id])} FIFO`
                                    : `Vacío = cobra todo (${fmtGs(f.saldo_pendiente)})`
                                }
                                sx={{ width: 180 }}
                              />
                            )}
                          </TableCell>
                        </TableRow>
                        {esCredito && exp && tieneCuotas && (() => {
                          const importeParcial = importesParciales[f.id];
                          const montoEfectivo = sel
                            ? (Number(importeParcial) > 0 ? Number(importeParcial) : Number(f.saldo_pendiente))
                            : 0;
                          const imputaciones = montoEfectivo > 0 ? calcularImputacionFIFO(f.cuotas, montoEfectivo) : {};
                          const totalImputado = Object.values(imputaciones).reduce((s, v) => s + v, 0);
                          return (
                            <TableRow>
                              <TableCell colSpan={8} sx={{ bgcolor: "action.hover", py: 1 }}>
                                <Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5, pl: 1 }}>
                                  <Typography variant="caption" color="text.secondary" sx={{ flex: 1 }}>
                                    Cuotas {sel && `— preview FIFO con ${fmtGs(montoEfectivo)}`}
                                  </Typography>
                                  {sel && montoEfectivo > 0 && (
                                    <Chip
                                      size="small"
                                      color="success"
                                      variant="outlined"
                                      label={`Se imputa: ${fmtGs(totalImputado)}`}
                                    />
                                  )}
                                </Box>
                                <Table size="small">
                                  <TableHead>
                                    <TableRow>
                                      <TableCell>Nº</TableCell>
                                      <TableCell>Vencimiento</TableCell>
                                      <TableCell align="right">Monto cuota</TableCell>
                                      <TableCell align="right">Saldo cuota</TableCell>
                                      {sel && <TableCell align="right">A imputar</TableCell>}
                                      <TableCell>Estado</TableCell>
                                    </TableRow>
                                  </TableHead>
                                  <TableBody>
                                    {f.cuotas.map((c) => {
                                      const vencida = c.fecha_vencimiento && new Date(c.fecha_vencimiento) < new Date() && Number(c.saldo_pendiente) > 0;
                                      const aImputar = imputaciones[c.id] ?? 0;
                                      const seCobra = aImputar > 0;
                                      const cubre = seCobra && aImputar >= Number(c.saldo_pendiente);
                                      return (
                                        <TableRow
                                          key={c.id}
                                          sx={{
                                            bgcolor: seCobra ? "rgba(34,197,94,0.12)" : "transparent",
                                            opacity: sel && !seCobra ? 0.55 : 1,
                                          }}
                                        >
                                          <TableCell>{c.nro_cuota}</TableCell>
                                          <TableCell>
                                            {fmtFecha(c.fecha_vencimiento)}
                                            {vencida && <Chip size="small" label="Vencida" color="error" variant="outlined" sx={{ ml: 1, height: 18, fontSize: "0.65rem" }} />}
                                          </TableCell>
                                          <TableCell align="right">{fmtGs(c.monto_cuota)}</TableCell>
                                          <TableCell align="right" sx={{ fontWeight: 600 }}>{fmtGs(c.saldo_pendiente)}</TableCell>
                                          {sel && (
                                            <TableCell align="right" sx={{ fontWeight: 600, color: seCobra ? "success.main" : "text.disabled" }}>
                                              {seCobra ? (
                                                <Box sx={{ display: "flex", alignItems: "center", gap: 0.5, justifyContent: "flex-end" }}>
                                                  {fmtGs(aImputar)}
                                                  {cubre && <Icon icon="mdi:check-circle" width={14} />}
                                                </Box>
                                              ) : "—"}
                                            </TableCell>
                                          )}
                                          <TableCell>
                                            <Chip size="small" label={c.estado ?? "—"} variant="outlined" />
                                          </TableCell>
                                        </TableRow>
                                      );
                                    })}
                                  </TableBody>
                                </Table>
                              </TableCell>
                            </TableRow>
                          );
                        })()}
                      </Fragment>
                    );
                  })}
                </TableBody>
              </Table>
            </TableContainer>
          )}
        </>
      )}
    </Box>
  );
}

// ─── Paso 2: NC + Retenciones ─────────────────────────────────────────────────
function StepNcRetenciones({ clienteId, selFacturas, selNcs, setSelNcs, retenciones, setRetenciones, facturasCompraAplicadas, setFacturasCompraAplicadas, gastosAplicados, setGastosAplicados, canNcFlexible, canRetencion, canGastos, esAgenteRetencion = false }) {
  const { data: ncs = [] } = useNcDisponiblesQuery(clienteId);
  const { data: saldosFavor = [] } = useSaldosFavorQuery(clienteId);
  const { data: comprasCompensables = [] } = useComprasCompensablesQuery(clienteId);
  const { data: gastosDisponibles = [], isLoading: loadingGastos } = useGastosDisponiblesQuery(
    { cliente_id: clienteId },
    { enabled: canGastos && !!clienteId },
  );
  // "Todos, pero priorizado": el backend marca es_del_cliente y ordena arriba los del
  // mismo RUC del cliente. Este toggle permite además filtrar solo esos.
  const [soloGastosCliente, setSoloGastosCliente] = useState(false);
  const hayGastosDelCliente = gastosDisponibles.some((g) => g.es_del_cliente);
  const gastosMostrados = soloGastosCliente
    ? gastosDisponibles.filter((g) => g.es_del_cliente)
    : gastosDisponibles;

  const [retencionFactura, setRetencionFactura] = useState(null); // factura para la que se agrega ret.

  const toggleNc = (nc) =>
    setSelNcs((prev) =>
      prev.find((x) => x.id === nc.id) ? prev.filter((x) => x.id !== nc.id) : [...prev, nc]
    );

  const addRetencion = (data) => {
    setRetenciones((prev) => [
      ...prev.filter((r) => r.factura_id !== retencionFactura.id || r._key !== data._key),
      { ...data, factura_id: retencionFactura.id, _key: Date.now() },
    ]);
    setRetencionFactura(null);
  };

  const removeRetencion = (key) =>
    setRetenciones((prev) => prev.filter((r) => r._key !== key));

  const totalNcSel = selNcs.reduce((s, nc) => s + Number(nc.saldo_disponible ?? 0), 0);
  const totalRetSel = retenciones.reduce((s, r) => s + Number(r.monto ?? 0), 0);

  const toggleCompra = (compra) => {
    setFacturasCompraAplicadas((prev) => {
      const existe = prev.find((x) => x.compra_cab_id === compra.id);
      if (existe) return prev.filter((x) => x.compra_cab_id !== compra.id);
      return [...prev, { compra_cab_id: compra.id, monto: Number(compra.saldo_pendiente) || 0, _compra: compra }];
    });
  };

  const setCompraMonto = (compraId, val) =>
    setFacturasCompraAplicadas((prev) =>
      prev.map((x) => (x.compra_cab_id === compraId ? { ...x, monto: val } : x))
    );

  const totalComprasSel = facturasCompraAplicadas.reduce((s, c) => s + Number(c.monto ?? 0), 0);

  const toggleGasto = (g) => {
    setGastosAplicados((prev) => {
      const existe = prev.find((x) => x.gasto_id === g.gasto_id);
      if (existe) return prev.filter((x) => x.gasto_id !== g.gasto_id);
      return [...prev, { gasto_id: g.gasto_id, monto_aplicado: Number(g.saldo_pendiente) || 0, _gasto: g }];
    });
  };

  const setGastoMonto = (id, val) =>
    setGastosAplicados((prev) =>
      prev.map((x) => (x.gasto_id === id ? { ...x, monto_aplicado: val } : x))
    );

  const totalGastosSel = gastosAplicados.reduce((s, g) => s + Number(g.monto_aplicado ?? 0), 0);

  return (
    <Box sx={{ display: "flex", flexDirection: "column", gap: 3 }}>
      <Alert severity="info" icon={<Icon icon="mdi:information-outline" />} sx={{ "& .MuiAlert-message": { fontSize: "0.8125rem" } }}>
        <strong>Cómo funciona este paso:</strong> es <em>opcional</em>. Acá podés:
        <Box component="ul" sx={{ m: 0, pl: 2.5, mt: 0.5 }}>
          <li><strong>Notas de crédito y saldos a favor</strong> — descuentan del total a cobrar como si fueran un pago.</li>
          <li><strong>Retenciones IVA / Renta</strong> — certificados que el cliente te entrega; reducen lo que cobrás en efectivo y se cargan al Libro de Retenciones automáticamente.</li>
          {canGastos && <li><strong>Gastos documentados</strong> — comisiones bancarias, fletes u otros con CxP pendiente; se descuentan del cobro y la CxP queda cancelada automáticamente.</li>}
        </Box>
        Si el cliente no entrega ni NC ni retenciones, dejá todo vacío y avanzá.
      </Alert>

      <Accordion
        disableGutters
        elevation={0}
        sx={{
          bgcolor: "transparent",
          border: 1,
          borderColor: "divider",
          borderRadius: 1,
          "&:before": { display: "none" },
        }}
      >
        <AccordionSummary
          expandIcon={<Icon icon="mdi:chevron-down" />}
          sx={{ minHeight: 36, "& .MuiAccordionSummary-content": { my: 0.5 } }}
        >
          <Typography variant="caption" color="text.secondary">
            <Icon icon="mdi:help-circle-outline" style={{ verticalAlign: "middle", marginRight: 4 }} />
            Detalles contables y guía de retenciones
          </Typography>
        </AccordionSummary>
        <AccordionDetails sx={{ pt: 0 }}>
          <Box sx={{ fontSize: "0.75rem", color: "text.secondary", display: "flex", alignItems: "flex-start", gap: 0.5, mb: 1 }}>
            <Icon icon="mdi:information-outline" style={{ marginTop: 2, flexShrink: 0 }} />
            <span>
              <strong>Contable:</strong> NC modo <em>ESTRICTO</em> (aplicada a su factura origen) no genera línea en el asiento — la NC al emitirse ya descargó CxC. NC modo <em>FLEXIBLE</em> (a otra factura del cliente) sí genera línea contra cuenta puente.
            </span>
          </Box>
          <Box sx={{ fontSize: "0.75rem", color: "text.secondary", display: "flex", alignItems: "flex-start", gap: 0.5 }}>
            <Icon icon="mdi:receipt-text-check-outline" style={{ marginTop: 2, flexShrink: 0 }} />
            <span>
              <strong>¿Cuándo cargar retención?</strong> Solo cuando el cliente <em>es agente de retención</em> designado por SET y te entrega el <strong>comprobante de retención</strong> (físico o electrónico) al momento de pagar. Vos no decidís retener — lo hace el cliente.
              <Box component="ul" sx={{ m: 0, pl: 2, mt: 0.5 }}>
                <li><strong>Efecto:</strong> cobrás menos en efectivo, pero la factura queda <strong>totalmente saldada</strong> (la parte retenida cuenta como pago vía SET).</li>
                <li><strong>Destino:</strong> queda en el <strong>Libro de Retenciones Recibidas</strong>. Te sirve como crédito de IVA (DJ mensual) y pago a cuenta de Renta (cierre anual).</li>
                <li><strong>Datos requeridos:</strong> tipo (IVA/Renta), monto, número de comprobante, timbrado y fecha.</li>
                <li><strong>Si no trae comprobante:</strong> no cargues nada — no es opcional ni a criterio del cobrador.</li>
              </Box>
            </span>
          </Box>
        </AccordionDetails>
      </Accordion>

      {/* Notas de crédito */}
      {(() => {
        const selFacturaIds = new Set(selFacturas.map((f) => f.id));
        const ncsVisibles = ncs.filter((nc) => {
          const modo = (nc.rec_modo_aplicacion || "ESTRICTO").toUpperCase();
          if (modo === "ESTRICTO") return nc.factura_cab_id && selFacturaIds.has(nc.factura_cab_id);
          return canNcFlexible;
        });
        if (!canNcFlexible && ncsVisibles.length === 0) return null;
        return (
        <Box>
          <Typography variant="subtitle2" gutterBottom>
            Notas de crédito disponibles
            {canNcFlexible && (
              <Chip size="small" label="Modo flexible" color="primary" variant="outlined" sx={{ ml: 1 }} />
            )}
          </Typography>

          {ncsVisibles.length === 0 && saldosFavor.length === 0 && (
            <Alert severity="info">No hay NC ni saldos a favor disponibles para este cliente.</Alert>
          )}

          {ncsVisibles.length > 0 && (
            <TableContainer component={Paper} variant="outlined">
              <Table size="small">
                <TableHead>
                  <TableRow sx={{ "& th": { fontWeight: 600, bgcolor: "action.hover" } }}>
                    <TableCell padding="checkbox" />
                    <TableCell>Nota de crédito</TableCell>
                    <TableCell>Fecha</TableCell>
                    <TableCell>Factura vinculada</TableCell>
                    <TableCell>Modo</TableCell>
                    <TableCell align="right">Saldo disponible</TableCell>
                  </TableRow>
                </TableHead>
                <TableBody>
                  {ncsVisibles.map((nc) => {
                    const sel = !!selNcs.find((x) => x.id === nc.id);
                    const modo = (nc.rec_modo_aplicacion || "ESTRICTO").toUpperCase();
                    return (
                      <TableRow key={nc.id} selected={sel} hover sx={{ cursor: "pointer" }} onClick={() => toggleNc(nc)}>
                        <TableCell padding="checkbox">
                          <Checkbox checked={sel} size="small" />
                        </TableCell>
                        <TableCell sx={{ fontFamily: "monospace" }}>{nc.numero}</TableCell>
                        <TableCell>{fmtFecha(nc.fecha)}</TableCell>
                        <TableCell sx={{ fontFamily: "monospace", fontSize: "0.8125rem" }}>
                          {nc.factura_numero || (
                            <Typography variant="caption" color="text.secondary">— sin factura origen —</Typography>
                          )}
                        </TableCell>
                        <TableCell>
                          <Chip
                            size="small"
                            label={modo}
                            color={modo === "ESTRICTO" ? "primary" : "info"}
                            variant="outlined"
                          />
                        </TableCell>
                        <TableCell align="right" sx={{ fontWeight: 600, color: "success.main" }}>
                          {fmtGs(nc.saldo_disponible)}
                        </TableCell>
                      </TableRow>
                    );
                  })}
                </TableBody>
              </Table>
            </TableContainer>
          )}

          {totalNcSel > 0 && (
            <Typography variant="body2" color="success.main" sx={{ mt: 1 }}>
              Total NC a aplicar: <strong>{fmtGs(totalNcSel)}</strong>
            </Typography>
          )}
        </Box>
        );
      })()}

      <Divider />

      {/* Retenciones recibidas */}
      {canRetencion && (
        <Box
          sx={
            esAgenteRetencion
              ? {
                  p: 1.5,
                  borderRadius: 1,
                  border: 1,
                  borderColor: "warning.main",
                  bgcolor: (t) =>
                    t.palette.mode === "dark"
                      ? "rgba(255, 167, 38, 0.08)"
                      : "rgba(255, 167, 38, 0.12)",
                }
              : undefined
          }
        >
          <Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5 }}>
            <Typography variant="subtitle2">Retenciones recibidas por factura</Typography>
            {esAgenteRetencion && (
              <Chip
                size="small"
                color="warning"
                icon={<Icon icon="mdi:alert-circle-outline" />}
                label="Cliente agente de retención"
              />
            )}
          </Box>
          {esAgenteRetencion && retenciones.length === 0 && (
            <Alert severity="warning" sx={{ mb: 1, "& .MuiAlert-message": { fontSize: "0.8125rem" } }}>
              Este cliente está marcado como <strong>agente de retención</strong>. Si te entregó comprobante(s) al pagar, cargalos acá antes de continuar.
            </Alert>
          )}

          {selFacturas.length === 0 && (
            <Alert severity="info">Seleccioná al menos una factura en el paso anterior.</Alert>
          )}

          {selFacturas.map((f) => {
            const rets = retenciones.filter((r) => r.factura_id === f.id);
            return (
              <Box key={f.id} sx={{ mb: 2 }}>
                <Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5 }}>
                  <Typography variant="body2" fontWeight={600}>{f.numero}</Typography>
                  <Typography variant="caption" color="text.secondary">Saldo: {fmtGs(f.saldo_pendiente)}</Typography>
                  <Button size="small" startIcon={<Icon icon="mdi:plus" />} onClick={() => setRetencionFactura(f)}>
                    Agregar retención
                  </Button>
                </Box>
                {rets.map((r) => (
                  <Box key={r._key} sx={{ display: "flex", alignItems: "center", gap: 1, pl: 2, py: 0.25 }}>
                    <Chip size="small" label={r.tipo} />
                    <Typography variant="body2">{r.numero_comprobante}</Typography>
                    <Typography variant="body2" color="warning.main">{fmtGs(r.monto)}</Typography>
                    <IconButton size="small" onClick={() => removeRetencion(r._key)}>
                      <Icon icon="mdi:close" />
                    </IconButton>
                  </Box>
                ))}
              </Box>
            );
          })}

          {totalRetSel > 0 && (
            <Typography variant="body2" color="warning.main">
              Total retenciones: <strong>{fmtGs(totalRetSel)}</strong>
            </Typography>
          )}
        </Box>
      )}

      {clienteId && (
        <>
          <Divider />
          <Box>
            <Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5 }}>
              <Typography variant="subtitle2">Facturas por acuerdo comercial</Typography>
              <Chip
                size="small"
                color="info"
                icon={<Icon icon="mdi:swap-horizontal" />}
                label="Compensación"
              />
            </Box>
            <Alert severity="info" sx={{ mb: 1, "& .MuiAlert-message": { fontSize: "0.8125rem" } }}>
              Facturas que el cliente le emitió a la empresa (mismo titular como proveedor). El monto se compensa contra la deuda del cliente — FIFO sobre las cuotas pendientes de la compra.
            </Alert>
            {comprasCompensables.length === 0 ? (
              <Alert severity="info">
                Este cliente no tiene facturas de compra (mismo RUC como proveedor) con saldo pendiente para compensar.
              </Alert>
            ) : (
            <TableContainer component={Paper} variant="outlined">
              <Table size="small">
                <TableHead>
                  <TableRow sx={{ "& th": { fontWeight: 600, bgcolor: "action.hover" } }}>
                    <TableCell padding="checkbox" />
                    <TableCell>Factura</TableCell>
                    <TableCell>Fecha</TableCell>
                    <TableCell align="right">Total</TableCell>
                    <TableCell align="right">Saldo</TableCell>
                    <TableCell align="right">Monto a compensar</TableCell>
                  </TableRow>
                </TableHead>
                <TableBody>
                  {comprasCompensables.map((c) => {
                    const aplicada = facturasCompraAplicadas.find((x) => x.compra_cab_id === c.id);
                    const sel = !!aplicada;
                    return (
                      <TableRow key={c.id} selected={sel} hover sx={{ cursor: "pointer" }} onClick={() => toggleCompra(c)}>
                        <TableCell padding="checkbox" onClick={(e) => e.stopPropagation()}>
                          <Checkbox checked={sel} size="small" onChange={() => toggleCompra(c)} />
                        </TableCell>
                        <TableCell sx={{ fontFamily: "monospace" }}>{c.numero}</TableCell>
                        <TableCell>{fmtFecha(c.fecha_emision)}</TableCell>
                        <TableCell align="right">{fmtGs(c.total)}</TableCell>
                        <TableCell align="right" sx={{ fontWeight: 600 }}>{fmtGs(c.saldo_pendiente)}</TableCell>
                        <TableCell align="right" onClick={(e) => e.stopPropagation()}>
                          {sel && (
                            <MonedaInput
                              size="small"
                              value={aplicada.monto}
                              onChange={(v) => setCompraMonto(c.id, v)}
                              sx={{ width: 180 }}
                              helperText={`Máx: ${fmtGs(c.saldo_pendiente)}`}
                            />
                          )}
                        </TableCell>
                      </TableRow>
                    );
                  })}
                </TableBody>
              </Table>
            </TableContainer>
            )}
            {totalComprasSel > 0 && (
              <Typography variant="body2" color="info.main" sx={{ mt: 1 }}>
                Total a compensar: <strong>{fmtGs(totalComprasSel)}</strong>
              </Typography>
            )}
          </Box>
        </>
      )}

      {canGastos && (
        <>
          <Divider />
          <Box>
            <Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5, flexWrap: "wrap" }}>
              <Typography variant="subtitle2">Gastos documentados</Typography>
              <Chip size="small" color="warning" icon={<Icon icon="mdi:receipt-text-minus-outline" />} label="Descuento del cobro" />
              {hayGastosDelCliente && (
                <FormControlLabel
                  sx={{ ml: "auto", mr: 0 }}
                  control={
                    <Checkbox
                      size="small"
                      checked={soloGastosCliente}
                      onChange={(e) => setSoloGastosCliente(e.target.checked)}
                    />
                  }
                  label={<Typography variant="caption">Solo del cliente (mismo RUC)</Typography>}
                />
              )}
            </Box>
            <Alert severity="info" sx={{ mb: 1, "& .MuiAlert-message": { fontSize: "0.8125rem" } }}>
              Gastos con CxP pendiente (comisiones bancarias, fletes, etc.) descontados del monto a cobrar. El sistema cancela la CxP automáticamente al confirmar.
            </Alert>
            {loadingGastos ? (
              <CircularProgress size={20} />
            ) : gastosDisponibles.length === 0 ? (
              <Alert severity="info">No hay gastos con CxP pendiente disponibles.</Alert>
            ) : (
              <TableContainer component={Paper} variant="outlined">
                <Table size="small">
                  <TableHead>
                    <TableRow sx={{ "& th": { fontWeight: 600, bgcolor: "action.hover" } }}>
                      <TableCell padding="checkbox" />
                      <TableCell>Proveedor</TableCell>
                      <TableCell>Factura</TableCell>
                      <TableCell>Fecha</TableCell>
                      <TableCell align="right">CxP disponible</TableCell>
                      <TableCell align="right">Monto a descontar</TableCell>
                    </TableRow>
                  </TableHead>
                  <TableBody>
                    {gastosMostrados.length === 0 ? (
                      <TableRow>
                        <TableCell colSpan={6}>
                          <Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
                            No hay gastos del mismo RUC del cliente. Desmarcá el filtro para ver todos.
                          </Typography>
                        </TableCell>
                      </TableRow>
                    ) : gastosMostrados.map((g) => {
                      const aplicado = gastosAplicados.find((x) => x.gasto_id === g.gasto_id);
                      const sel = !!aplicado;
                      return (
                        <TableRow key={g.gasto_id} selected={sel} hover sx={{ cursor: "pointer" }} onClick={() => toggleGasto(g)}>
                          <TableCell padding="checkbox" onClick={(e) => e.stopPropagation()}>
                            <Checkbox checked={sel} size="small" onChange={() => toggleGasto(g)} />
                          </TableCell>
                          <TableCell>
                            <Box sx={{ display: "flex", alignItems: "center", gap: 0.75, flexWrap: "wrap" }}>
                              {g.proveedor_razon_social}
                              {g.es_del_cliente && (
                                <Chip
                                  size="small"
                                  color="info"
                                  variant="outlined"
                                  icon={<Icon icon="mdi:account-check-outline" />}
                                  label="Mismo RUC del cliente"
                                />
                              )}
                            </Box>
                          </TableCell>
                          <TableCell sx={{ fontFamily: "monospace" }}>{g.numero_documento}</TableCell>
                          <TableCell>{fmtFecha(g.fecha_emision)}</TableCell>
                          <TableCell align="right" sx={{ fontWeight: 600 }}>{fmtGs(g.saldo_pendiente)}</TableCell>
                          <TableCell align="right" onClick={(e) => e.stopPropagation()}>
                            {sel && (
                              <MonedaInput
                                size="small"
                                value={aplicado.monto_aplicado}
                                onChange={(v) => setGastoMonto(g.gasto_id, v)}
                                sx={{ width: 160 }}
                                helperText={`Máx: ${fmtGs(g.saldo_pendiente)}`}
                              />
                            )}
                          </TableCell>
                        </TableRow>
                      );
                    })}
                  </TableBody>
                </Table>
              </TableContainer>
            )}
            {totalGastosSel > 0 && (
              <Typography variant="body2" color="warning.main" sx={{ mt: 1 }}>
                Total gastos a descontar: <strong>{fmtGs(totalGastosSel)}</strong>
              </Typography>
            )}
          </Box>
        </>
      )}

      <RetencionFormModal
        open={!!retencionFactura}
        facturaNumero={retencionFactura?.numero ?? ""}
        onConfirm={addRetencion}
        onClose={() => setRetencionFactura(null)}
      />
    </Box>
  );
}

// ─── Paso 3: Medios de pago ───────────────────────────────────────────────────
function StepMediosPago({ mediosPago, setMediosPago, totalAPagar, totalFacturas = 0, totalNcSel = 0, totalRetSel = 0, totalComprasSel = 0, totalGastosSel = 0 }) {
  const totalMedios = mediosPago.reduce((s, m) => s + Number(m.monto || 0), 0);
  const totalCubierto = totalMedios + totalNcSel + totalRetSel;
  const diferencia = totalAPagar - totalMedios;

  const { data: mediosEmpresa = [] } = useMediosPagoEmpresaQuery();
  const { data: cuentasData } = useTesCuentasQuery();
  const cuentasTes = (Array.isArray(cuentasData) ? cuentasData : (cuentasData?.data || []))
    .filter((c) => c.activo !== false);
  const tiposDisponibles = useMemo(() => {
    const activos = new Set();
    for (const m of mediosEmpresa) {
      if (m.activo && SIFEN_A_TIPO[m.codigo]) activos.add(SIFEN_A_TIPO[m.codigo]);
    }
    const orden = ["EFECTIVO", "TRANSFERENCIA", "CHEQUE", "TARJETA", "DEPOSITO", "SALDO_FAVOR"];
    return orden
      .filter((t) => activos.has(t) || TIPOS_INTERNOS.includes(t))
      .map((value) => ({ value, label: TIPO_LABELS[value] }));
  }, [mediosEmpresa]);

  const tipoDefault = tiposDisponibles[0]?.value ?? "EFECTIVO";

  const addMedio = () =>
    setMediosPago((prev) => {
      const cubierto = prev.reduce((s, m) => s + Number(m.monto || 0), 0);
      const falta = Math.max(0, Math.round(totalAPagar - cubierto));
      return [
        ...prev,
        { _key: Date.now(), tipo: tipoDefault, monto: falta > 0 ? falta : "", referencia: "" },
      ];
    });

  useEffect(() => {
    if (mediosPago.length === 0 && totalAPagar > 0 && tipoDefault) {
      setMediosPago([
        { _key: Date.now(), tipo: tipoDefault, monto: Math.round(totalAPagar), referencia: "" },
      ]);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [totalAPagar, tipoDefault]);

  const updateMedio = (key, field, val) =>
    setMediosPago((prev) => prev.map((m) => (m._key === key ? { ...m, [field]: val } : m)));

  const removeMedio = (key) =>
    setMediosPago((prev) => prev.filter((m) => m._key !== key));

  return (
    <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
      <Alert severity="info" icon={<Icon icon="mdi:information-outline" />} sx={{ "& .MuiAlert-message": { fontSize: "0.8125rem" } }}>
        <strong>Cómo funciona este paso:</strong> agregá uno o varios medios para cubrir el <strong>total a pagar</strong> (lo que falta después de aplicar NC, saldos a favor y retenciones).
        <Box component="ul" sx={{ m: 0, pl: 2.5, mt: 0.5 }}>
          <li><strong>Combinable</strong>: ej. parte en efectivo + parte en transferencia.</li>
          <li><strong>Cheques</strong> requieren número y banco; quedan registrados en Tesorería como pendientes hasta depositarse.</li>
          <li><strong>Sobrante</strong>: si pagás de más, el excedente queda como <em>saldo a favor</em> del cliente para futuros recibos.</li>
        </Box>
        El indicador "Balanceado ✓" debe quedar en verde antes de confirmar.
      </Alert>

      <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
        <Typography variant="subtitle2">Medios de pago</Typography>
        <Button size="small" startIcon={<Icon icon="mdi:plus" />} onClick={addMedio}>
          Agregar medio
        </Button>
      </Box>

      {mediosPago.length === 0 && (
        <Alert severity="info">Agregá al menos un medio de pago.</Alert>
      )}

      {mediosPago.map((m) => (
        <Box key={m._key} sx={{ border: m.tipo === "CHEQUE" ? "1px dashed" : "none", borderColor: "divider", borderRadius: 1, p: m.tipo === "CHEQUE" ? 1.5 : 0 }}>
          <Box sx={{ display: "flex", gap: 1, alignItems: "flex-start" }}>
            <TextField
              select
              label="Tipo"
              value={m.tipo}
              onChange={(e) => updateMedio(m._key, "tipo", e.target.value)}
              size="small"
              sx={{ width: 200 }}
            >
              {tiposDisponibles.map((t) => (
                <MenuItem key={t.value} value={t.value}>{t.label}</MenuItem>
              ))}
            </TextField>

            <MonedaInput
              label="Monto"
              value={m.monto}
              onChange={(v) => updateMedio(m._key, "monto", v)}
              size="small"
              sx={{ width: 180 }}
              helperText={m.tipo === "EFECTIVO" && diferencia > 0 ? `Sugerido: ${fmtGs(diferencia)}` : undefined}
            />

            {m.tipo !== "EFECTIVO" && m.tipo !== "SALDO_FAVOR" && (
              <TextField
                select
                label="Cuenta tesorería"
                value={m.cuenta_tesoreria_id ?? ""}
                onChange={(e) => updateMedio(m._key, "cuenta_tesoreria_id", e.target.value)}
                size="small"
                sx={{ width: 200 }}
                required
                helperText={m.tipo === "CHEQUE" ? "Cartera destino" : "Cuenta de ingreso"}
              >
                {cuentasTes.map((c) => (
                  <MenuItem key={c.id} value={c.id}>{c.nombre}</MenuItem>
                ))}
              </TextField>
            )}

            <TextField
              label="Referencia (opcional)"
              value={m.referencia}
              onChange={(e) => updateMedio(m._key, "referencia", e.target.value)}
              size="small"
              sx={{ flex: 1 }}
              helperText={m.tipo === "TRANSFERENCIA" ? "Ej: nro. transferencia" : ""}
            />

            <IconButton size="small" color="error" onClick={() => removeMedio(m._key)} sx={{ mt: 0.5 }}>
              <Icon icon="mdi:delete-outline" />
            </IconButton>
          </Box>

          {m.tipo === "CHEQUE" && (
            <Box sx={{ mt: 1.5, display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 1 }}>
              <TextField
                label="N° Cheque"
                value={m.cheque_numero ?? ""}
                onChange={(e) => updateMedio(m._key, "cheque_numero", e.target.value)}
                size="small"
                required
                helperText="Ej: 0012345"
              />
              <BancoSelect
                label="Banco emisor"
                required
                value={m.cheque_banco ?? ""}
                onChange={(v) => updateMedio(m._key, "cheque_banco", v)}
                helperText="Seleccioná o escribí"
              />
              <TextField
                label="Titular"
                value={m.cheque_titular ?? ""}
                onChange={(e) => updateMedio(m._key, "cheque_titular", e.target.value)}
                size="small"
                required
                helperText="Nombre del firmante"
              />
              <TextField
                label="N° Cuenta"
                value={m.cheque_cuenta ?? ""}
                onChange={(e) => updateMedio(m._key, "cheque_cuenta", e.target.value)}
                size="small"
                helperText="Opcional"
              />
              <TextField
                type="date"
                label="Fecha emisión"
                value={m.cheque_fecha_emision ?? ""}
                onChange={(e) => updateMedio(m._key, "cheque_fecha_emision", e.target.value)}
                size="small"
                required
                InputLabelProps={{ shrink: true }}
              />
              <TextField
                type="date"
                label="Fecha cobro / venc."
                value={m.cheque_fecha_vencimiento ?? ""}
                onChange={(e) => updateMedio(m._key, "cheque_fecha_vencimiento", e.target.value)}
                size="small"
                InputLabelProps={{ shrink: true }}
                helperText="Si es futura → diferido"
              />
            </Box>
          )}
        </Box>
      ))}

      <Divider />

      {totalFacturas > 0 && (
        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
          <Typography variant="body2" color="text.secondary">Total facturas:</Typography>
          <Typography variant="body2" color="text.secondary">{fmtGs(totalFacturas)}</Typography>
        </Box>
      )}
      {totalNcSel > 0 && (
        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
          <Typography variant="body2" color="text.secondary">− Notas de Crédito:</Typography>
          <Typography variant="body2" color="text.secondary">{fmtGs(totalNcSel)}</Typography>
        </Box>
      )}
      {totalRetSel > 0 && (
        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
          <Typography variant="body2" color="text.secondary">− Retenciones:</Typography>
          <Typography variant="body2" color="text.secondary">{fmtGs(totalRetSel)}</Typography>
        </Box>
      )}
      {totalComprasSel > 0 && (
        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
          <Typography variant="body2" color="text.secondary">− Facturas acuerdo comercial:</Typography>
          <Typography variant="body2" color="text.secondary">{fmtGs(totalComprasSel)}</Typography>
        </Box>
      )}
      {totalGastosSel > 0 && (
        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
          <Typography variant="body2" color="text.secondary">− Gastos documentados:</Typography>
          <Typography variant="body2" color="text.secondary">{fmtGs(totalGastosSel)}</Typography>
        </Box>
      )}
      <Box sx={{ display: "flex", justifyContent: "space-between" }}>
        <Typography variant="body2">Total a cobrar (medios de pago):</Typography>
        <Typography fontWeight={600}>{fmtGs(totalAPagar)}</Typography>
      </Box>
      <Box sx={{ display: "flex", justifyContent: "space-between" }}>
        <Typography variant="body2">Cubierto por medios:</Typography>
        <Typography fontWeight={600} color={totalMedios >= totalAPagar ? "success.main" : "warning.main"}>
          {fmtGs(totalMedios)}
        </Typography>
      </Box>
      {(totalNcSel > 0 || totalRetSel > 0) && (
        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
          <Typography variant="body2" color="text.secondary">
            Total cubierto (medios + NC{totalRetSel > 0 ? " + retenciones" : ""}):
          </Typography>
          <Typography variant="body2" fontWeight={600} color="text.secondary">
            {fmtGs(totalCubierto)}
          </Typography>
        </Box>
      )}
      {Math.abs(diferencia) > 0 && (
        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
          <Typography variant="body2" color={diferencia > 0 ? "error.main" : "success.main"}>
            {diferencia > 0 ? "Faltante:" : "Vuelto / saldo a favor:"}
          </Typography>
          <Typography fontWeight={600} color={diferencia > 0 ? "error.main" : "success.main"}>
            {fmtGs(Math.abs(diferencia))}
          </Typography>
        </Box>
      )}
    </Box>
  );
}

// ─── Paso 4: Confirmación ─────────────────────────────────────────────────────
function StepConfirmacion({ preview, isLoadingPreview, fechaEmision, setFechaEmision }) {
  if (isLoadingPreview) {
    return (
      <Box sx={{ display: "flex", justifyContent: "center", py: 4 }}>
        <CircularProgress />
      </Box>
    );
  }

  if (!preview) {
    return <Alert severity="error">No se pudo generar la vista previa. Revisá los pasos anteriores.</Alert>;
  }

  return (
    <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
      <TextField
        label="Fecha de emisión del recibo"
        type="date"
        size="small"
        value={fechaEmision}
        onChange={(e) => setFechaEmision(e.target.value)}
        InputLabelProps={{ shrink: true }}
        sx={{ maxWidth: 240 }}
        helperText="Por defecto, hoy. Modificá si necesitás registrar el recibo con otra fecha."
      />

      <Alert severity="warning" icon={<Icon icon="mdi:alert-circle-outline" />} sx={{ "& .MuiAlert-message": { fontSize: "0.8125rem" } }}>
        <strong>Último paso — revisá antes de confirmar:</strong>
        <Box component="ul" sx={{ m: 0, pl: 2.5, mt: 0.5 }}>
          <li>La imputación FIFO mostrada se aplicará exactamente así a las facturas y cuotas.</li>
          <li>Las retenciones quedarán en el <strong>Libro de Retenciones</strong> del mes del recibo.</li>
          <li>Si hay diferencia positiva, se registra como saldo a favor del cliente.</li>
          <li>Para revertir hay que <strong>anular</strong> el recibo — bloqueado si algún cheque ya fue depositado.</li>
        </Box>
      </Alert>

      {/* Resumen imputación */}
      <Typography variant="subtitle2">Imputación FIFO</Typography>
      <TableContainer component={Paper} variant="outlined">
        <Table size="small">
          <TableHead>
            <TableRow sx={{ "& th": { fontWeight: 600, bgcolor: "action.hover" } }}>
              <TableCell>Factura</TableCell>
              <TableCell align="right">Saldo anterior</TableCell>
              <TableCell align="right">Imputado</TableCell>
              <TableCell align="right">Saldo nuevo</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {(preview.facturas ?? []).map((f) => (
              <TableRow key={f.factura_id}>
                <TableCell sx={{ fontFamily: "monospace" }}>{f.factura_numero || "Sin número"}</TableCell>
                <TableCell align="right">{fmtGs(f.saldo_anterior)}</TableCell>
                <TableCell align="right" sx={{ color: "primary.main", fontWeight: 600 }}>{fmtGs(f.monto_imputado)}</TableCell>
                <TableCell align="right">{fmtGs(f.saldo_nuevo)}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </TableContainer>

      {/* Totales */}
      <Paper variant="outlined" sx={{ p: 2 }}>
        {[
          { label: "Total imputado", value: preview.total_imputado, color: "primary.main" },
          preview.total_nc > 0 && { label: "NC aplicadas", value: -preview.total_nc, color: "success.main" },
          preview.total_retenciones > 0 && { label: "Retenciones", value: -preview.total_retenciones, color: "warning.main" },
          preview.total_intereses > 0 && { label: "Intereses", value: preview.total_intereses },
          { label: "Total a cobrar", value: preview.total_cobrado, color: "text.primary", bold: true },
        ].filter(Boolean).map((row) => (
          <Box key={row.label} sx={{ display: "flex", justifyContent: "space-between", py: 0.25 }}>
            <Typography variant="body2" fontWeight={row.bold ? 600 : 400}>{row.label}</Typography>
            <Typography variant="body2" color={row.color} fontWeight={row.bold ? 600 : 400}>
              {fmtGs(row.value)}
            </Typography>
          </Box>
        ))}
      </Paper>

      {preview.genera_saldo_favor > 0 && (
        <Alert severity="success">
          Se generará un saldo a favor de <strong>{fmtGs(preview.genera_saldo_favor)}</strong> para este cliente.
        </Alert>
      )}
    </Box>
  );
}

// ─── Wizard ───────────────────────────────────────────────────────────────────
// `initialClienteId` (opcional) preselecciona el cliente al abrir, útil cuando
// el wizard se invoca desde el worklist de Cartera Inteligente para registrar
// el cobro directamente sin que el usuario tenga que buscar el cliente.
// eslint-disable-next-line react/prop-types
export function NuevoReciboMultiWizard({ open, onClose, initialClienteId = null }) {
  const { can } = usePermission("COBRANZAS");
  const { data: recibosConfig } = useRecibosConfigQuery();
  const canGastos = can("COB_REC_GASTOS") && recibosConfig?.permitir_gastos_en_recibo === true;
  const [step, setStep] = useState(0);

  // Step 1
  const [clienteId, setClienteId] = useState(initialClienteId);
  const [clienteOpts, setClienteOpts] = useState([]);
  const [selFacturas, setSelFacturas] = useState([]);
  const [importesParciales, setImportesParciales] = useState({});

  // Aplica el initialClienteId cada vez que el wizard se abre y precarga el
  // cliente en clienteOpts para que el Autocomplete muestre su nombre desde
  // el primer render.
  useEffect(() => {
    if (!open || !initialClienteId) return;
    if (!clienteId) setClienteId(initialClienteId);
    // Cargar cliente para que aparezca en el Autocomplete (sin texto del usuario).
    getCliente(initialClienteId)
      .then((c) => {
        if (!c) return;
        setClienteOpts((prev) => (prev.find((x) => x.id === c.id) ? prev : [c, ...prev]));
      })
      .catch(() => {});
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, initialClienteId]);

  // Step 2
  const [selNcs, setSelNcs] = useState([]);
  const [retenciones, setRetenciones] = useState([]);
  const [facturasCompraAplicadas, setFacturasCompraAplicadas] = useState([]);
  const [gastosAplicados, setGastosAplicados] = useState([]);

  // Step 3
  const [mediosPago, setMediosPago] = useState([]);

  // Step 4: preview + fecha de emisión editable
  const [preview, setPreview] = useState(null);
  const [fechaEmision, setFechaEmision] = useState(() => hoyISO());

  const previewMutation = usePreviewReciboMultiMutation();
  const createMutation = useCreateReciboMultiMutation();

  const toNum = (v) => {
    const n = Number(v);
    if (!Number.isFinite(n)) return 0;
    return Math.round(n * 100) / 100; // máx 2 decimales (constraint del DTO)
  };
  // Mapeo de tipos internos del wizard → enum DTO backend
  const TIPO_A_MEDIO_DTO = {
    EFECTIVO:      "EFECTIVO",
    TRANSFERENCIA: "TRANSFERENCIA",
    CHEQUE:        "CHEQUE",
    TARJETA:       "TARJETA",
    DEPOSITO:      "TRANSFERENCIA", // backend no tiene DEPOSITO, se loguea como TRANSFERENCIA
    SALDO_FAVOR:   "SALDO_FAVOR",
  };

  const buildPayload = () => ({
    cliente_id: clienteId,
    fecha_emision: fechaEmision || undefined,
    facturas: selFacturas.map((f) => ({
      factura_id: f.id,
      monto_pagado: toNum(importesParciales[f.id] || f.saldo_pendiente),
    })),
    nc_aplicadas: selNcs.map((nc) => {
      const facturasIds = selFacturas.map((f) => f.id);
      const esFlexible = nc.rec_modo_aplicacion === "FLEXIBLE";
      // ESTRICTO: solo a su factura origen. FLEXIBLE: a cualquier factura del recibo,
      // priorizando la origen si está incluida, sino la primera.
      const destino = esFlexible
        ? (facturasIds.includes(nc.factura_cab_id) ? nc.factura_cab_id : selFacturas[0]?.id)
        : (nc.factura_cab_id ?? selFacturas[0]?.id);
      return {
        nota_credito_id:    nc.id,
        factura_id_destino: destino,
        monto:              toNum(nc.saldo_disponible),
      };
    }),
    retenciones: retenciones.map(({ _key, factura_id, ...r }) => ({ ...r, factura_ref: factura_id })),
    facturas_compra_aplicadas: facturasCompraAplicadas.map(({ compra_cab_id, monto, observacion }) => ({
      compra_cab_id,
      monto: toNum(monto),
      ...(observacion ? { observacion } : {}),
    })),
    gastos: gastosAplicados.map(({ gasto_id, monto_aplicado, observacion }) => ({
      gasto_id,
      monto_aplicado: toNum(monto_aplicado),
      ...(observacion ? { observacion } : {}),
    })),
    medios_pago: mediosPago.map(({ _key, tipo, cheque_numero, cheque_banco, cheque_titular, cheque_cuenta, cheque_fecha_emision, cheque_fecha_vencimiento, ...m }) => ({
      ...m,
      medio: TIPO_A_MEDIO_DTO[tipo] ?? "OTRO",
      monto: toNum(m.monto),
      ...(tipo === "CHEQUE" && cheque_numero ? {
        cheque: {
          numero_cheque:          cheque_numero,
          banco_emisor:           cheque_banco,
          titular:                cheque_titular,
          cuenta_bancaria_origen: cheque_cuenta || undefined,
          fecha_emision:          cheque_fecha_emision,
          fecha_vencimiento:      cheque_fecha_vencimiento || undefined,
        },
      } : {}),
    })),
  });

  const totalNcSel = selNcs.reduce((s, nc) => s + Number(nc.saldo_disponible ?? 0), 0);
  const totalRetSel = retenciones.reduce((s, r) => s + Number(r.monto ?? 0), 0);
  const totalComprasSel = facturasCompraAplicadas.reduce((s, c) => s + Number(c.monto ?? 0), 0);
  const totalGastosSel = gastosAplicados.reduce((s, g) => s + Number(g.monto_aplicado ?? 0), 0);
  const totalFacturas = selFacturas.reduce((s, f) => {
    const parcial = importesParciales[f.id];
    return s + Number(parcial || f.saldo_pendiente);
  }, 0);
  const totalAPagar = Math.max(0, totalFacturas - totalNcSel - totalRetSel - totalComprasSel - totalGastosSel);

  const canGoNext = useMemo(() => {
    if (step === 0) return !!clienteId && selFacturas.length > 0;
    if (step === 1) return true; // NC y retenciones son opcionales
    if (step === 2) return mediosPago.length > 0 && mediosPago.every((m) => {
      if (!(Number(m.monto) > 0)) return false;
      if (m.tipo !== "EFECTIVO" && m.tipo !== "SALDO_FAVOR" && !m.cuenta_tesoreria_id) return false;
      if (m.tipo === "CHEQUE") {
        return !!m.cheque_numero && !!m.cheque_banco && !!m.cheque_titular && !!m.cheque_fecha_emision;
      }
      return true;
    });
    return true;
  }, [step, clienteId, selFacturas, mediosPago]);

  const handleNext = async () => {
    if (step === 2) {
      // Generar preview antes de paso 4
      try {
        const result = await previewMutation.mutateAsync(buildPayload());
        setPreview(result);
      } catch {
        toast.error("Error al calcular el preview. Revisá los datos.");
        return;
      }
    }
    setStep((s) => s + 1);
  };

  const handleConfirm = () => {
    createMutation.mutate(buildPayload(), {
      onSuccess: () => {
        toast.success("Recibo creado correctamente");
        onClose(true);
      },
    });
  };

  const handleClose = () => {
    if (createMutation.isPending) return;
    setStep(0);
    setClienteId(null);
    setClienteOpts([]);
    setSelFacturas([]);
    setImportesParciales({});
    setSelNcs([]);
    setRetenciones([]);
    setFacturasCompraAplicadas([]);
    setGastosAplicados([]);
    setMediosPago([]);
    setPreview(null);
    setFechaEmision(hoyISO());
    onClose();
  };

  return (
    <Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>
      <DialogTitle sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
        <span>Nuevo recibo multi-factura</span>
        <IconButton size="small" onClick={handleClose} disabled={createMutation.isPending}>
          <Icon icon="mdi:close" />
        </IconButton>
      </DialogTitle>

      <Box sx={{ px: 3, pt: 1 }}>
        <Stepper activeStep={step} alternativeLabel>
          {STEPS.map((label) => (
            <Step key={label}>
              <StepLabel>{label}</StepLabel>
            </Step>
          ))}
        </Stepper>
      </Box>

      <DialogContent dividers sx={{ minHeight: 400 }}>
        {step === 0 && (
          <StepClienteFacturas
            clienteId={clienteId}
            setClienteId={setClienteId}
            clienteOpts={clienteOpts}
            setClienteOpts={setClienteOpts}
            selFacturas={selFacturas}
            setSelFacturas={setSelFacturas}
            importesParciales={importesParciales}
            setImportesParciales={setImportesParciales}
          />
        )}
        {step === 1 && (
          <StepNcRetenciones
            clienteId={clienteId}
            selFacturas={selFacturas}
            selNcs={selNcs}
            setSelNcs={setSelNcs}
            retenciones={retenciones}
            setRetenciones={setRetenciones}
            facturasCompraAplicadas={facturasCompraAplicadas}
            setFacturasCompraAplicadas={setFacturasCompraAplicadas}
            gastosAplicados={gastosAplicados}
            setGastosAplicados={setGastosAplicados}
            canNcFlexible={can("COB_REC_NC_FLEXIBLE")}
            canRetencion={can("COB_RET_RETENCION_CREAR")}
            canGastos={canGastos}
            esAgenteRetencion={!!clienteOpts.find((c) => c.id === clienteId)?.es_agente_retencion}
          />
        )}
        {step === 2 && (
          <StepMediosPago
            mediosPago={mediosPago}
            setMediosPago={setMediosPago}
            totalAPagar={totalAPagar}
            totalFacturas={totalFacturas}
            totalNcSel={totalNcSel}
            totalRetSel={totalRetSel}
            totalComprasSel={totalComprasSel}
            totalGastosSel={totalGastosSel}
          />
        )}
        {step === 3 && (
          <StepConfirmacion
            preview={preview}
            isLoadingPreview={previewMutation.isPending}
            fechaEmision={fechaEmision}
            setFechaEmision={setFechaEmision}
          />
        )}
      </DialogContent>

      <DialogActions sx={{ px: 3, py: 2 }}>
        {step > 0 && (
          <Button onClick={() => setStep((s) => s - 1)} disabled={createMutation.isPending}>
            Atrás
          </Button>
        )}
        <Box sx={{ flex: 1 }} />
        <Button onClick={handleClose} disabled={createMutation.isPending}>
          Cancelar
        </Button>
        {step < STEPS.length - 1 ? (
          <Button
            variant="contained"
            onClick={handleNext}
            disabled={!canGoNext || previewMutation.isPending}
            endIcon={previewMutation.isPending ? <CircularProgress size={16} /> : <Icon icon="mdi:arrow-right" />}
          >
            {step === 2 ? "Calcular y revisar" : "Siguiente"}
          </Button>
        ) : (
          <Button
            variant="contained"
            color="success"
            onClick={handleConfirm}
            disabled={!preview || createMutation.isPending}
            startIcon={createMutation.isPending ? <CircularProgress size={16} /> : <Icon icon="mdi:check" />}
          >
            {createMutation.isPending ? "Guardando…" : "Confirmar recibo"}
          </Button>
        )}
      </DialogActions>
    </Dialog>
  );
}
