import { Icon } from "@iconify/react";
import {
  Alert,
  Autocomplete,
  Box,
  Button,
  Chip,
  Collapse,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider,
  FormControl,
  Grid,
  IconButton,
  InputAdornment,
  InputLabel,
  MenuItem,
  Paper,
  Popover,
  Select,
  Skeleton,
  TextField,
  Tooltip,
  Typography,
  useMediaQuery,
  useTheme
} from "@mui/material";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import React, {
  startTransition,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { useNavigate } from "react-router-dom";
import { toast } from "sonner";
import styled from "styled-components";
import { getCreditInfo, searchClientes } from "../api/clientes.service";
import {
  getConfigMora,
  getCuotasPendientes,
  getCuotasPorFactura,
  getFacturasCreditoPendientes,
} from "../api/cobros.service";
import { getCondicionesPago } from "../api/condiciones-pago.service";
import { getFacturas } from "../api/facturas.service";
import { getNumeracionesBySucursal } from "../api/numeraciones.service";
import { getProductoPrecioCuotasByProducto } from "../api/producto-precio-cuotas.service";
import { getPrecioProducto, getPreciosProductosBatch } from "../api/listas-precios.service";
import { calcularCuotas as calcularCuotasPlan, getPlanesEmpresa, obtenerPlanesAplicables } from "../api/planes-cuotas.service";
import { buscarProductos } from "../api/productos.service";
import { ProductoBrowserModal } from "../components/organismos/POSAdmin/ProductoBrowserModal";
import { ClienteHistorialDrawer } from "../components/organismos/ClientesDesign/ClienteHistorialDrawer";
import { createSolicitudCredito } from "../api/solicitud-credito.service";
import { getSucursales } from "../api/sucursales.service";
import { getCobradoresActivos, getVendedoresActivos } from "../api/vendedores-cobradores.service";
import { MonedaInput } from "../components/common/MonedaInput";
import { fmtMoneda } from "../components/_standards/enums/monedas";
import { ScreenGuia } from "../components/_standards/ScreenGuia";
import { usePermission } from "../hooks/usePermission";
import { useAuthStore } from "../store/AuthStore";
import { hoyISO } from "../utils/fecha";

const TIPO_DOC_SOLICITUD_CODIGO = 300;

// Redondear monto de cuota para cobro limpio (Gs. paraguayos).
// Usa redondeo adaptativo según el monto:
//   ≥ 10.000 → al millar (115.385 → 115.000)
//   ≥  1.000 → a la centena (9.615 → 9.600)
//   <  1.000 → sin redondeo
// La diferencia se acumula en la última cuota del cronograma.
const redondearCuota = (monto) => {
  if (monto >= 10000) return Math.floor(monto / 1000) * 1000;
  if (monto >= 1000) return Math.floor(monto / 100) * 100;
  return Math.round(monto);
};

// Input numérico: type="text" + inputMode="numeric" (sin spinners nativos),
// solo commitea en blur, y marca la actualización como transición no-urgente.
const BlurNumberInput = React.memo(function BlurNumberInput({
  value,
  onCommit,
  inputProps,
  ...props
}) {
  const [local, setLocal] = useState(value ?? "");
  const isTypingRef = useRef(false);
  const committedRef = useRef(value);

  // Solo sincronizar desde fuera si el usuario no está escribiendo
  useEffect(() => {
    if (!isTypingRef.current) {
      setLocal(value ?? "");
      committedRef.current = value;
    }
  }, [value]);

  return (
    <TextField
      {...props}
      type="text"
      inputProps={{ ...inputProps, inputMode: "numeric", pattern: "[0-9]*" }}
      value={local}
      onFocus={() => {
        isTypingRef.current = true;
      }}
      onChange={(e) => {
        // Solo permitir dígitos (y vacío)
        const raw = e.target.value;
        if (raw === "" || /^\d+$/.test(raw)) setLocal(raw);
      }}
      onBlur={() => {
        isTypingRef.current = false;
        const num = local === "" ? null : Number(local);
        if (num !== committedRef.current) {
          committedRef.current = num;
          // Marcar como transición no-urgente para no bloquear el UI
          startTransition(() => {
            onCommit(num);
          });
        }
      }}
      onKeyDown={(e) => {
        if (e.key === "Enter") {
          e.target.blur();
        }
      }}
    />
  );
});

// Cronograma como componente memo para no re-renderizar con cada cambio de items
const CronogramaPagos = React.memo(function CronogramaPagos({
  cronograma,
  saldoFinanciar,
  intervaloDias,
  moneda = "PYG",
}) {
  if (cronograma.length === 0) return null;

  // Con plan de interés, la última cuota difiere por amortización (no por redondeo) —
  // la nota de "cuotas redondeadas" solo aplica al cronograma lineal sin interés.
  const esConInteres = cronograma.some((c) => c.interes != null);

  // Detectar si la última cuota difiere de las regulares (por redondeo)
  const ultimaCuota = cronograma[cronograma.length - 1];
  const primeraCuota = cronograma[0];
  const tieneAjusteUltimaCuota =
    !esConInteres && cronograma.length > 1 && ultimaCuota.monto !== primeraCuota.monto;

  // Calcular detalle de redondeo para la nota informativa
  const cuotaExacta =
    cronograma.length > 0 ? Math.round(saldoFinanciar / cronograma.length) : 0;
  const difPorCuota = cuotaExacta - primeraCuota.monto;
  const difTotal = tieneAjusteUltimaCuota
    ? ultimaCuota.monto - primeraCuota.monto
    : 0;

  return (
    <Paper sx={{ p: 2, mb: 2 }}>
      <Typography
        variant="subtitle2"
        fontWeight={700}
        gutterBottom
        sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
      >
        <Icon icon="mdi:calendar-clock" width={18} />
        Cronograma de pagos ({cronograma.length} cuotas)
      </Typography>
      {cronograma.some((c) => c.interes != null) && (
        <Typography variant="caption" color="warning.dark" sx={{ display: "block", mb: 0.5 }}>
          Interés total del plan: {fmtMoneda(cronograma.reduce((s, c) => s + (Number(c.interes) || 0), 0), moneda)}
        </Typography>
      )}
      <Typography
        variant="caption"
        color="text.secondary"
        sx={{ display: "block", mb: 1 }}
      >
        {intervaloDias === 7
          ? "Frecuencia semanal"
          : intervaloDias === 15
            ? "Frecuencia quincenal"
            : intervaloDias === 30
              ? "Frecuencia mensual"
              : `Cada ${intervaloDias} días`}
      </Typography>

      {tieneAjusteUltimaCuota && (
        <Box
          sx={{
            display: "flex",
            alignItems: "flex-start",
            gap: 0.5,
            p: 1,
            mb: 1,
            borderRadius: 1,
            bgcolor: "rgba(59, 130, 246, 0.06)",
            border: "1px solid rgba(59, 130, 246, 0.15)",
          }}
        >
          <Icon
            icon="mdi:information-outline"
            width={14}
            color="#3b82f6"
            style={{ marginTop: 1, flexShrink: 0 }}
          />
          <Box>
            <Typography
              variant="caption"
              color="text.secondary"
              sx={{ lineHeight: 1.5, display: "block" }}
            >
              Cuotas redondeadas de {fmtMoneda(cuotaExacta, moneda)} a{" "}
              {fmtMoneda(primeraCuota.monto, moneda)} ({fmtMoneda(difPorCuota, moneda)} por
              cuota).
            </Typography>
            <Typography
              variant="caption"
              color="text.secondary"
              sx={{ lineHeight: 1.5, display: "block" }}
            >
              El ajuste total de {fmtMoneda(difTotal, moneda)} se suma a la última
              cuota ({fmtMoneda(ultimaCuota.monto, moneda)}).
            </Typography>
          </Box>
        </Box>
      )}

      <Box sx={{ display: "flex", flexDirection: "column", gap: 0 }}>
        {cronograma.map((c, idx) => {
          const esUltima = idx === cronograma.length - 1;
          const esDistinta = esUltima && tieneAjusteUltimaCuota;
          const tieneDesglose = c.interes != null && c.capital != null;
          return (
            <Box
              key={c.numero}
              sx={{
                py: 0.6,
                px: 0.5,
                borderBottom: !esUltima ? "1px solid" : "none",
                borderColor: "divider",
                bgcolor: c.esEntrega
                  ? "rgba(245, 158, 11, 0.08)"
                  : esDistinta
                    ? "rgba(59, 130, 246, 0.06)"
                    : idx % 2 === 0
                      ? "transparent"
                      : "action.hover",
              }}
            >
              <Box sx={{ display: "flex", alignItems: "center" }}>
                <Typography
                  variant="caption"
                  fontWeight={700}
                  sx={{ width: 22, textAlign: "center", flexShrink: 0 }}
                >
                  {c.numero}
                </Typography>
                <Typography
                  variant="caption"
                  sx={{ flex: 1, ml: 0.5, display: "flex", alignItems: "center", gap: 0.5 }}
                >
                  {c.fechaStr}
                  {c.esEntrega && (
                    <Chip
                      label="Entrega"
                      size="small"
                      color="warning"
                      variant="outlined"
                      sx={{ height: 14, fontSize: "0.55rem", fontWeight: 700 }}
                    />
                  )}
                </Typography>
                <Typography
                  variant="caption"
                  fontWeight={c.esEntrega ? 700 : 600}
                  color={c.esEntrega ? "warning.main" : "inherit"}
                  sx={{ textAlign: "right", minWidth: 80 }}
                >
                  {fmtMoneda(c.monto, moneda)}
                  {esDistinta && " *"}
                </Typography>
                <Typography
                  variant="caption"
                  sx={{
                    textAlign: "right",
                    minWidth: 80,
                    ml: 0.5,
                    color: "text.secondary",
                  }}
                >
                  {fmtMoneda(c.saldo, moneda)}
                </Typography>
              </Box>
              {tieneDesglose && (
                <Typography
                  variant="caption"
                  sx={{ display: "block", textAlign: "right", color: "text.secondary", fontSize: "0.65rem", mt: 0.25 }}
                >
                  Capital {fmtMoneda(c.capital, moneda)} · Interés {fmtMoneda(c.interes, moneda)}
                </Typography>
              )}
            </Box>
          );
        })}
      </Box>

      <Box sx={{ display: "flex", mt: 0.5, px: 0.5, opacity: 0.5 }}>
        <Typography
          variant="caption"
          sx={{ width: 22, textAlign: "center", fontSize: "0.6rem" }}
        >
          Nº
        </Typography>
        <Typography
          variant="caption"
          sx={{ flex: 1, ml: 0.5, fontSize: "0.6rem" }}
        >
          Fecha
        </Typography>
        <Typography
          variant="caption"
          sx={{ textAlign: "right", minWidth: 80, fontSize: "0.6rem" }}
        >
          Cuota
        </Typography>
        <Typography
          variant="caption"
          sx={{ textAlign: "right", minWidth: 80, ml: 0.5, fontSize: "0.6rem" }}
        >
          Saldo
        </Typography>
      </Box>

      <Divider sx={{ my: 1 }} />
      <Box
        sx={{
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
        }}
      >
        <Typography variant="caption" color="text.secondary">
          Último vencimiento
        </Typography>
        <Typography variant="caption" fontWeight={700}>
          {ultimaCuota.fechaLarga}
        </Typography>
      </Box>
    </Paper>
  );
});

// ═══════════ HISTORIAL CREDITICIO DEL CLIENTE ═══════════
function HistorialCrediticio({ cliente }) {
  const clienteId = cliente?.id;
  const [open, setOpen] = useState(false);
  const [drawerOpen, setDrawerOpen] = useState(false);
  const [facturaExpandida, setFacturaExpandida] = useState(null);

  // Info de crédito (siempre carga)
  const { data: creditInfo, isLoading: loadingCredit } = useQuery({
    queryKey: ["credit-info", clienteId],
    queryFn: () => getCreditInfo(clienteId),
    enabled: !!clienteId,
    staleTime: 30000,
  });

  // Facturas crédito pendientes (carga al expandir)
  const { data: facturasPendientesData, isLoading: loadingFacturas } = useQuery(
    {
      queryKey: ["facturas-credito-pendientes", clienteId],
      queryFn: () => getFacturasCreditoPendientes(clienteId),
      enabled: !!clienteId && open,
      staleTime: 30000,
    },
  );

  // Últimas 5 facturas crédito del cliente (carga al expandir)
  const { data: ultimasFacturasData, isLoading: loadingUltimas } = useQuery({
    queryKey: ["ultimas-facturas-credito", clienteId],
    queryFn: () => getFacturas({ clienteId, limit: 5, condicion: "credito" }),
    enabled: !!clienteId && open,
    staleTime: 30000,
  });

  // Cuotas de una factura específica (carga al expandir factura)
  const { data: cuotasFacturaData, isLoading: loadingCuotasFactura } = useQuery(
    {
      queryKey: ["cuotas-factura", facturaExpandida],
      queryFn: () => getCuotasPorFactura(facturaExpandida),
      enabled: !!facturaExpandida,
      staleTime: 30000,
    },
  );

  // Todas las cuotas pendientes del cliente
  const { data: cuotasPendientesData } = useQuery({
    queryKey: ["cuotas-pendientes", clienteId],
    queryFn: () => getCuotasPendientes(clienteId),
    enabled: !!clienteId && open,
    staleTime: 30000,
  });

  const info = creditInfo?.data || creditInfo || {};
  const facturasPendientes = Array.isArray(
    facturasPendientesData?.data || facturasPendientesData,
  )
    ? facturasPendientesData?.data || facturasPendientesData
    : [];
  const ultimasFacturas = Array.isArray(
    ultimasFacturasData?.data || ultimasFacturasData,
  )
    ? ultimasFacturasData?.data || ultimasFacturasData
    : [];
  const cuotasDetalle = Array.isArray(
    cuotasFacturaData?.data || cuotasFacturaData,
  )
    ? cuotasFacturaData?.data || cuotasFacturaData
    : [];
  const cuotasPendientes = Array.isArray(
    cuotasPendientesData?.data || cuotasPendientesData,
  )
    ? cuotasPendientesData?.data || cuotasPendientesData
    : [];

  const saldoTotal =
    info.saldo_pendiente ||
    info.saldo_total ||
    facturasPendientes.reduce(
      (s, f) => s + Number(f.saldo_pendiente || f.monto_pendiente || 0),
      0,
    );
  const hoy = new Date();
  const cuotasVencidas = cuotasPendientes.filter(
    (c) => c.fecha_vencimiento && new Date(c.fecha_vencimiento) < hoy,
  );
  const montoVencido = cuotasVencidas.reduce(
    (s, c) =>
      s + Number(c.saldo_pendiente || c.monto_cuota || c.dmoncuota || 0),
    0,
  );
  const tieneDeuda = saldoTotal > 0;
  const tieneAtrasos = cuotasVencidas.length > 0;

  const colorIndicador = tieneAtrasos
    ? "error"
    : tieneDeuda
      ? "warning"
      : "success";
  const textoIndicador = tieneAtrasos
    ? `${cuotasVencidas.length} cuota${cuotasVencidas.length > 1 ? "s" : ""} vencida${cuotasVencidas.length > 1 ? "s" : ""}`
    : tieneDeuda
      ? "Créditos al día"
      : "Sin deudas";

  // Combinar facturas: pendientes + últimas (sin duplicados)
  const todasFacturas = useMemo(() => {
    const mapa = new Map();
    [...facturasPendientes, ...ultimasFacturas].forEach((f) => {
      if (f.id && !mapa.has(f.id)) mapa.set(f.id, f);
    });
    return Array.from(mapa.values()).slice(0, 5);
  }, [facturasPendientes, ultimasFacturas]);

  const renderFactura = (f, idx) => {
    const monedaFactura = f.moneda?.codigo || "PYG";
    const saldoF = Number(f.saldo_pendiente || f.monto_pendiente || 0);
    const totalF = Number(
      f.factura_subtotales?.[0]?.dtotope ||
        f.total_factura ||
        f.monto_total ||
        f.total ||
        0,
    );
    const porcentajePagado =
      totalF > 0 ? Math.round(((totalF - saldoF) / totalF) * 100) : 0;
    const pagada = porcentajePagado >= 100;
    const tieneVencidas = !pagada && f.cuotas_vencidas > 0;
    const fechaEmision = f.dfeemide || f.fecha_emision || f.created_at;
    const estaExpandida = facturaExpandida === f.id;
    const numero =
      f.numero_factura ||
      f.numero_completo ||
      f.numero ||
      f.dserienum ||
      "Sin número";

    return (
      <Box key={f.id || idx}>
        <Box
          onClick={() => setFacturaExpandida(estaExpandida ? null : f.id)}
          sx={{
            display: "flex",
            alignItems: "center",
            py: 0.75,
            px: 0.5,
            cursor: "pointer",
            "&:hover": { bgcolor: "action.hover" },
            borderBottom: "1px solid",
            borderColor: "divider",
            bgcolor: tieneVencidas ? "rgba(239, 68, 68, 0.04)" : "transparent",
          }}
        >
          <Icon
            icon={
              pagada
                ? "mdi:check-circle"
                : tieneVencidas
                  ? "mdi:alert-circle"
                  : "mdi:file-document-outline"
            }
            width={16}
            color={pagada ? "#22c55e" : tieneVencidas ? "#ef4444" : undefined}
            style={{
              opacity: pagada ? 0.7 : tieneVencidas ? 1 : 0.4,
              flexShrink: 0,
            }}
          />
          <Box sx={{ flex: 1, ml: 0.75, minWidth: 0 }}>
            <Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
              <Typography variant="caption" fontWeight={600} noWrap>
                {numero}
              </Typography>
              <Chip
                size="small"
                label={pagada ? "Pagada" : `${porcentajePagado}% pagado`}
                sx={{ height: 16, fontSize: "0.6rem" }}
                color={
                  pagada
                    ? "success"
                    : porcentajePagado >= 50
                      ? "warning"
                      : "default"
                }
                variant="outlined"
              />
            </Box>
            <Typography
              variant="caption"
              color="text.secondary"
              sx={{ fontSize: "0.65rem" }}
            >
              {fechaEmision
                ? new Date(fechaEmision).toLocaleDateString("es-PY", {
                    day: "2-digit",
                    month: "short",
                    year: "numeric",
                  })
                : ""}{" "}
              | Total: {fmtMoneda(totalF, monedaFactura)}
            </Typography>
          </Box>
          <Box sx={{ textAlign: "right", flexShrink: 0 }}>
            <Typography
              variant="caption"
              fontWeight={700}
              color={
                pagada
                  ? "success.main"
                  : tieneVencidas
                    ? "error.main"
                    : "text.primary"
              }
            >
              {pagada ? "Pagada" : fmtMoneda(saldoF, monedaFactura)}
            </Typography>
            {!pagada && (
              <Typography
                variant="caption"
                color="text.secondary"
                sx={{ display: "block", fontSize: "0.6rem" }}
              >
                pendiente
              </Typography>
            )}
          </Box>
          <Icon
            icon={estaExpandida ? "mdi:chevron-up" : "mdi:chevron-down"}
            width={16}
            style={{ opacity: 0.3, ml: 4 }}
          />
        </Box>

        {/* Detalle de cuotas de la factura */}
        <Collapse in={estaExpandida}>
          <Box
            sx={{
              pl: 3,
              pr: 0.5,
              py: 0.5,
              bgcolor: "action.hover",
              borderBottom: "1px solid",
              borderColor: "divider",
            }}
          >
            {loadingCuotasFactura ? (
              <Box sx={{ py: 1 }}>
                <Skeleton height={18} />
                <Skeleton height={18} />
                <Skeleton height={18} />
              </Box>
            ) : cuotasDetalle.length > 0 ? (
              <>
                <Box sx={{ display: "flex", gap: 0, opacity: 0.5, mb: 0.25 }}>
                  <Typography
                    variant="caption"
                    sx={{ width: 28, fontSize: "0.6rem" }}
                  >
                    Nº
                  </Typography>
                  <Typography
                    variant="caption"
                    sx={{ flex: 1, fontSize: "0.6rem" }}
                  >
                    Vencimiento
                  </Typography>
                  <Typography
                    variant="caption"
                    sx={{ width: 70, textAlign: "right", fontSize: "0.6rem" }}
                  >
                    Monto
                  </Typography>
                  <Typography
                    variant="caption"
                    sx={{ width: 70, textAlign: "right", fontSize: "0.6rem" }}
                  >
                    Saldo
                  </Typography>
                  <Typography
                    variant="caption"
                    sx={{ width: 55, textAlign: "right", fontSize: "0.6rem" }}
                  >
                    Estado
                  </Typography>
                </Box>
                {cuotasDetalle.map((c) => {
                  const venc =
                    c.dvenccuo || c.fecha_vencimiento || c.vencimiento;
                  const montoCuota = Number(c.dmoncuota || c.monto_cuota || 0);
                  const saldoCuota = Number(c.saldo_pendiente || montoCuota);
                  const pagadaCuota = saldoCuota <= 0;
                  const vencidaCuota =
                    !pagadaCuota && venc && new Date(venc) < hoy;
                  const diasVenc = vencidaCuota
                    ? Math.round((hoy - new Date(venc)) / 86400000)
                    : 0;
                  return (
                    <Box
                      key={c.id || c.nro_cuota}
                      sx={{
                        display: "flex",
                        alignItems: "center",
                        py: 0.3,
                        bgcolor: vencidaCuota
                          ? "rgba(239, 68, 68, 0.06)"
                          : "transparent",
                      }}
                    >
                      <Typography
                        variant="caption"
                        sx={{ width: 28, fontWeight: 600 }}
                      >
                        {c.nro_cuota || c.numero_cuota}
                      </Typography>
                      <Typography variant="caption" sx={{ flex: 1 }}>
                        {venc
                          ? new Date(venc).toLocaleDateString("es-PY", {
                              day: "2-digit",
                              month: "2-digit",
                              year: "2-digit",
                            })
                          : "-"}
                      </Typography>
                      <Typography
                        variant="caption"
                        sx={{ width: 70, textAlign: "right" }}
                      >
                        {fmtMoneda(montoCuota, monedaFactura)}
                      </Typography>
                      <Typography
                        variant="caption"
                        fontWeight={600}
                        sx={{ width: 70, textAlign: "right" }}
                        color={
                          pagadaCuota
                            ? "success.main"
                            : vencidaCuota
                              ? "error.main"
                              : "text.primary"
                        }
                      >
                        {pagadaCuota ? fmtMoneda(0, monedaFactura) : fmtMoneda(saldoCuota, monedaFactura)}
                      </Typography>
                      <Box sx={{ width: 55, textAlign: "right" }}>
                        <Chip
                          size="small"
                          label={
                            pagadaCuota
                              ? "Pagada"
                              : vencidaCuota
                                ? `${diasVenc}d`
                                : "Al día"
                          }
                          sx={{ height: 16, fontSize: "0.55rem" }}
                          color={
                            pagadaCuota
                              ? "success"
                              : vencidaCuota
                                ? "error"
                                : "default"
                          }
                          variant={
                            pagadaCuota || vencidaCuota ? "filled" : "outlined"
                          }
                        />
                      </Box>
                    </Box>
                  );
                })}
              </>
            ) : (
              <Typography
                variant="caption"
                color="text.secondary"
                sx={{ py: 0.5, display: "block" }}
              >
                Sin detalle de cuotas disponible
              </Typography>
            )}
          </Box>
        </Collapse>
      </Box>
    );
  };

  return (
    <Paper sx={{ mb: 2, overflow: "hidden" }}>
      <Box
        onClick={() => setOpen(!open)}
        sx={{
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
          p: 1.5,
          cursor: "pointer",
          "&:hover": { bgcolor: "action.hover" },
        }}
      >
        <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
          <Icon icon="mdi:history" width={18} />
          <Typography variant="subtitle2" fontWeight={600}>
            Historial crediticio
          </Typography>
          {loadingCredit ? (
            <Skeleton width={80} height={20} />
          ) : (
            <Chip
              size="small"
              label={textoIndicador}
              color={colorIndicador}
              variant={tieneAtrasos ? "filled" : "outlined"}
              sx={{ height: 20, fontSize: "0.65rem" }}
            />
          )}
        </Box>
        <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
          {tieneDeuda && !loadingCredit && (
            <Typography
              variant="caption"
              fontWeight={600}
              color={tieneAtrasos ? "error.main" : "warning.main"}
            >
              Deuda: {fmtMoneda(saldoTotal, "PYG")}
            </Typography>
          )}
          <Button
            size="small"
            variant="outlined"
            startIcon={<Icon icon="mdi:account-details-outline" width={14} />}
            onClick={(e) => {
              e.stopPropagation();
              setDrawerOpen(true);
            }}
            sx={{ textTransform: "none", fontWeight: 600, py: 0.25, minHeight: 26 }}
          >
            Ver detalle
          </Button>
          <Icon
            icon={open ? "mdi:chevron-up" : "mdi:chevron-down"}
            width={20}
            style={{ opacity: 0.5 }}
          />
        </Box>
      </Box>

      <Collapse in={open}>
        <Divider />
        <Box sx={{ p: 1.5 }}>
          {/* Resumen rápido */}
          <Box sx={{ display: "flex", gap: 2, flexWrap: "wrap", mb: 1.5 }}>
            {info.limite_credito !== undefined && (
              <>
                <Box>
                  <Typography variant="caption" color="text.secondary">
                    Límite
                  </Typography>
                  <Typography variant="body2" fontWeight={600}>
                    {/* Tope opcional: sin tope no es un monto en 0. */}
                    {Number(info.limite_credito) ? fmtMoneda(info.limite_credito, "PYG") : "Sin tope"}
                  </Typography>
                </Box>
                <Box>
                  <Typography variant="caption" color="text.secondary">
                    Disponible
                  </Typography>
                  <Typography
                    variant="body2"
                    fontWeight={600}
                    color="success.main"
                  >
                    {Number(info.limite_credito) || info.credito_disponible
                      ? fmtMoneda(
                          info.credito_disponible || Math.max(0, (info.limite_credito || 0) - saldoTotal),
                          "PYG",
                        )
                      : "Sin tope"}
                  </Typography>
                </Box>
              </>
            )}
            <Box>
              <Typography variant="caption" color="text.secondary">
                Deuda total
              </Typography>
              <Typography
                variant="body2"
                fontWeight={600}
                color={tieneDeuda ? "warning.main" : "text.secondary"}
              >
                {fmtMoneda(saldoTotal, "PYG")}
              </Typography>
            </Box>
            {tieneAtrasos && (
              <Box>
                <Typography variant="caption" color="text.secondary">
                  Vencido
                </Typography>
                <Typography variant="body2" fontWeight={700} color="error.main">
                  {fmtMoneda(montoVencido, "PYG")}
                </Typography>
              </Box>
            )}
          </Box>

          {/* Alerta de atrasos */}
          {tieneAtrasos && (
            <Box
              sx={{
                display: "flex",
                alignItems: "flex-start",
                gap: 0.5,
                p: 1,
                mb: 1.5,
                borderRadius: 1,
                bgcolor: "rgba(239, 68, 68, 0.08)",
                border: "1px solid rgba(239, 68, 68, 0.2)",
              }}
            >
              <Icon
                icon="mdi:alert-circle"
                width={16}
                color="#ef4444"
                style={{ marginTop: 1, flexShrink: 0 }}
              />
              <Box>
                <Typography
                  variant="caption"
                  fontWeight={600}
                  color="error.main"
                >
                  {cuotasVencidas.length} cuota
                  {cuotasVencidas.length > 1 ? "s" : ""} vencida
                  {cuotasVencidas.length > 1 ? "s" : ""} — Total:{" "}
                  {fmtMoneda(montoVencido, "PYG")}
                </Typography>
                <Typography
                  variant="caption"
                  color="text.secondary"
                  sx={{ display: "block", lineHeight: 1.3 }}
                >
                  El cliente tiene pagos atrasados. Evalúe el riesgo antes de
                  aprobar un nuevo crédito.
                </Typography>
              </Box>
            </Box>
          )}

          {/* Últimas facturas a crédito */}
          <Typography
            variant="caption"
            fontWeight={600}
            color="text.secondary"
            sx={{ display: "block", mb: 0.5 }}
          >
            Últimas facturas a crédito{" "}
            {todasFacturas.length > 0 && `(${todasFacturas.length})`}
          </Typography>

          {loadingFacturas || loadingUltimas ? (
            <Box sx={{ display: "flex", flexDirection: "column", gap: 0.5 }}>
              <Skeleton height={36} />
              <Skeleton height={36} />
              <Skeleton height={36} />
            </Box>
          ) : todasFacturas.length > 0 ? (
            <Box
              sx={{
                border: "1px solid",
                borderColor: "divider",
                borderRadius: 1,
                overflow: "hidden",
              }}
            >
              {todasFacturas.map(renderFactura)}
            </Box>
          ) : (
            <Box sx={{ textAlign: "center", py: 2, opacity: 0.5 }}>
              <Icon icon="mdi:check-circle-outline" width={28} />
              <Typography variant="caption" sx={{ display: "block", mt: 0.5 }}>
                Sin facturas a crédito registradas
              </Typography>
              <Typography
                variant="caption"
                color="text.secondary"
                sx={{ fontSize: "0.6rem" }}
              >
                Este cliente no tiene historial de compras a crédito
              </Typography>
            </Box>
          )}

          <Typography
            variant="caption"
            color="text.secondary"
            sx={{
              display: "block",
              mt: 1,
              fontSize: "0.6rem",
              textAlign: "center",
              opacity: 0.6,
            }}
          >
            Toque una factura para ver el detalle de cuotas y vencimientos
          </Typography>
        </Box>
      </Collapse>
      <ClienteHistorialDrawer
        cliente={drawerOpen ? cliente : null}
        onClose={() => setDrawerOpen(false)}
      />
    </Paper>
  );
}

// ═══════════ FILA DE ITEM (memoizada para evitar re-renders) ═══════════
const ItemRow = React.memo(function ItemRow({ item, idx, onUpdate, onRemove, moneda = "PYG", puedeEditarPrecio = false }) {
  return (
    <Paper variant="outlined" sx={{ p: 1.5 }}>
      {/* Fila 1: Producto + eliminar */}
      <Box
        sx={{
          display: "flex",
          justifyContent: "space-between",
          alignItems: "flex-start",
          mb: 1,
        }}
      >
        <Box>
          <Typography variant="body2" fontWeight={600}>
            {idx + 1}. {item.descripcion}
          </Typography>
          {item.plan_nombre && (
            <Chip
              label={`Sugerido: ${item.plan_nombre}`}
              size="small"
              variant="outlined"
              color="info"
              sx={{ mt: 0.25, height: 18, fontSize: "0.65rem" }}
            />
          )}
        </Box>
        <IconButton
          size="small"
          color="error"
          onClick={() => onRemove(item._key)}
        >
          <Icon icon="mdi:close" width={16} />
        </IconButton>
      </Box>
      {/* Fila 2: Cantidad × Precio unitario = Total */}
      <Grid container spacing={1} alignItems="center">
        <Grid size={{ xs: 3, sm: 2 }}>
          <BlurNumberInput
            size="small"
            label="Cant."
            value={item.cantidad}
            onCommit={(v) =>
              onUpdate(item._key, "cantidad", Math.max(1, v || 1))
            }
            inputProps={{ min: 1, style: { textAlign: "center" } }}
            fullWidth
          />
        </Grid>
        <Grid size={{ xs: 5, sm: 5 }}>
          <Tooltip
            title={
              puedeEditarPrecio
                ? ""
                : "El precio viene de la lista de precios. Necesitás el permiso VEN_SOL_PRECIO_EDITAR para cambiarlo a mano."
            }
          >
            <span>
              <MonedaInput
                label="Precio unitario"
                value={Number(item.precio_unitario) || 0}
                onChange={(v) => onUpdate(item._key, "precio_unitario", v || 0)}
                moneda={moneda}
                fullWidth
                disabled={!puedeEditarPrecio}
                InputProps={
                  puedeEditarPrecio
                    ? undefined
                    : {
                        endAdornment: (
                          <InputAdornment position="end">
                            <Icon icon="mdi:lock-outline" width={14} />
                          </InputAdornment>
                        ),
                      }
                }
              />
            </span>
          </Tooltip>
        </Grid>
        <Grid size={{ xs: 4, sm: 5 }}>
          <Box sx={{ textAlign: "right", pr: 0.5, pt: 0.5 }}>
            <Typography variant="caption" color="text.secondary">
              Total
            </Typography>
            <Typography variant="body1" fontWeight={700} color="primary">
              {fmtMoneda(item.precio_total, moneda)}
            </Typography>
          </Box>
        </Grid>
      </Grid>
    </Paper>
  );
});

// ═══════════ PASO 1: SELECTOR DE CLIENTE ═══════════
function StepCliente({ onSelect }) {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);
  const inputRef = useRef(null);
  const debounceRef = useRef(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  const handleSearch = (val) => {
    setQuery(val);
    clearTimeout(debounceRef.current);
    if (val.length < 2) {
      setResults([]);
      return;
    }
    setLoading(true);
    debounceRef.current = setTimeout(async () => {
      try {
        const data = await searchClientes(val, 15);
        setResults(Array.isArray(data) ? data : data?.data || []);
      } catch {
        setResults([]);
      } finally {
        setLoading(false);
      }
    }, 300);
  };

  return (
    <CenteredContent>
      <Box sx={{ textAlign: "center", mb: 3 }}>
        <Icon icon="mdi:account-search" width={56} style={{ opacity: 0.6 }} />
        <Typography variant="h5" fontWeight={600} sx={{ mt: 1 }}>
          Seleccionar Cliente
        </Typography>
        <Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
          Busca por nombre, razón social, RUC o documento del cliente
        </Typography>
      </Box>

      <SearchBox>
        <Icon icon="mdi:magnify" width={22} />
        <input
          ref={inputRef}
          value={query}
          onChange={(e) => handleSearch(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === "Enter" && results.length === 1) onSelect(results[0]);
          }}
          placeholder='Ej: "Juan Pérez", "80012345-6", "0981..."'
          autoComplete="off"
        />
        {loading && <Icon icon="mdi:loading" width={20} className="spin" />}
        {query && !loading && (
          <IconButton
            size="small"
            onClick={() => {
              setQuery("");
              setResults([]);
              inputRef.current?.focus();
            }}
          >
            <Icon icon="mdi:close" width={16} />
          </IconButton>
        )}
      </SearchBox>

      {query.length >= 2 && !loading && results.length === 0 && (
        <Typography
          variant="body2"
          color="text.secondary"
          sx={{ mt: 2, textAlign: "center" }}
        >
          No se encontraron clientes para "{query}"
        </Typography>
      )}

      {results.length > 0 && (
        <ResultsList>
          {results.map((c) => {
            const nombre =
              c.personas?.razon_social || c.nombre_fantasia || "Sin nombre";
            const ruc = c.personas?.ruc || c.personas?.nro_documento || "";
            return (
              <ResultCard key={c.id} onClick={() => onSelect(c)}>
                <Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
                  <Icon
                    icon={
                      c.tipo_cliente === "mayorista"
                        ? "mdi:store"
                        : "mdi:account-circle"
                    }
                    width={32}
                    style={{ opacity: 0.5 }}
                  />
                  <Box>
                    <Typography variant="body1" fontWeight={600}>
                      {nombre}
                    </Typography>
                    <Box
                      sx={{
                        display: "flex",
                        gap: 1,
                        flexWrap: "wrap",
                        mt: 0.25,
                      }}
                    >
                      {ruc && (
                        <Typography variant="caption" color="text.secondary">
                          <Icon
                            icon="mdi:card-account-details"
                            width={12}
                            style={{ verticalAlign: "middle", marginRight: 2 }}
                          />
                          {ruc}
                        </Typography>
                      )}
                      {c.personas?.telefono && (
                        <Typography variant="caption" color="text.secondary">
                          <Icon
                            icon="mdi:phone"
                            width={12}
                            style={{ verticalAlign: "middle", marginRight: 2 }}
                          />
                          {c.personas.telefono}
                        </Typography>
                      )}
                      {c.tipo_cliente && (
                        <Chip
                          label={c.tipo_cliente}
                          size="small"
                          variant="outlined"
                          sx={{ height: 18, fontSize: "0.65rem" }}
                        />
                      )}
                    </Box>
                  </Box>
                </Box>
                <Icon
                  icon="mdi:chevron-right"
                  width={20}
                  style={{ opacity: 0.3 }}
                />
              </ResultCard>
            );
          })}
        </ResultsList>
      )}

      <Typography
        variant="caption"
        color="text.secondary"
        sx={{ mt: 3, textAlign: "center", display: "block" }}
      >
        Tip: Presiona Enter cuando hay un solo resultado para seleccionarlo
      </Typography>
    </CenteredContent>
  );
}

// ═══════════ PASO 2: FORMULARIO DE SOLICITUD ═══════════
function StepFormulario({ cliente, onBack, onCreated }) {
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down("md"));
  const navigate = useNavigate();
  const { user } = useAuthStore();

  // Datos de la solicitud
  const [condicionesPago, setCondicionesPago] = useState([]);
  const [condicionPagoId, setCondicionPagoId] = useState("");
  const [vendedores, setVendedores] = useState([]);
  const [vendedorId, setVendedorId] = useState("");
  const [cobradores, setCobradores] = useState([]);
  const [cobradorId, setCobradorId] = useState("");
  const [sucursales, setSucursales] = useState([]);
  const [sucursalActiva, setSucursalActiva] = useState(null);
  const [puntoExpedicionActivo, setPuntoExpedicionActivo] = useState(null);
  const [numeracionActiva, setNumeracionActiva] = useState(null);
  const [moneda, setMoneda] = useState("PYG");
  const [intervaloDias, setIntervaloDias] = useState(30);
  const intervaloDiasRef = useRef(30);
  const [entregaInicial, setEntregaInicial] = useState(0);
  const [fechaEntrega, setFechaEntrega] = useState(() => hoyISO());
  // Cuotas financiadas (excluye la entrega). Fuente única de verdad para el cronograma.
  const [cantCuotasFinanciadas, setCantCuotasFinanciadas] = useState(1);
  const [fechaPrimeraCuota, setFechaPrimeraCuota] = useState(() => {
    const d = new Date();
    d.setDate(d.getDate() + 30);
    return d.toISOString().split("T")[0];
  });
  const [diaFijoPago, setDiaFijoPago] = useState(null); // null = sin día fijo (1-31, mensual o 1ra quincena)
  const [diaFijoPago2, setDiaFijoPago2] = useState(null); // null = una sola quincena (solo aplica quincenal)
  const [diaCobro, setDiaCobro] = useState(null); // null = sin restricción (1=Lun..7=Dom, para planes semanales)
  const [diasGraciaOverride, setDiasGraciaOverride] = useState(null); // null = hereda config global
  const [motivoOverrideGracia, setMotivoOverrideGracia] = useState("");
  const [graciaAnchor, setGraciaAnchor] = useState(null); // anchor del Popover de edición
  const [observaciones, setObservaciones] = useState("");

  // Plan de cuotas automático (addon "Planes de Cuotas Automáticos"). Si la empresa no
  // tiene el addon contratado, `getPlanesEmpresa` devuelve 403 y `planesAutomaticos`
  // queda vacío — el selector no se muestra y todo sigue funcionando como antes
  // (condición de pago o ingreso manual, sin interés).
  // Editar a mano el precio unitario en la solicitud es un privilegio aparte:
  // sin él, el precio queda fijo al de la lista de precios resuelta. Evita que
  // se cargue una solicitud con un precio arbitrario (o en 0) por error.
  const { can: canVentas } = usePermission("VENTAS");
  const puedeEditarPrecio = canVentas("VEN_SOL_PRECIO_EDITAR");

  const [planAutomaticoId, setPlanAutomaticoId] = useState("");
  const [cuotasPlanElegidas, setCuotasPlanElegidas] = useState("");
  const [cronogramaPlan, setCronogramaPlan] = useState(null);
  const [calculandoPlan, setCalculandoPlan] = useState(false);

  // Al elegir un plan (o cambiarlo), arrancar con su máximo de cuotas.
  const handleElegirPlanAutomatico = (id) => {
    setPlanAutomaticoId(id);
    const plan = planesAutomaticos.find((p) => p.id === id);
    setCuotasPlanElegidas(plan?.cantidad_cuotas || "");

    // Plan automático y cuotas por producto son excluyentes (ver
    // `handleSelectProducto`). Si ya había ítems cargados con una cuota por
    // producto, su `precio_unitario` es el precio FINANCIADO — al activar un
    // plan hay que devolverlos a precio de catálogo, o el plan calcularía su
    // interés sobre un precio que ya venía financiado (doble financiación).
    if (id) {
      setItems((prev) =>
        prev.map((it) => {
          if (!it.producto_precio_cuota_id) return it;
          const precioCatalogo = Number(it.precio_original ?? it.precio_unitario) || 0;
          return {
            ...it,
            producto_precio_cuota_id: null,
            plan_nombre: null,
            precio_unitario: precioCatalogo,
            precio_total: precioCatalogo * (Number(it.cantidad) || 1),
          };
        }),
      );
    }
  };

  // Configuración de límites (en producción vendrían de la config de sucursal/empresa)
  const PLAZO_MAXIMO_PRIMERA_CUOTA_DIAS = 90;

  // Período de gracia — se hereda de cob_config_mora salvo override explícito por solicitud.
  const { data: configMoraResp } = useQuery({
    queryKey: ["config-mora"],
    queryFn: getConfigMora,
    staleTime: 5 * 60 * 1000,
  });
  const configMora = configMoraResp?.data || configMoraResp || {};
  const diasGraciaGlobal = Number(configMora?.periodo_gracia ?? 0);
  const diasGraciaEfectivo = diasGraciaOverride != null ? diasGraciaOverride : diasGraciaGlobal;
  const hayOverrideGracia = diasGraciaOverride != null && diasGraciaOverride !== diasGraciaGlobal;
  const [items, setItems] = useState([]);
  // Fase 7: filtra por categoría/precio de los productos ya agregados a la
  // solicitud — firma en la queryKey para que se recalcule al cambiar `items`.
  const itemsParaPlanes = items.map((i) => ({
    productoId: i.producto_id,
    // El filtro espera el precio CONTADO de catálogo: `precio_unitario` puede
    // haber sido sobrescrito por un plan de cuotas por producto elegido al
    // agregar el ítem (ver `addItem` / `precio_original`).
    precioContado: i.precio_original ?? i.precio_unitario,
  }));
  // Monto a financiar, para que el backend excluya los planes cuyo monto
  // mínimo/máximo no lo admiten. Es el mismo número que después se manda a
  // `calcularCuotasPlan` (total − entrega), así el selector no ofrece un plan
  // que al elegirlo iba a fallar con "El monto total debe ser mayor a X".
  const montoParaPlanes = Math.max(
    0,
    items.reduce((s, i) => s + Number(i.precio_total || 0), 0) - (Number(entregaInicial) || 0),
  );

  // Debounce de la firma del carrito: sin esto, cada tecla en un campo de precio
  // genera una queryKey nueva → request nuevo + entrada de caché nueva.
  const firmaPlanes = JSON.stringify({ itemsParaPlanes, montoParaPlanes });
  const [firmaPlanesDebounced, setFirmaPlanesDebounced] = useState(firmaPlanes);
  useEffect(() => {
    const t = setTimeout(() => setFirmaPlanesDebounced(firmaPlanes), 400);
    return () => clearTimeout(t);
  }, [firmaPlanes]);

  const {
    data: planesAutomaticosResp,
    isSuccess: planesAutomaticosResueltos,
    isFetching: planesAutomaticosCargando,
  } = useQuery({
    queryKey: ["planes-cuotas-empresa-aplicables", firmaPlanesDebounced],
    queryFn: () => {
      const { itemsParaPlanes: its, montoParaPlanes: monto } = JSON.parse(firmaPlanesDebounced);
      return its.length > 0
        ? obtenerPlanesAplicables(its, monto > 0 ? monto : undefined)
        : getPlanesEmpresa({ activo: "true" });
    },
    // Mantiene la lista anterior mientras llega la nueva: evita que el bloque del
    // selector se desmonte y remonte (parpadeo) en cada cambio del carrito.
    placeholderData: keepPreviousData,
    staleTime: 5 * 60 * 1000,
    retry: false,
  });
  const planesAutomaticos = planesAutomaticosResp?.data || [];
  const planAutomaticoActivo = planesAutomaticos.find((p) => p.id === planAutomaticoId) || null;

  // Fase 7 (fix revisión final, Important 1): si el carrito cambia y el plan
  // elegido deja de aplicar (cambio de categoría/precio), `planesAutomaticos`
  // se refetchea sin ese plan — hay que limpiar la selección y todo lo
  // derivado de ella para no seguir enviando `plan_cuota_id`/cronograma de un
  // plan que ya no corresponde al carrito actual.
  // `planesAutomaticosResueltos` evita limpiar antes del primer resultado, y
  // `!planesAutomaticosCargando` evita decidir sobre la lista ANTERIOR: con
  // `keepPreviousData` los datos viejos siguen visibles mientras llega la nueva
  // lista, así que solo se evalúa cuando la query ya está asentada.
  useEffect(() => {
    if (!planesAutomaticosResueltos || planesAutomaticosCargando) return;
    if (!planAutomaticoId) return;
    const listaActual = planesAutomaticosResp?.data || [];
    const sigueValido = listaActual.some((p) => p.id === planAutomaticoId);
    if (!sigueValido) {
      setPlanAutomaticoId("");
      setCuotasPlanElegidas("");
      setCronogramaPlan(null);
    }
  }, [
    planesAutomaticosResp,
    planesAutomaticosResueltos,
    planesAutomaticosCargando,
    planAutomaticoId,
  ]);
  const [saving, setSaving] = useState(false);
  const [confirmOpen, setConfirmOpen] = useState(false);

  // Producto search
  const [prodQuery, setProdQuery] = useState("");
  const [prodResults, setProdResults] = useState([]);
  const [prodLoading, setProdLoading] = useState(false);
  const [prodOpen, setProdOpen] = useState(false);
  const [showBrowser, setShowBrowser] = useState(false);
  // Precio real resuelto por lista de precios para los resultados del buscador inline
  // (id producto -> { precioFinal, listaAplicada }). Mismo motivo que en ProductoBrowserModal.
  const [preciosResueltos, setPreciosResueltos] = useState({});
  const prodDebounce = useRef(null);
  const prodInputRef = useRef(null);

  // Cuotas selector
  const [cuotasOpen, setCuotasOpen] = useState(false);
  const [cuotasOpciones, setCuotasOpciones] = useState([]);
  const [cuotasProd, setCuotasProd] = useState(null);

  const clienteNombre =
    cliente.personas?.razon_social || cliente.nombre_fantasia || "";
  const clienteRuc =
    cliente.personas?.ruc || cliente.personas?.nro_documento || "";

  // ═══ Numeración (mismo patrón que PedidosMayoristas) ═══
  const { data: numeracionesData } = useQuery({
    queryKey: ["numeraciones-solicitud", sucursalActiva?.id],
    queryFn: () => getNumeracionesBySucursal(sucursalActiva?.id),
    enabled: !!sucursalActiva?.id,
  });

  const numeracionesSolicitud = useMemo(() => {
    if (!numeracionesData) return [];
    return (numeracionesData || []).filter(
      (n) => n.tipo_documento?.codigo === TIPO_DOC_SOLICITUD_CODIGO,
    );
  }, [numeracionesData]);

  const puntosExpedicion = useMemo(() => {
    const map = new Map();
    numeracionesSolicitud.forEach((n) => {
      if (n.punto_expedicion && !map.has(n.punto_expedicion.id)) {
        map.set(n.punto_expedicion.id, n.punto_expedicion);
      }
    });
    return Array.from(map.values());
  }, [numeracionesSolicitud]);

  const numeracionSiguiente = useMemo(() => {
    if (!numeracionActiva) return "";
    const pe = numeracionActiva.punto_expedicion;
    const est =
      pe?.empresas_sucursales?.punto_establecimiento || pe?.codigo || "001";
    const pexp = pe?.punto_expedicion || "001";
    const num = String(numeracionActiva.numero_actual).padStart(7, "0");
    return `${est}-${pexp}-${num}`;
  }, [numeracionActiva]);

  const tieneNumeracion = !!numeracionActiva;

  // Punto expedición por defecto
  useEffect(() => {
    if (puntosExpedicion.length > 0) {
      setPuntoExpedicionActivo(puntosExpedicion[0]);
    } else {
      setPuntoExpedicionActivo(null);
    }
  }, [puntosExpedicion]);

  // Numeración por defecto
  useEffect(() => {
    if (numeracionesSolicitud.length > 0 && puntoExpedicionActivo) {
      const numPE = numeracionesSolicitud.find(
        (n) => n.punto_expedicion_id === puntoExpedicionActivo.id,
      );
      setNumeracionActiva(numPE || null);
    } else {
      setNumeracionActiva(null);
    }
  }, [numeracionesSolicitud, puntoExpedicionActivo]);

  // ═══ Cargar datos referenciales ═══
  useEffect(() => {
    getCondicionesPago({ aplica_a: "VENTAS", activo: true })
      .then((data) => {
        const lista = Array.isArray(data) ? data : data?.data || [];
        setCondicionesPago(lista);
        const credito = lista.find((c) => c.cod_condicion_venta === 2);
        if (credito) setCondicionPagoId(credito.id);
      })
      .catch(() => {});

    getVendedoresActivos()
      .then((data) => {
        const lista = Array.isArray(data) ? data : data?.data || [];
        setVendedores(lista);
        // Buscar vendedor vinculado al usuario logueado
        const vinculado = lista.find((v) => v.usuario_id === user?.id);
        if (vinculado) setVendedorId(vinculado.id);
      })
      .catch(() => {});

    getCobradoresActivos()
      .then((data) => {
        const lista = Array.isArray(data) ? data : data?.data || [];
        setCobradores(lista);
      })
      .catch(() => {});

    getSucursales()
      .then((data) => {
        const lista = Array.isArray(data) ? data : data?.data || [];
        setSucursales(lista);
        if (lista.length > 0) {
          const defaultSuc =
            lista.find((s) => s.id === user?.sucursal_id) || lista[0];
          setSucursalActiva(defaultSuc);
        }
      })
      .catch(() => {});
  }, [user]);

  // Buscar productos
  useEffect(() => {
    clearTimeout(prodDebounce.current);
    if (prodQuery.length < 2) {
      setProdResults([]);
      setProdOpen(false);
      return;
    }
    setProdLoading(true);
    prodDebounce.current = setTimeout(async () => {
      try {
        const data = await buscarProductos(prodQuery, 12);
        const lista = Array.isArray(data) ? data : data?.data || [];
        setProdResults(lista);
        setProdOpen(true);
        const ids = lista.map((p) => p.id).filter(Boolean);
        if (ids.length > 0) {
          getPreciosProductosBatch(ids, cliente?.id)
            .then((resueltos) => setPreciosResueltos((prev) => ({ ...prev, ...resueltos })))
            .catch(() => {});
        }
      } catch {
        setProdResults([]);
      } finally {
        setProdLoading(false);
      }
    }, 300);
    return () => clearTimeout(prodDebounce.current);
  }, [prodQuery]);

  const handleSelectProducto = useCallback(async (productoOriginal) => {
    setProdQuery("");
    setProdResults([]);
    setProdOpen(false);

    // Resolver el precio real por lista de precios (mismo motor que usa el POS).
    // El buscador inline (`buscarProductos`) trae el campo `precio` crudo de la
    // tabla productos, que casi siempre es 0 si la empresa usa listas — ver
    // docs/info/aclaracion-gastos-cuotas.md. El modal de catálogo ya lo resuelve
    // en batch, pero se re-resuelve acá para cubrir ambos caminos por igual.
    let producto = productoOriginal;
    if (producto?.id && user?.empresa_id) {
      try {
        const resuelto = await getPrecioProducto({
          productoId: producto.id,
          empresaId: user.empresa_id,
          clienteId: cliente?.id,
        });
        const data = resuelto?.data || resuelto;
        if (data?.precioFinal != null) {
          producto = { ...producto, precio: data.precioFinal, lista_precios_aplicada: data.listaAplicada };
        }
      } catch {
        /* si falla, se sigue con el precio que ya traía */
      }
    }

    // Plan automático y cuotas por producto son excluyentes: el plan financia el
    // TOTAL de la solicitud (con su propio interés), mientras que las cuotas por
    // producto ya vienen con el precio financiado incorporado. Aplicar ambos
    // financiaría dos veces el mismo producto (interés del plan sobre un precio
    // que ya era financiado). Con un plan activo se agrega siempre a precio de
    // catálogo y el cronograma lo calcula el plan.
    if (planAutomaticoId) {
      addItem(producto, null);
      return;
    }

    try {
      const cuotas = await getProductoPrecioCuotasByProducto(producto.id);
      const activas = (
        Array.isArray(cuotas) ? cuotas : cuotas?.data || []
      ).filter((c) => c.activo);

      if (activas.length > 1) {
        setCuotasProd(producto);
        setCuotasOpciones(activas);
        setCuotasOpen(true);
        return;
      }
      if (activas.length === 1) {
        addItem(producto, activas[0]);
        return;
      }
    } catch {
      /* sin cuotas */
    }

    addItem(producto, null);
  }, [user?.empresa_id, cliente?.id, planAutomaticoId]);

  const addItem = (producto, cuota) => {
    // Precio de catálogo (contado, ya resuelto por lista de precios en
    // `handleSelectProducto`) — se preserva aparte porque `precio_unitario`
    // puede quedar sobrescrito más abajo por el total de un plan de cuotas
    // por producto (`cuota`). El filtro de planes automáticos (Fase 7,
    // Important 3) necesita el precio de catálogo, no el financiado.
    const precioOriginal = Number(producto.precio || 0);
    const precioTotal = cuota
      ? Number(cuota.monto_total) ||
        Number(cuota.monto_cuota) * cuota.cant_cuotas
      : precioOriginal;
    const precioUnitario = precioTotal;

    // Si el producto trae un plan sugerido, aplicamos la cantidad de cuotas
    // al nivel de la solicitud (fuente única de verdad). Ojo: si hay varios
    // productos con planes de distinta cantidad de cuotas, gana el último
    // agregado — por eso se avisa con `avisoCuotasDistintas` (abajo), para que
    // el plazo quede elegido a conciencia y no por orden de carga.
    if (cuota?.cant_cuotas) {
      setCantCuotasFinanciadas(Math.min(120, Number(cuota.cant_cuotas)));
    }

    setItems((prev) => [
      ...prev,
      {
        _key: Date.now() + Math.random(),
        producto_id: producto.id,
        producto,
        producto_precio_cuota_id: cuota?.id || null,
        descripcion: producto.descripcion,
        cantidad: 1,
        precio_unitario: precioUnitario,
        precio_total: precioTotal,
        precio_original: precioOriginal,
        porcentaje_iva: producto.porcentaje_iva || 10,
        plan_nombre: cuota?.nombre || null,
        plan_cant_cuotas: cuota?.cant_cuotas ? Number(cuota.cant_cuotas) : null,
      },
    ]);
    toast.success(`${producto.descripcion} agregado`);
    setTimeout(() => prodInputRef.current?.focus(), 100);
  };

  const handleSelectCuota = (cuota) => {
    if (cuotasProd) addItem(cuotasProd, cuota);
    setCuotasOpen(false);
    setCuotasProd(null);
  };

  const handleSkipCuota = () => {
    if (cuotasProd) addItem(cuotasProd, null);
    setCuotasOpen(false);
    setCuotasProd(null);
  };

  const removeItem = useCallback((key) => {
    setItems((prev) => {
      const next = prev.filter((i) => i._key !== key);
      if (next.length === 0) {
        // Resetear sidebar al quedar sin items
        setEntregaInicial(0);
        setIntervaloDias(30);
        intervaloDiasRef.current = 30;
        setDiaFijoPago(null);
        const d = new Date();
        d.setDate(d.getDate() + 30);
        setFechaPrimeraCuota(d.toISOString().split("T")[0]);
      }
      return next;
    });
  }, []);

  const updateItem = useCallback((key, field, rawVal) => {
    setItems((prev) =>
      prev.map((item) => {
        if (item._key !== key) return item;
        if (item[field] === rawVal) return item;
        const updated = { ...item, [field]: rawVal };
        const cant = Math.max(1, Number(updated.cantidad) || 1);
        const unit = Math.max(0, Number(updated.precio_unitario) || 0);
        updated.precio_total = cant * unit;
        return updated;
      }),
    );
  }, []);

  const montoTotal = useMemo(
    () => items.reduce((s, i) => s + Number(i.precio_total || 0), 0),
    [items],
  );

  // La solicitud tiene UN solo cronograma, pero cada producto puede traer un plan
  // de cuotas por producto (según su categoría) con distinta cantidad de cuotas.
  // Cuando no coinciden, `addItem` deja la del último producto agregado — un plazo
  // elegido por orden de carga, no a conciencia. Se avisa para que el usuario lo
  // defina explícitamente (o separe la venta en dos solicitudes).
  const avisoCuotasDistintas = useMemo(() => {
    if (planAutomaticoId) return null; // con plan automático el plazo lo define el plan
    const conPlan = items.filter((i) => i.plan_cant_cuotas > 0);
    const distintas = [...new Set(conPlan.map((i) => i.plan_cant_cuotas))];
    if (distintas.length < 2) return null;
    return {
      opciones: distintas.sort((a, b) => a - b),
      detalle: conPlan.map((i) => ({
        descripcion: i.descripcion,
        cuotas: i.plan_cant_cuotas,
      })),
    };
  }, [items, planAutomaticoId]);

  const condicionActiva = useMemo(
    () => condicionesPago.find((c) => c.id === condicionPagoId) || null,
    [condicionesPago, condicionPagoId],
  );

  // Al cambiar el intervalo (semanal ↔ quincenal ↔ mensual), reajustar
  // cantCuotasFinanciadas para preservar la duración total del financiamiento.
  // Ej: 12 cuotas × 30d = 360d → 24 cuotas × 15d = 360d → 52 cuotas × 7d ≈ 364d.
  const recalcularCuotasPorIntervalo = useCallback((nuevoIntervalo) => {
    const intervalo = Math.max(1, Number(nuevoIntervalo) || 30);
    const anterior = intervaloDiasRef.current || 30;
    intervaloDiasRef.current = intervalo;
    setIntervaloDias(intervalo);
    if (intervalo !== anterior) {
      setCantCuotasFinanciadas((prev) => {
        const actual = Math.max(1, Number(prev) || 1);
        const duracion = actual * anterior;
        const nuevas = Math.max(1, Math.round(duracion / intervalo));
        return Math.min(120, nuevas);
      });
    }
  }, []);

  // Aplicar cuotas de la condición de pago al nivel de la solicitud.
  const aplicarCuotasCondicion = useCallback((cantCuotas) => {
    if (!cantCuotas || cantCuotas <= 0) return;
    setCantCuotasFinanciadas(Math.min(120, Number(cantCuotas)));
  }, []);

  // Sincronizar cuando cambia condición de pago: aplicar intervalo Y cuotas
  const prevCondicionRef = useRef(condicionPagoId);
  useEffect(() => {
    if (prevCondicionRef.current === condicionPagoId) return;
    prevCondicionRef.current = condicionPagoId;
    if (condicionActiva?.intervalo_dias) {
      intervaloDiasRef.current = condicionActiva.intervalo_dias;
      setIntervaloDias(condicionActiva.intervalo_dias);
    }
    // Con plan automático activo, la cantidad de cuotas la manda el plan
    // (`cuotasPlanElegidas`): la condición de pago no debe pisarla.
    if (condicionActiva?.cuotas && !planAutomaticoId) {
      aplicarCuotasCondicion(condicionActiva.cuotas);
    }
  }, [condicionPagoId, condicionActiva, aplicarCuotasCondicion, planAutomaticoId]);

  // Con plan automático, la venta es a crédito por definición: si la condición
  // elegida era "Contado" (u otra no-crédito), se pasa a una de crédito para que
  // el comprobante declare iCondOpe/iCondCred correctamente.
  useEffect(() => {
    if (!planAutomaticoId) return;
    if (condicionActiva?.cod_condicion_venta === 2) return;
    const credito = condicionesPago.find((c) => c.cod_condicion_venta === 2);
    if (credito) setCondicionPagoId(credito.id);
  }, [planAutomaticoId, condicionActiva, condicionesPago]);

  const cuotaInicialTotal = Number(entregaInicial) || 0;
  const saldoFinanciar = Math.max(0, montoTotal - cuotaInicialTotal);

  const cantCuotasFinal = useMemo(
    () => Math.max(1, Math.min(Number(cantCuotasFinanciadas) || 1, 120)),
    [cantCuotasFinanciadas],
  );

  // Cronograma - cálculo estable sin crear objetos Date innecesarios
  const cronogramaLocal = useMemo(() => {
    if (cantCuotasFinal <= 0 || items.length === 0 || montoTotal <= 0)
      return [];

    const intervalo = intervaloDias || 30;

    // Helpers de calendario ────────────────────────────────────────────────
    const ultimoDiaDelMes = (year, month) => new Date(year, month + 1, 0).getDate();
    const clampDia = (year, month, dia) => {
      const fecha = new Date(year, month, 1);
      fecha.setDate(Math.min(dia, ultimoDiaDelMes(year, month)));
      return fecha;
    };
    // Devuelve la primera ocurrencia de alguno de los días fijos que sea >= fechaBase.
    // Considera roll-over al mes siguiente si todos los días del mes actual ya pasaron.
    const proximoDiaFijo = (fechaBase, ...dias) => {
      const diasValidos = dias.filter((d) => d != null);
      if (diasValidos.length === 0) return new Date(fechaBase);
      const base = new Date(fechaBase);
      const candidatos = [
        ...diasValidos.map((d) => clampDia(base.getFullYear(), base.getMonth(), d)),
        ...diasValidos.map((d) => clampDia(base.getFullYear(), base.getMonth() + 1, d)),
      ]
        .filter((f) => f >= base)
        .sort((a, b) => a.getTime() - b.getTime());
      return candidatos[0] || base;
    };

    // Calcular fecha de cada cuota
    // Si hay día fijo (ej: "siempre los 15"), las cuotas caen ese día de cada mes/quincena.
    // La PRIMERA cuota se ancla al próximo día fijo ≥ fechaPrimeraCuota, así el usuario
    // no genera cuotas anteriores a la fecha que eligió como arranque.
    const calcularFechaCuota = (nroCuota) => {
      const base = fechaPrimeraCuota
        ? new Date(fechaPrimeraCuota + "T12:00:00")
        : new Date();

      // Quincenal con DOS días fijos (ej. 5 y 20 alternantes)
      if (intervalo === 15 && diaFijoPago && diaFijoPago2) {
        const idx = nroCuota - 1;
        // Alinear la primera cuota al próximo día fijo ≥ base
        const primera = proximoDiaFijo(base, diaFijoPago, diaFijoPago2);
        const primeraCoincide = primera.getDate() === diaFijoPago;
        // Alternar días desde el que efectivamente arrancó
        const dia = idx % 2 === 0
          ? (primeraCoincide ? diaFijoPago : diaFijoPago2)
          : (primeraCoincide ? diaFijoPago2 : diaFijoPago);
        const mesOffset = Math.floor((idx + (primeraCoincide ? 0 : 1)) / 2);
        return clampDia(primera.getFullYear(), primera.getMonth() + mesOffset, dia);
      }

      // Mensual o quincenal con un solo día fijo → cae el día X de cada mes/quincena
      if (diaFijoPago && intervalo >= 15) {
        // Alinear la primera cuota al próximo día X ≥ base
        const primera = proximoDiaFijo(base, diaFijoPago);
        const mesesOffset = intervalo >= 28 ? 1 : 0.5;
        const totalMeses = Math.round(mesesOffset * (nroCuota - 1));
        return clampDia(primera.getFullYear(), primera.getMonth() + totalMeses, diaFijoPago);
      }

      // Sin día fijo: intervalo simple desde la primera cuota
      const baseMs = fechaPrimeraCuota
        ? new Date(fechaPrimeraCuota + "T12:00:00").getTime()
        : Date.now() + intervalo * 86400000;
      return new Date(baseMs + intervalo * (nroCuota - 1) * 86400000);
    };

    // Monto por cuota financiada = saldo / N cuotas. Última cuota absorbe redondeo.
    const cuotasPorNumero = {};
    if (cantCuotasFinal > 0 && saldoFinanciar > 0) {
      const montoPorCuota = redondearCuota(saldoFinanciar / cantCuotasFinal);
      for (let n = 1; n <= cantCuotasFinal; n++) {
        cuotasPorNumero[n] =
          n < cantCuotasFinal
            ? montoPorCuota
            : saldoFinanciar - montoPorCuota * (cantCuotasFinal - 1);
      }
    }

    const numeros = Object.keys(cuotasPorNumero)
      .map(Number)
      .sort((a, b) => a - b);

    // Calcular saldo pendiente de cada cuota hacia atrás (cuota N = suma de cuota N hasta la última)
    // Así cuota 1 = total, cuota 12 = monto de la última cuota
    const saldoPendiente = {};
    let acumulado = 0;
    for (let i = numeros.length - 1; i >= 0; i--) {
      acumulado += cuotasPorNumero[numeros[i]];
      saldoPendiente[numeros[i]] = acumulado;
    }

    const cuotasFinanciadas = numeros.map((num) => {
      const fecha = calcularFechaCuota(num);
      return {
        numero: num,
        fechaStr: fecha.toLocaleDateString("es-PY", {
          day: "2-digit",
          month: "2-digit",
          year: "2-digit",
        }),
        fechaLarga: fecha.toLocaleDateString("es-PY", {
          day: "2-digit",
          month: "short",
          year: "numeric",
        }),
        fechaISO: fecha.toISOString().split("T")[0],
        monto: cuotasPorNumero[num],
        saldo: saldoPendiente[num],
      };
    });

    // Si hay entrega inicial, se registra como cuota 1 (fecha = fechaEntrega, monto = entrega)
    // y las demás cuotas se renumeran 2..N+1. El cobrador la ve como una cuota más pendiente.
    if (cuotaInicialTotal <= 0) return cuotasFinanciadas;

    const fechaEntregaObj = fechaEntrega
      ? new Date(fechaEntrega + "T12:00:00")
      : new Date();
    const entregaRow = {
      numero: 1,
      fechaStr: fechaEntregaObj.toLocaleDateString("es-PY", {
        day: "2-digit",
        month: "2-digit",
        year: "2-digit",
      }),
      fechaLarga: fechaEntregaObj.toLocaleDateString("es-PY", {
        day: "2-digit",
        month: "short",
        year: "numeric",
      }),
      fechaISO: fechaEntregaObj.toISOString().split("T")[0],
      monto: cuotaInicialTotal,
      esEntrega: true,
    };
    const shifted = cuotasFinanciadas.map((c) => ({ ...c, numero: c.numero + 1 }));
    const filas = [entregaRow, ...shifted];

    // Recalcular saldo acumulado hacia atrás (cuota 1 saldo = total, última cuota saldo = su propio monto)
    let acumSaldo = 0;
    for (let i = filas.length - 1; i >= 0; i--) {
      acumSaldo += filas[i].monto;
      filas[i].saldo = acumSaldo;
    }
    return filas;
  }, [
    cantCuotasFinal,
    intervaloDias,
    saldoFinanciar,
    montoTotal,
    fechaPrimeraCuota,
    diaFijoPago,
    diaFijoPago2,
    cuotaInicialTotal,
    fechaEntrega,
    items.length,
  ]);

  // Si se eligió un plan de cuotas automático, el monto de cada cuota lo calcula el
  // backend (con interés, según el sistema del plan) en vez de la división simple.
  useEffect(() => {
    if (!planAutomaticoId || saldoFinanciar <= 0) {
      setCronogramaPlan(null);
      return;
    }
    let cancelado = false;
    setCalculandoPlan(true);
    calcularCuotasPlan({
      planCuotaId: planAutomaticoId,
      montoTotal: saldoFinanciar,
      monedaIso: moneda,
      cantidadCuotas: cuotasPlanElegidas ? Number(cuotasPlanElegidas) : undefined,
    })
      .then((res) => {
        if (cancelado) return;
        const detalle = res?.data?.cuotas_calculadas_detalle || [];
        setCronogramaPlan(
          detalle.map((d, idx) => ({
            numero: cuotaInicialTotal > 0 ? d.numero_cuota + 1 : d.numero_cuota,
            fechaISO: d.fecha_vencimiento?.split?.("T")?.[0] || d.fecha_vencimiento,
            fechaStr: new Date(d.fecha_vencimiento).toLocaleDateString("es-PY", { day: "2-digit", month: "2-digit", year: "2-digit" }),
            fechaLarga: new Date(d.fecha_vencimiento).toLocaleDateString("es-PY", { day: "2-digit", month: "short", year: "numeric" }),
            monto: Number(d.monto_cuota),
            capital: d.capital != null ? Number(d.capital) : null,
            interes: d.interes != null ? Number(d.interes) : null,
            saldo: detalle.slice(idx).reduce((s, c) => s + Number(c.monto_cuota), 0),
          })),
        );
      })
      .catch((err) => {
        if (!cancelado) {
          // El backend explica el motivo real (ej. "El monto total debe ser mayor
          // a Gs. 500.000" cuando el carrito no llega al mínimo del plan) — sin
          // esto el usuario solo veía un error genérico y no sabía qué corregir.
          const motivo =
            err?.response?.data?.message ||
            err?.message ||
            "No se pudieron calcular las cuotas del plan seleccionado";
          toast.error(motivo);
          setCronogramaPlan(null);
        }
      })
      .finally(() => {
        if (!cancelado) setCalculandoPlan(false);
      });
    return () => {
      cancelado = true;
    };
  }, [planAutomaticoId, saldoFinanciar, cuotaInicialTotal, moneda, cuotasPlanElegidas]);

  // Con plan automático: cronograma con interés viene del backend. Sin plan (o addon no
  // contratado): se mantiene exactamente el cálculo lineal sin interés de siempre.
  const cronogramaSinEntrega = planAutomaticoId ? cronogramaPlan || [] : cronogramaLocal;
  const cronograma =
    planAutomaticoId && cuotaInicialTotal > 0
      ? [
          {
            numero: 1,
            fechaStr: new Date(fechaEntrega || Date.now()).toLocaleDateString("es-PY", { day: "2-digit", month: "2-digit", year: "2-digit" }),
            fechaLarga: new Date(fechaEntrega || Date.now()).toLocaleDateString("es-PY", { day: "2-digit", month: "short", year: "numeric" }),
            fechaISO: fechaEntrega,
            monto: cuotaInicialTotal,
            esEntrega: true,
          },
          ...cronogramaSinEntrega,
        ]
      : cronogramaSinEntrega;

  // Validación quincenal: bloquear si ambos días están seteados pero son iguales
  // o su separación es < 5 días (claramente no es un cronograma quincenal válido).
  const quincenalInvalido = (() => {
    if (intervaloDias !== 15 || !diaFijoPago || !diaFijoPago2) return false;
    if (diaFijoPago === diaFijoPago2) return true;
    const a = Math.min(diaFijoPago, diaFijoPago2);
    const b = Math.max(diaFijoPago, diaFijoPago2);
    const sep = Math.min(b - a, 30 - b + a);
    return sep < 5;
  })();
  const entregaInvalida = cuotaInicialTotal > 0 && montoTotal > 0 && cuotaInicialTotal >= montoTotal;
  // Piso de precio: independiente del permiso de edición. Un precio en 0 (o por
  // debajo del costo del producto) no se puede confirmar ni siquiera teniendo
  // `VEN_SOL_PRECIO_EDITAR` — el permiso define QUIÉN edita, esto define hasta
  // dónde. Mismo criterio que el tope de descuento del POS, que nunca deja
  // facturar por debajo del costo sin autorización.
  const itemsPrecioInvalido = useMemo(
    () =>
      items.filter((i) => {
        const precio = Number(i.precio_unitario) || 0;
        if (precio <= 0) return true;
        const costo = Number(i.producto?.precio_costo) || 0;
        return costo > 0 && precio < costo;
      }),
    [items],
  );

  const puedeGuardar =
    items.length > 0 &&
    !!vendedorId &&
    !quincenalInvalido &&
    !entregaInvalida &&
    itemsPrecioInvalido.length === 0;

  const handleSave = async () => {
    if (items.length === 0) {
      toast.error("Agregue al menos un producto");
      return;
    }
    if (!vendedorId) {
      toast.error("Seleccione un vendedor");
      return;
    }
    if (itemsPrecioInvalido.length > 0) {
      const cero = itemsPrecioInvalido.filter((i) => !(Number(i.precio_unitario) > 0));
      toast.error(
        cero.length > 0
          ? `El precio no puede ser 0: ${cero.map((i) => i.descripcion).join(", ")}`
          : `El precio no puede estar por debajo del costo: ${itemsPrecioInvalido.map((i) => i.descripcion).join(", ")}`,
      );
      return;
    }
    if (entregaInvalida) {
      toast.error("La entrega inicial no puede ser mayor o igual al total. Si el cliente paga todo hoy, use una venta contado.");
      return;
    }
    setSaving(true);
    try {
      const payload = {
        // Cabecera
        cliente_id: cliente.id,
        vendedor_id: vendedorId || undefined,
        cobrador_id: cobradorId || undefined,
        sucursal_id: sucursalActiva?.id || undefined,
        punto_expedicion_id: puntoExpedicionActivo?.id || undefined,
        numeracion_id: numeracionActiva?.id || undefined,
        condicion_pago_id: condicionPagoId || undefined,
        plan_cuota_id: planAutomaticoId || undefined,
        moneda: moneda || "PYG",

        // Condiciones del crédito
        intervalo_dias: intervaloDias || 30,
        cuota_inicial_total: cuotaInicialTotal || 0,
        fecha_primera_cuota: fechaPrimeraCuota || undefined,
        // Los días fijos solo se envían para mensual/quincenal; el día semanal solo para semanal.
        dia_fijo_pago_1: intervaloDias !== 7 ? diaFijoPago || undefined : undefined,
        dia_fijo_pago_2: intervaloDias === 15 ? diaFijoPago2 || undefined : undefined,
        dia_cobro_semana: intervaloDias === 7 ? diaCobro || undefined : undefined,
        // dias_gracia_override solo se envía si el usuario cambió el default (queda null en BD si no)
        dias_gracia_override: hayOverrideGracia ? diasGraciaOverride : undefined,
        // dias_gracia_override queda null → hereda cob_config_mora
        // cant_cuotas incluye la cuota-entrega si existe (backend genera 1:1 desde cronograma)
        cant_cuotas: cronograma.length || undefined,

        // Totales
        monto_total: montoTotal,
        saldo_financiar: saldoFinanciar,

        // Observaciones
        observaciones: observaciones || undefined,

        // Crear como pendiente de aprobación (no borrador)
        estado: "pendiente_aprobacion",

        // Items con detalle completo. Las cuotas se manejan al nivel de la
        // solicitud (cantCuotasFinanciadas + entrega), no por ítem.
        items: items.map((i) => ({
          producto_id: i.producto_id,
          producto_precio_cuota_id: i.producto_precio_cuota_id || undefined,
          descripcion: i.descripcion,
          cantidad: i.cantidad,
          precio_total: i.precio_total,
          precio_unitario: Number(i.precio_unitario) || i.precio_total / (i.cantidad || 1),
          porcentaje_iva: i.porcentaje_iva,
        })),

        // Cronograma (fuente de verdad para cuentas a cobrar)
        cronograma: cronograma.map((c) => ({
          nro_cuota: c.numero,
          monto_cuota: c.monto,
          fecha_vencimiento: c.fechaISO,
          saldo_pendiente: c.saldo,
        })),
      };

      console.log("Creando solicitud:", payload);
      await createSolicitudCredito(payload);
      toast.success("Solicitud creada exitosamente");
      navigate("/solicitudes-credito");
    } catch (err) {
      console.error("Error creando solicitud:", err?.response?.data || err);
      toast.error(
        err?.response?.data?.message ||
          err?.message ||
          "Error al crear la solicitud",
      );
    } finally {
      setSaving(false);
    }
  };

  return (
    <FormLayout $isMobile={isMobile}>
      {/* ═══ PANEL IZQUIERDO: Productos ═══ */}
      <MainPanel>
        {/* Header con info del cliente y datos de la solicitud */}
        <Paper sx={{ p: 2, mb: 2 }}>
          {/* Fila 1: Cliente + Estado */}
          <Box
            sx={{
              display: "flex",
              justifyContent: "space-between",
              alignItems: "flex-start",
              flexWrap: "wrap",
              gap: 1,
              mb: 1.5,
            }}
          >
            <Box>
              <Box
                sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5 }}
              >
                <Tooltip title="Cambiar cliente">
                  <IconButton size="small" onClick={onBack}>
                    <Icon icon="mdi:arrow-left" width={20} />
                  </IconButton>
                </Tooltip>
                <Typography variant="h6" fontWeight={600}>
                  {clienteNombre}
                </Typography>
              </Box>
              <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", ml: 4.5 }}>
                {clienteRuc && (
                  <Chip
                    size="small"
                    icon={<Icon icon="mdi:card-account-details" width={14} />}
                    label={clienteRuc}
                    variant="outlined"
                  />
                )}
                {cliente.personas?.telefono && (
                  <Chip
                    size="small"
                    icon={<Icon icon="mdi:phone" width={14} />}
                    label={cliente.personas.telefono}
                    variant="outlined"
                  />
                )}
                {cliente.tipo_cliente && (
                  <Chip
                    size="small"
                    label={cliente.tipo_cliente}
                    color="primary"
                    variant="outlined"
                  />
                )}
                {cliente.personas?.direccion && (
                  <Chip
                    size="small"
                    icon={<Icon icon="mdi:map-marker" width={14} />}
                    label={cliente.personas.direccion}
                    variant="outlined"
                    sx={{ maxWidth: 250 }}
                  />
                )}
              </Box>
            </Box>
            <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
              {numeracionSiguiente && (
                <Tooltip title="Número de solicitud (se asigna al enviar a aprobación)">
                  <Chip
                    icon={<Icon icon="mdi:pound" width={14} />}
                    label={numeracionSiguiente}
                    size="small"
                    variant="outlined"
                    color="info"
                  />
                </Tooltip>
              )}
              <Chip
                icon={<Icon icon="mdi:file-edit" width={14} />}
                label="Nueva"
                size="small"
                color="info"
                variant="outlined"
              />
            </Box>
          </Box>

          <Divider sx={{ mb: 1.5 }} />

          {/* Vendedor */}
          <Autocomplete
            fullWidth
            size="small"
            sx={{ mb: 1.5 }}
            options={vendedores}
            value={vendedores.find((v) => v.id === vendedorId) || null}
            onChange={(_, val) => setVendedorId(val?.id || "")}
            getOptionLabel={(v) => `${v.nombre || ""} ${v.apellido || ""}`.trim() || "—"}
            isOptionEqualToValue={(o, val) => o.id === val?.id}
            noOptionsText={
              vendedores.length === 0
                ? "No hay vendedores activos"
                : "Sin coincidencias"
            }
            renderOption={(props, v) => (
              <li {...props} key={v.id}>
                <Typography variant="body2">
                  {`${v.nombre || ""} ${v.apellido || ""}`.trim() || "—"}
                </Typography>
              </li>
            )}
            renderInput={(params) => (
              <TextField
                {...params}
                label="Vendedor *"
                placeholder="Buscar vendedor…"
                error={!vendedorId && vendedores.length > 0}
                helperText={
                  vendedores.length === 0
                    ? "Configure vendedores activos en Configuración > Vendedores/Cobradores"
                    : undefined
                }
                FormHelperTextProps={{ sx: { color: "error.main" } }}
              />
            )}
          />

          {/* Cobrador */}
          <Autocomplete
            fullWidth
            size="small"
            sx={{ mb: 1.5 }}
            options={cobradores}
            value={cobradores.find((c) => c.id === cobradorId) || null}
            onChange={(_, val) => setCobradorId(val?.id || "")}
            getOptionLabel={(c) => `${c.nombre || ""} ${c.apellido || ""}`.trim() || "—"}
            isOptionEqualToValue={(o, val) => o.id === val?.id}
            noOptionsText="Sin coincidencias"
            renderOption={(props, c) => (
              <li {...props} key={c.id}>
                <Typography variant="body2">
                  {`${c.nombre || ""} ${c.apellido || ""}`.trim() || "—"}
                </Typography>
              </li>
            )}
            renderInput={(params) => (
              <TextField
                {...params}
                label="Cobrador"
                placeholder="Buscar cobrador (opcional)…"
              />
            )}
          />

          {/* Sucursal, Punto Exp., Moneda */}
          <Grid container spacing={1.5} alignItems="center">
            <Grid size={{ xs: 6, sm: 3, md: 4 }}>
              <FormControl fullWidth size="small">
                <InputLabel>Sucursal</InputLabel>
                <Select
                  value={sucursalActiva?.id || ""}
                  label="Sucursal"
                  onChange={(e) => {
                    const suc = sucursales.find((s) => s.id === e.target.value);
                    setSucursalActiva(suc || null);
                    setPuntoExpedicionActivo(null);
                    setNumeracionActiva(null);
                  }}
                >
                  {sucursales.map((s) => (
                    <MenuItem key={s.id} value={s.id}>
                      {s.descripcion || s.nombre || "Principal"}
                    </MenuItem>
                  ))}
                </Select>
              </FormControl>
            </Grid>
            <Grid size={{ xs: 6, sm: 3, md: 2 }}>
              <FormControl
                fullWidth
                size="small"
                disabled={puntosExpedicion.length === 0}
              >
                <InputLabel>Pto. Expedición</InputLabel>
                <Select
                  value={puntoExpedicionActivo?.id || ""}
                  label="Pto. Expedición"
                  onChange={(e) => {
                    const pe = puntosExpedicion.find(
                      (p) => p.id === e.target.value,
                    );
                    setPuntoExpedicionActivo(pe || null);
                  }}
                >
                  {puntosExpedicion.map((pe) => (
                    <MenuItem key={pe.id} value={pe.id}>
                      {pe.punto_expedicion || pe.codigo || pe.descripcion}
                    </MenuItem>
                  ))}
                </Select>
              </FormControl>
            </Grid>
            <Grid size={{ xs: 6, sm: 3, md: 2 }}>
              <FormControl fullWidth size="small">
                <InputLabel>Moneda</InputLabel>
                <Select
                  value={moneda}
                  label="Moneda"
                  onChange={(e) => setMoneda(e.target.value)}
                >
                  <MenuItem value="PYG">Gs. (PYG)</MenuItem>
                  <MenuItem value="USD">US$ (USD)</MenuItem>
                  <MenuItem value="BRL">R$ (BRL)</MenuItem>
                </Select>
              </FormControl>
            </Grid>
            {/* Alerta si no tiene numeración configurada */}
            {sucursalActiva && !tieneNumeracion && (
              <Grid size={12}>
                <Box
                  sx={{
                    display: "flex",
                    alignItems: "center",
                    gap: 1,
                    p: 1,
                    borderRadius: 1,
                    bgcolor: "rgba(245, 158, 11, 0.08)",
                    border: "1px solid rgba(245, 158, 11, 0.3)",
                  }}
                >
                  <Icon icon="mdi:alert" width={16} color="#f59e0b" />
                  <Typography variant="caption" color="warning.main">
                    No hay numeración de "Solicitud de crédito" configurada para
                    esta sucursal. Configure una numeración con tipo de
                    documento código 300 en Configuración &gt; Numeración.
                  </Typography>
                </Box>
              </Grid>
            )}
          </Grid>
        </Paper>

        {/* Historial crediticio del cliente */}
        <HistorialCrediticio cliente={cliente} />

        {/* Buscador de productos */}
        <Paper sx={{ p: 2, mb: 2 }}>
          <Typography
            variant="subtitle2"
            color="primary"
            sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 1 }}
          >
            <Icon icon="mdi:package-variant-plus" width={18} />
            Agregar productos a la solicitud
          </Typography>
          <Box sx={{ position: "relative" }}>
            <TextField
              fullWidth
              size="small"
              inputRef={prodInputRef}
              value={prodQuery}
              onChange={(e) => setProdQuery(e.target.value)}
              placeholder="Buscar por nombre, código o código de barras..."
              autoComplete="off"
              InputProps={{
                startAdornment: (
                  <InputAdornment position="start">
                    <Tooltip title="Buscar en catálogo" arrow>
                      <IconButton size="small" color="primary" onClick={() => setShowBrowser(true)} edge="start">
                        <Icon icon="mdi:magnify" width={20} />
                      </IconButton>
                    </Tooltip>
                  </InputAdornment>
                ),
                endAdornment: (prodLoading || prodQuery) ? (
                  <InputAdornment position="end">
                    {prodLoading
                      ? <Icon icon="mdi:loading" width={18} className="spin" />
                      : <IconButton size="small" onClick={() => { setProdQuery(""); setProdResults([]); prodInputRef.current?.focus(); }}>
                          <Icon icon="mdi:close" width={16} />
                        </IconButton>
                    }
                  </InputAdornment>
                ) : null,
              }}
              helperText="Si el producto tiene planes de cuotas predefinidos, se ofrecerán como sugerencia. Siempre puede ajustar cuotas y montos."
            />

            {/* Dropdown de resultados — usa MUI Paper para respetar el tema claro/oscuro */}
            {prodOpen && prodResults.length > 0 && (
              <Paper
                elevation={8}
                sx={{
                  position: "absolute",
                  top: "100%",
                  left: 0,
                  right: 0,
                  zIndex: 1300,
                  mt: 0.5,
                  maxHeight: 300,
                  overflowY: "auto",
                  borderRadius: 2,
                  border: 1,
                  borderColor: "divider",
                }}
              >
                {prodResults.map((p, idx) => (
                  <Box
                    key={p.id}
                    onClick={() => handleSelectProducto(p)}
                    sx={{
                      display: "flex",
                      alignItems: "center",
                      justifyContent: "space-between",
                      px: 1.5,
                      py: 1,
                      cursor: "pointer",
                      borderBottom: idx < prodResults.length - 1 ? 1 : 0,
                      borderColor: "divider",
                      "&:hover": { bgcolor: "action.hover" },
                    }}
                  >
                    <Box sx={{ flex: 1, minWidth: 0 }}>
                      <Typography variant="body2" fontWeight={600} noWrap>
                        {p.descripcion}
                      </Typography>
                      <Box sx={{ display: "flex", alignItems: "center", gap: 0.5, flexWrap: "wrap" }}>
                        <Typography variant="caption" color="text.secondary">
                          {p.cod_producto || p.codigo_barra || "Sin código"}
                        </Typography>
                        {p.stock_disponible !== undefined && p.maneja_inventario && (
                          <Chip
                            size="small"
                            icon={p.stock_bajo ? <Icon icon="mdi:alert" width={12} /> : undefined}
                            label={`Stock: ${p.stock_disponible}${p.stock_minimo > 0 ? ` (mín: ${p.stock_minimo})` : ""}`}
                            color={p.stock_disponible <= 0 ? "error" : p.stock_bajo ? "warning" : "default"}
                            variant={p.stock_bajo ? "filled" : "outlined"}
                            sx={{ height: 18, fontSize: "0.6rem" }}
                          />
                        )}
                      </Box>
                    </Box>
                    <Typography variant="body2" fontWeight={700} color="primary.main" sx={{ whiteSpace: "nowrap", ml: 1 }}>
                      {(() => {
                        const precioReal = preciosResueltos[p.id]?.precioFinal ?? p.precio;
                        return precioReal ? fmtMoneda(precioReal, moneda) : "";
                      })()}
                    </Typography>
                  </Box>
                ))}
              </Paper>
            )}
          </Box>

          {/* Modal de catálogo de productos */}
          <ProductoBrowserModal
            isOpen={showBrowser}
            onClose={() => setShowBrowser(false)}
            clienteId={cliente?.id}
            onSelect={(p) => {
              setShowBrowser(false);
              handleSelectProducto(p);
            }}
          />
        </Paper>

        {/* Tabla de items */}
        {items.length === 0 ? (
          <Paper sx={{ p: 4, textAlign: "center" }}>
            <Icon
              icon="mdi:package-variant-remove"
              width={48}
              style={{ opacity: 0.25 }}
            />
            <Typography color="text.secondary" sx={{ mt: 1 }}>
              Busque y agregue productos para armar la solicitud
            </Typography>
            <Typography
              variant="caption"
              color="text.secondary"
              sx={{ maxWidth: 400 }}
            >
              Los precios y cuotas sugeridos son referenciales. Puede negociar y
              ajustar cuotas, montos y frecuencia de pago libremente.
            </Typography>
          </Paper>
        ) : (
          <Paper sx={{ p: { xs: 1, sm: 2 } }}>
            <Typography
              variant="subtitle2"
              color="primary"
              sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 1 }}
            >
              <Icon icon="mdi:format-list-checks" width={18} />
              Productos de la solicitud ({items.length})
            </Typography>
            <Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
              {items.map((item, idx) => (
                <ItemRow
                  key={item._key}
                  item={item}
                  idx={idx}
                  onUpdate={updateItem}
                  onRemove={removeItem}
                  moneda={moneda}
                  puedeEditarPrecio={puedeEditarPrecio}
                />
              ))}
            </Box>
          </Paper>
        )}
      </MainPanel>

      {/* ═══ PANEL DERECHO: Resumen + Cronograma ═══ */}
      <SidePanel>
        {/* Condición de pago */}
        <Paper sx={{ p: 2, mb: 2 }}>
          <Typography
            variant="subtitle2"
            fontWeight={700}
            gutterBottom
            sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
          >
            <Icon icon="mdi:file-cog" width={18} />
            Condiciones
          </Typography>
          {/* Con un plan automático activo, la condición de pago deja de ser un
              desplegable de "N cuotas": la cantidad de cuotas la define el plan.
              Queda como dato fiscal (Crédito → iCondOpe/iCondCred de SIFEN), en
              solo lectura, para no mostrar dos números de cuotas que compiten. */}
          {planAutomaticoId ? (
            <TextField
              fullWidth
              size="small"
              label="Condición de Pago"
              value="Crédito"
              InputProps={{ readOnly: true }}
              sx={{ mb: 1.5 }}
              helperText="La cantidad de cuotas la define el plan automático"
            />
          ) : (
            <FormControl fullWidth size="small" sx={{ mb: 1.5 }}>
              <InputLabel>Condición de Pago</InputLabel>
              <Select
                value={condicionPagoId}
                label="Condición de Pago"
                onChange={(e) => setCondicionPagoId(e.target.value)}
              >
                {condicionesPago.map((c) => (
                  <MenuItem key={c.id} value={c.id}>
                    <Box
                      sx={{
                        display: "flex",
                        justifyContent: "space-between",
                        width: "100%",
                        alignItems: "center",
                      }}
                    >
                      <span>{c.descripcion}</span>
                      {c.intervalo_dias && (
                        <Typography
                          variant="caption"
                          color="text.secondary"
                          sx={{ ml: 1 }}
                        >
                          cada {c.intervalo_dias} días
                        </Typography>
                      )}
                    </Box>
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
          )}

          {planesAutomaticos.length > 0 && (
            <FormControl fullWidth size="small" sx={{ mb: 1.5 }}>
              <InputLabel shrink>Plan de cuotas automático</InputLabel>
              <Select
                value={planAutomaticoId}
                label="Plan de cuotas automático"
                displayEmpty
                onChange={(e) => handleElegirPlanAutomatico(e.target.value)}
              >
                <MenuItem value="">
                  Sin plan (división simple, sin interés)
                </MenuItem>
                {planesAutomaticos.map((p) => (
                  <MenuItem key={p.id} value={p.id}>
                    {p.nombre}
                    {Number(p.tasa_interes) > 0
                      ? ` (${Number(p.tasa_interes)}% ${p.tipo_interes})`
                      : " (sin interés)"}
                  </MenuItem>
                ))}
              </Select>
              {planAutomaticoActivo && (
                <Typography variant="caption" color="text.secondary" sx={{ mt: 0.5 }}>
                  hasta {planAutomaticoActivo.cantidad_cuotas} cuotas cada {planAutomaticoActivo.intervalo_dias} días
                  {calculandoPlan ? " · calculando…" : ""}
                </Typography>
              )}
            </FormControl>
          )}

          {/* Frecuencia de pago (solo si no hay un plan automático seleccionado —
              el plan ya define cantidad de cuotas e intervalo) */}
          {!planAutomaticoId && (
          <>
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{ display: "block", mt: 1.5, mb: 0.5 }}
          >
            Frecuencia de pago
          </Typography>
          <Grid container spacing={1}>
            <Grid size={5}>
              <BlurNumberInput
                size="small"
                label="Cada (días)"
                value={intervaloDias}
                onCommit={(v) =>
                  recalcularCuotasPorIntervalo(Math.max(1, v || 30))
                }
                inputProps={{ min: 1 }}
                fullWidth
              />
            </Grid>
            <Grid size={7}>
              <Box
                sx={{ display: "flex", gap: 0.5, flexWrap: "wrap", pt: 0.5 }}
              >
                {[7, 15, 30].map((d) => (
                  <Chip
                    key={d}
                    label={
                      d === 7 ? "Semanal" : d === 15 ? "Quincenal" : "Mensual"
                    }
                    size="small"
                    variant={intervaloDias === d ? "filled" : "outlined"}
                    color={intervaloDias === d ? "primary" : "default"}
                    onClick={() => recalcularCuotasPorIntervalo(d)}
                    sx={{ cursor: "pointer" }}
                  />
                ))}
              </Box>
            </Grid>
          </Grid>
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{ display: "block", mt: 0.5 }}
          >
            <Icon
              icon="mdi:information-outline"
              width={12}
              style={{ verticalAlign: "middle", marginRight: 2 }}
            />
            Al cambiar la frecuencia, las cuotas se recalculan manteniendo el
            monto total
          </Typography>
          </>
          )}

          <Divider sx={{ my: 1.5 }} />

          {/* ═══ Vencimientos ═══ */}
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{ display: "block", mb: 0.5 }}
          >
            Vencimiento de cuotas
          </Typography>

          {/* Fecha primera cuota */}
          {(() => {
            const hoy = hoyISO();
            const maxFecha = new Date();
            maxFecha.setDate(
              maxFecha.getDate() + PLAZO_MAXIMO_PRIMERA_CUOTA_DIAS,
            );
            const maxFechaStr = maxFecha.toISOString().split("T")[0];
            const diasHastaVenc = fechaPrimeraCuota
              ? Math.round(
                  (new Date(fechaPrimeraCuota + "T12:00:00") - new Date()) /
                    86400000,
                )
              : 0;
            const excedePlazo = diasHastaVenc > PLAZO_MAXIMO_PRIMERA_CUOTA_DIAS;

            // Detectar si la fecha elegida no coincide con la política de días fijos
            const diaSeleccionado = fechaPrimeraCuota
              ? new Date(fechaPrimeraCuota + "T12:00:00").getDate()
              : null;
            const diasPolitica = [diaFijoPago, diaFijoPago2].filter(Boolean);
            const noCoincideConPolitica =
              diaSeleccionado &&
              diasPolitica.length > 0 &&
              !diasPolitica.includes(diaSeleccionado);
            let ajustadaAStr = null;
            if (noCoincideConPolitica) {
              const base = new Date(fechaPrimeraCuota + "T12:00:00");
              const candidatos = [];
              for (const d of diasPolitica) {
                const c1 = new Date(base.getFullYear(), base.getMonth(), 1);
                const ult1 = new Date(c1.getFullYear(), c1.getMonth() + 1, 0).getDate();
                c1.setDate(Math.min(d, ult1));
                if (c1 >= base) candidatos.push(c1);
                const c2 = new Date(base.getFullYear(), base.getMonth() + 1, 1);
                const ult2 = new Date(c2.getFullYear(), c2.getMonth() + 1, 0).getDate();
                c2.setDate(Math.min(d, ult2));
                candidatos.push(c2);
              }
              const proxima = candidatos.sort((a, b) => a - b)[0];
              if (proxima) {
                ajustadaAStr = `${String(proxima.getDate()).padStart(2, "0")}/${String(proxima.getMonth() + 1).padStart(2, "0")}/${proxima.getFullYear()}`;
              }
            }

            const helperText = excedePlazo
              ? `Excede el plazo máximo de ${PLAZO_MAXIMO_PRIMERA_CUOTA_DIAS} días`
              : noCoincideConPolitica && ajustadaAStr
                ? `El día ${diaSeleccionado} no coincide con la política (${diasPolitica.join(" / ")}). Primera cuota ajustada a ${ajustadaAStr}.`
                : entregaInicial > 0
                  ? `Entrega se cobra hoy. Primera cuota en ${diasHastaVenc} días.`
                  : `En ${diasHastaVenc} días. Máximo ${PLAZO_MAXIMO_PRIMERA_CUOTA_DIAS} días desde hoy.`;

            return (
              <TextField
                type="date"
                size="small"
                label="Fecha primera cuota"
                value={fechaPrimeraCuota}
                onChange={(e) => setFechaPrimeraCuota(e.target.value)}
                fullWidth
                inputProps={{ min: hoy, max: maxFechaStr }}
                error={excedePlazo}
                color={noCoincideConPolitica && !excedePlazo ? "warning" : "primary"}
                focused={noCoincideConPolitica && !excedePlazo ? true : undefined}
                helperText={helperText}
                FormHelperTextProps={{
                  sx: noCoincideConPolitica && !excedePlazo ? { color: "warning.main" } : {},
                }}
                InputLabelProps={{ shrink: true }}
                sx={{ mb: 1.5 }}
              />
            );
          })()}

          {/* Día fijo de pago — según frecuencia */}
          {intervaloDias === 30 && (
            <FormControl fullWidth size="small" sx={{ mb: 1 }}>
              <InputLabel>Día fijo de pago</InputLabel>
              <Select
                value={diaFijoPago || ""}
                label="Día fijo de pago"
                onChange={(e) => {
                  const val = e.target.value;
                  setDiaFijoPago(val === "" ? null : Number(val));
                  setDiaFijoPago2(null);
                }}
              >
                <MenuItem value="">Sin día fijo (por intervalo)</MenuItem>
                {[1, 5, 10, 15, 20, 25, 28, 30].map((d) => (
                  <MenuItem key={d} value={d}>
                    Día {d} de cada mes
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
          )}

          {/* Quincenal — dos días fijos por mes (ej. 5 y 20, 15 y último) */}
          {intervaloDias === 15 && (() => {
            // Validación de separación: quincenal ≈ 15 días entre pagos.
            // Calculamos la distancia mínima considerando el roll-over de mes.
            const separacionQuincenal = (() => {
              if (!diaFijoPago || !diaFijoPago2) return null;
              const a = Math.min(diaFijoPago, diaFijoPago2);
              const b = Math.max(diaFijoPago, diaFijoPago2);
              const dentroDelMes = b - a;
              const cruzandoMes = 30 - b + a;
              return Math.min(dentroDelMes, cruzandoMes);
            })();
            const esIgual = diaFijoPago && diaFijoPago2 && diaFijoPago === diaFijoPago2;
            const separacionInvalida = separacionQuincenal !== null && separacionQuincenal < 5;
            const separacionSubOptima =
              separacionQuincenal !== null && separacionQuincenal >= 5 && separacionQuincenal < 10;

            return (
              <Box sx={{ mb: 1 }}>
                <Typography variant="caption" color="text.secondary" sx={{ display: "block", mb: 0.75, fontWeight: 600 }}>
                  Vencimientos quincenales
                </Typography>
                <Box sx={{ display: "flex", gap: 1, mb: 0.75 }}>
                  <FormControl size="small" sx={{ flex: 1 }} error={esIgual || separacionInvalida}>
                    <InputLabel>Primer día</InputLabel>
                    <Select
                      value={diaFijoPago || ""}
                      label="Primer día"
                      onChange={(e) => {
                        const val = e.target.value === "" ? null : Number(e.target.value);
                        setDiaFijoPago(val);
                        if (val && diaFijoPago2 === val) setDiaFijoPago2(null);
                      }}
                    >
                      <MenuItem value="">—</MenuItem>
                      {[1, 5, 10, 15, 20, 25, 28, 30].map((d) => (
                        <MenuItem key={d} value={d} disabled={diaFijoPago2 === d}>
                          Día {d}
                        </MenuItem>
                      ))}
                    </Select>
                  </FormControl>
                  <FormControl
                    size="small"
                    sx={{ flex: 1 }}
                    disabled={!diaFijoPago}
                    error={esIgual || separacionInvalida}
                  >
                    <InputLabel>Segundo día</InputLabel>
                    <Select
                      value={diaFijoPago2 || ""}
                      label="Segundo día"
                      onChange={(e) => {
                        const val = e.target.value === "" ? null : Number(e.target.value);
                        setDiaFijoPago2(val);
                      }}
                    >
                      <MenuItem value="">—</MenuItem>
                      {[5, 10, 15, 20, 25, 28, 30].map((d) => (
                        <MenuItem key={d} value={d} disabled={diaFijoPago === d}>
                          Día {d}
                        </MenuItem>
                      ))}
                    </Select>
                  </FormControl>
                </Box>
                <Box sx={{ display: "flex", gap: 0.5, flexWrap: "wrap" }}>
                  {[
                    { label: "15 y último", d1: 15, d2: 30 },
                    { label: "5 y 20", d1: 5, d2: 20 },
                    { label: "1 y 15", d1: 1, d2: 15 },
                    { label: "10 y 25", d1: 10, d2: 25 },
                  ].map((preset) => {
                    const activo = diaFijoPago === preset.d1 && diaFijoPago2 === preset.d2;
                    return (
                      <Chip
                        key={preset.label}
                        label={preset.label}
                        size="small"
                        variant={activo ? "filled" : "outlined"}
                        color={activo ? "primary" : "default"}
                        onClick={() => {
                          setDiaFijoPago(preset.d1);
                          setDiaFijoPago2(preset.d2);
                        }}
                        sx={{ fontSize: "0.68rem", height: 22 }}
                      />
                    );
                  })}
                </Box>
                {esIgual && (
                  <Typography variant="caption" color="error.main" sx={{ display: "block", mt: 0.75, fontWeight: 600 }}>
                    <Icon icon="mdi:alert-circle" width={12} style={{ verticalAlign: "middle", marginRight: 3 }} />
                    Los dos días no pueden ser iguales — quincenal implica dos fechas distintas por mes.
                  </Typography>
                )}
                {!esIgual && separacionInvalida && (
                  <Typography variant="caption" color="error.main" sx={{ display: "block", mt: 0.75, fontWeight: 600 }}>
                    <Icon icon="mdi:alert-circle" width={12} style={{ verticalAlign: "middle", marginRight: 3 }} />
                    Los días están muy cerca ({separacionQuincenal} días de separación) — quincenal implica ~15 días entre pagos.
                  </Typography>
                )}
                {!esIgual && separacionSubOptima && (
                  <Typography variant="caption" color="warning.main" sx={{ display: "block", mt: 0.75 }}>
                    <Icon icon="mdi:alert" width={12} style={{ verticalAlign: "middle", marginRight: 3 }} />
                    Separación baja ({separacionQuincenal} días). Quincenal habitual es ~15 días entre pagos.
                  </Typography>
                )}
              </Box>
            );
          })()}

          {/* Día de cobro semanal (solo visible cuando intervalo es 7 días) */}
          {intervaloDias === 7 && (
            <FormControl fullWidth size="small" sx={{ mb: 1 }}>
              <InputLabel>Día de cobro semanal</InputLabel>
              <Select
                value={diaCobro || ""}
                label="Día de cobro semanal"
                onChange={(e) => {
                  const val = e.target.value;
                  setDiaCobro(val === "" ? null : Number(val));
                }}
              >
                <MenuItem value="">Sin día fijo de semana</MenuItem>
                {[
                  { v: 1, label: "Lunes" },
                  { v: 2, label: "Martes" },
                  { v: 3, label: "Miércoles" },
                  { v: 4, label: "Jueves" },
                  { v: 5, label: "Viernes" },
                  { v: 6, label: "Sábado" },
                  { v: 7, label: "Domingo" },
                ].map(({ v, label }) => (
                  <MenuItem key={v} value={v}>
                    {label}
                  </MenuItem>
                ))}
              </Select>
              {diaCobro && (
                <Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: "block" }}>
                  Las cuotas se ajustarán automáticamente al próximo {["", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado", "domingo"][diaCobro]}.
                </Typography>
              )}
            </FormControl>
          )}
          {/* Período de gracia — chip compacto; edición en Popover on-demand */}
          <Tooltip
            title={
              hayOverrideGracia
                ? `Acuerdo especial (default empresa: ${diasGraciaGlobal} días). Motivo: ${motivoOverrideGracia || "—"}. Click para editar.`
                : `Configurado en Cobranzas > Config. de Mora. Click para acordar un plazo distinto sólo para este cliente.`
            }
          >
            <Chip
              icon={<Icon icon={hayOverrideGracia ? "mdi:pencil-outline" : "mdi:clock-alert-outline"} width={14} />}
              label={
                hayOverrideGracia
                  ? `${diasGraciaEfectivo} días de gracia (acuerdo)`
                  : `${diasGraciaEfectivo} días de gracia (config. mora)`
              }
              size="small"
              color={hayOverrideGracia ? "warning" : "default"}
              variant="outlined"
              onClick={(e) => {
                if (diasGraciaOverride == null) setDiasGraciaOverride(diasGraciaGlobal);
                setGraciaAnchor(e.currentTarget);
              }}
              sx={{ mb: 0.5, cursor: "pointer" }}
            />
          </Tooltip>
          <Popover
            open={Boolean(graciaAnchor)}
            anchorEl={graciaAnchor}
            onClose={() => setGraciaAnchor(null)}
            anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
            transformOrigin={{ vertical: "top", horizontal: "left" }}
          >
            <Box sx={{ p: 2, width: 320, display: "flex", flexDirection: "column", gap: 1.25 }}>
              <Typography variant="subtitle2" fontWeight={700}>
                Ajustar días de gracia
              </Typography>
              <Typography variant="caption" color="text.secondary" sx={{ lineHeight: 1.35 }}>
                Default de la empresa: <strong>{diasGraciaGlobal} días</strong>. Sólo modificar en casos de acuerdo especial (queda registrado en auditoría).
              </Typography>
              <TextField
                size="small"
                type="number"
                label="Días de gracia"
                value={diasGraciaOverride ?? diasGraciaGlobal}
                onChange={(e) => {
                  const v = e.target.value === "" ? 0 : Math.max(0, Number(e.target.value));
                  setDiasGraciaOverride(v);
                }}
                inputProps={{ min: 0, step: 1 }}
                fullWidth
              />
              <TextField
                size="small"
                label="Motivo del acuerdo"
                placeholder="Ej: cliente VIP, convenio, empleado…"
                value={motivoOverrideGracia}
                onChange={(e) => setMotivoOverrideGracia(e.target.value)}
                fullWidth
                multiline
                minRows={2}
                helperText={
                  diasGraciaOverride !== diasGraciaGlobal
                    ? "Requerido para el registro de auditoría"
                    : "No requerido si mantenés el default"
                }
              />
              <Box sx={{ display: "flex", justifyContent: "space-between", gap: 1 }}>
                <Button
                  size="small"
                  color="inherit"
                  onClick={() => {
                    setDiasGraciaOverride(null);
                    setMotivoOverrideGracia("");
                    setGraciaAnchor(null);
                  }}
                  disabled={diasGraciaOverride == null}
                >
                  Volver al default
                </Button>
                <Box sx={{ display: "flex", gap: 1 }}>
                  <Button size="small" onClick={() => setGraciaAnchor(null)}>
                    Cancelar
                  </Button>
                  <Button
                    size="small"
                    variant="contained"
                    onClick={() => setGraciaAnchor(null)}
                    disabled={
                      diasGraciaOverride !== diasGraciaGlobal &&
                      !motivoOverrideGracia.trim()
                    }
                  >
                    Aplicar
                  </Button>
                </Box>
              </Box>
            </Box>
          </Popover>
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{ display: "block", mt: 0.5, mb: 0.5, lineHeight: 1.3 }}
          >
            <Icon
              icon="mdi:information-outline"
              width={11}
              style={{ verticalAlign: "middle", marginRight: 2 }}
            />
            {diaFijoPago && diaFijoPago2
              ? `Las cuotas alternan entre el día ${diaFijoPago} y el ${diaFijoPago2} de cada mes.`
              : diaFijoPago
                ? `Todas las cuotas vencen el día ${diaFijoPago} de cada mes. Facilita al cliente recordar su fecha de pago.`
                : intervaloDias === 7 && diaCobro
                  ? `Cobro semanal fijo los ${["", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado", "domingo"][diaCobro]}.`
                  : `Las cuotas se calculan por intervalo (cada ${intervaloDias} días) desde la primera cuota.`}
          </Typography>

          <Divider sx={{ my: 1.5 }} />

          {/* Cuotas financiadas (excluye entrega) */}
          <Typography variant="caption" color="text.secondary" sx={{ display: "block", mb: 0.5 }}>
            Plan de pago
          </Typography>
          {planAutomaticoId ? (
            <BlurNumberInput
              size="small"
              label="Cuotas"
              value={cuotasPlanElegidas}
              onCommit={(v) => {
                const max = planAutomaticoActivo?.cantidad_cuotas || 1;
                setCuotasPlanElegidas(Math.max(1, Math.min(max, Number(v) || max)));
              }}
              inputProps={{ min: 1, max: planAutomaticoActivo?.cantidad_cuotas || 60, style: { textAlign: "center" } }}
              fullWidth
              helperText={
                `Máx. ${planAutomaticoActivo?.cantidad_cuotas || "-"} cuotas` +
                (saldoFinanciar > 0 && cronogramaSinEntrega[0]
                  ? ` — ${fmtMoneda(cronogramaSinEntrega[0].monto, moneda)} por cuota`
                  : "")
              }
            />
          ) : (
            <BlurNumberInput
              size="small"
              label="Cuotas financiadas"
              value={cantCuotasFinanciadas}
              onCommit={(v) =>
                setCantCuotasFinanciadas(Math.max(1, Math.min(120, Number(v) || 1)))
              }
              inputProps={{ min: 1, max: 120, style: { textAlign: "center" } }}
              fullWidth
              helperText={
                saldoFinanciar > 0
                  ? `${fmtMoneda(redondearCuota(saldoFinanciar / cantCuotasFinal), moneda)} por cuota (${cantCuotasFinal} × ${intervaloDias === 7 ? "semanal" : intervaloDias === 15 ? "quincenal" : intervaloDias === 30 ? "mensual" : `c/${intervaloDias}d`})`
                  : "Cantidad de cuotas para financiar el saldo (sin contar la entrega)"
              }
            />
          )}

          {avisoCuotasDistintas && (
            <Alert severity="warning" sx={{ mt: 1, py: 0.5 }}>
              <Typography variant="caption" sx={{ display: "block", fontWeight: 600, mb: 0.5 }}>
                Los productos traen planes de distinta cantidad de cuotas
              </Typography>
              <Typography variant="caption" sx={{ display: "block", mb: 0.5 }}>
                {avisoCuotasDistintas.detalle
                  .map((d) => `${d.descripcion}: ${d.cuotas}`)
                  .join(" · ")}
              </Typography>
              <Typography variant="caption" sx={{ display: "block", mb: 0.5 }}>
                La solicitud tiene un solo cronograma. Elegí el plazo que corresponde
                (o cargá los productos en solicitudes separadas):
              </Typography>
              <Box sx={{ display: "flex", gap: 0.5, flexWrap: "wrap" }}>
                {avisoCuotasDistintas.opciones.map((n) => (
                  <Chip
                    key={n}
                    size="small"
                    label={`${n} cuotas`}
                    color={Number(cantCuotasFinanciadas) === n ? "primary" : "default"}
                    variant={Number(cantCuotasFinanciadas) === n ? "filled" : "outlined"}
                    onClick={() => setCantCuotasFinanciadas(n)}
                  />
                ))}
              </Box>
            </Alert>
          )}

          <Divider sx={{ my: 1.5 }} />

          {/* Entrega inicial */}
          <Typography variant="caption" color="text.secondary" sx={{ display: "block", mb: 0.5 }}>
            Entrega inicial (opcional)
          </Typography>
          <Grid container spacing={1}>
            <Grid size={{ xs: 12, sm: 7 }}>
              <MonedaInput
                label="Monto de entrega"
                value={Number(entregaInicial) || 0}
                onChange={(v) => setEntregaInicial(v || 0)}
                moneda={moneda}
                max={montoTotal || undefined}
                fullWidth
                error={cuotaInicialTotal > montoTotal && montoTotal > 0}
                helperText={
                  cuotaInicialTotal > montoTotal && montoTotal > 0
                    ? `La entrega no puede superar el total (${fmtMoneda(montoTotal, moneda)})`
                    : cuotaInicialTotal > 0 && montoTotal > 0
                      ? `${Math.round((cuotaInicialTotal / montoTotal) * 100)}% del total — Se registra como cuota 1 del cronograma.`
                      : "Monto que el cliente entrega hoy. Se registra como primera cuota del cronograma."
                }
              />
            </Grid>
            <Grid size={{ xs: 12, sm: 5 }}>
              <TextField
                size="small"
                type="date"
                label="Fecha de entrega"
                value={fechaEntrega}
                onChange={(e) => setFechaEntrega(e.target.value)}
                InputLabelProps={{ shrink: true }}
                fullWidth
                disabled={cuotaInicialTotal <= 0}
                helperText={cuotaInicialTotal > 0 ? "Vencimiento de la cuota 1 (entrega)" : "Sólo si hay entrega"}
              />
            </Grid>
          </Grid>
        </Paper>

        {/* Observaciones */}
        <Paper sx={{ p: 2, mb: 2 }}>
          <TextField
            fullWidth
            size="small"
            label="Observaciones"
            placeholder="Condiciones especiales, notas para el supervisor, referencias..."
            multiline
            rows={2}
            value={observaciones}
            onChange={(e) => setObservaciones(e.target.value)}
            helperText="Visible en el PDF y para el supervisor al aprobar"
          />
        </Paper>

        {/* Resumen */}
        <Paper sx={{ p: 2, mb: 2 }}>
          <Typography
            variant="subtitle2"
            fontWeight={700}
            gutterBottom
            sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
          >
            <Icon icon="mdi:calculator" width={18} />
            Resumen
          </Typography>
          <Box sx={{ display: "flex", flexDirection: "column", gap: 0.75 }}>
            <SummaryRow>
              <Typography variant="body2" color="text.secondary">
                Productos
              </Typography>
              <Typography variant="body2" fontWeight={600}>
                {items.length}
              </Typography>
            </SummaryRow>
            {cantCuotasFinal > 0 && items.length > 0 && (
              <SummaryRow>
                <Typography variant="body2" color="text.secondary">
                  Cuotas financiadas
                </Typography>
                <Chip
                  label={`${cantCuotasFinal} × ${intervaloDias === 7 ? "semanal" : intervaloDias === 15 ? "quincenal" : intervaloDias === 30 ? "mensual" : `c/${intervaloDias}d`}`}
                  size="small"
                  color="info"
                  variant="outlined"
                />
              </SummaryRow>
            )}
            <Divider sx={{ my: 0.25 }} />
            <SummaryRow>
              <Typography variant="body2" fontWeight={600}>
                Total solicitud
              </Typography>
              <Typography variant="body2" fontWeight={700}>
                {fmtMoneda(montoTotal, moneda)}
              </Typography>
            </SummaryRow>
            {cuotaInicialTotal > 0 && (
              <>
                <SummaryRow>
                  <Typography variant="body2" color="text.secondary">
                    Entrega inicial
                  </Typography>
                  <Typography
                    variant="body2"
                    fontWeight={600}
                    color="warning.main"
                  >
                    - {fmtMoneda(cuotaInicialTotal, moneda)}
                  </Typography>
                </SummaryRow>
                <Divider sx={{ my: 0.25 }} />
              </>
            )}
            <SummaryRow>
              <Typography variant="subtitle1" fontWeight={700}>
                A financiar
              </Typography>
              <Typography variant="subtitle1" fontWeight={700} color="primary">
                {fmtMoneda(saldoFinanciar, moneda)}
              </Typography>
            </SummaryRow>
            {cantCuotasFinal > 0 && saldoFinanciar > 0 && (
              <Typography
                variant="caption"
                color="text.secondary"
                sx={{ textAlign: "right" }}
              >
                ≈{" "}
                {fmtMoneda(redondearCuota(saldoFinanciar / cantCuotasFinal), moneda)}{" "}
                por cuota
              </Typography>
            )}
          </Box>
        </Paper>

        {/* Cronograma de cuotas (componente memo) */}
        <CronogramaPagos
          cronograma={cronograma}
          saldoFinanciar={saldoFinanciar}
          intervaloDias={intervaloDias}
          moneda={moneda}
        />

        {/* Botón crear */}
        <Paper sx={{ p: 2, position: "sticky", bottom: 0 }}>
          <Button
            fullWidth
            variant="contained"
            size="large"
            sx={{ py: 1.5 }}
            startIcon={<Icon icon="mdi:content-save-check" width={20} />}
            onClick={() => setConfirmOpen(true)}
            disabled={saving || !puedeGuardar}
          >
            {saving ? "Guardando..." : "Confirmar Solicitud"}
          </Button>
          {!puedeGuardar && (
            <Typography
              variant="caption"
              color="error"
              sx={{ display: "block", textAlign: "center", mt: 0.5 }}
            >
              {items.length === 0 && "Agregue al menos un producto"}
              {items.length > 0 && !vendedorId && "Seleccione un vendedor"}
              {items.length > 0 && vendedorId && quincenalInvalido && "Ajuste los días de pago quincenal (deben ser distintos y con separación razonable)"}
              {items.length > 0 &&
                vendedorId &&
                !quincenalInvalido &&
                itemsPrecioInvalido.length > 0 &&
                (itemsPrecioInvalido.some((i) => !(Number(i.precio_unitario) > 0))
                  ? `Precio en 0: ${itemsPrecioInvalido
                      .filter((i) => !(Number(i.precio_unitario) > 0))
                      .map((i) => i.descripcion)
                      .join(", ")}`
                  : `Precio por debajo del costo: ${itemsPrecioInvalido
                      .map((i) => i.descripcion)
                      .join(", ")}`)}
            </Typography>
          )}
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{ display: "block", textAlign: "center", mt: 0.5 }}
          >
            Se enviará a aprobación del supervisor. Una vez aprobada, el cajero
            podrá facturarla.
          </Typography>
        </Paper>
      </SidePanel>

      <Dialog open={confirmOpen} onClose={() => setConfirmOpen(false)} maxWidth="xs" fullWidth>
        <DialogTitle>Confirmar solicitud de crédito</DialogTitle>
        <DialogContent dividers>
          <Typography variant="body2" color="text.secondary" gutterBottom>
            Cliente
          </Typography>
          <Typography variant="body1" fontWeight={600} gutterBottom>
            {cliente?.personas?.razon_social || cliente?.nombre_fantasia || "—"}
          </Typography>
          <Divider sx={{ my: 1.5 }} />
          <Box sx={{ display: "flex", justifyContent: "space-between", mb: 0.5 }}>
            <Typography variant="body2" color="text.secondary">Productos</Typography>
            <Typography variant="body2">{items.length}</Typography>
          </Box>
          <Box sx={{ display: "flex", justifyContent: "space-between", mb: 0.5 }}>
            <Typography variant="body2" color="text.secondary">Total</Typography>
            <Typography variant="body2" fontWeight={600}>{fmtMoneda(montoTotal, moneda)}</Typography>
          </Box>
          {cuotaInicialTotal > 0 && (
            <Box sx={{ display: "flex", justifyContent: "space-between", mb: 0.5 }}>
              <Typography variant="body2" color="text.secondary">Entrega (cuota 1)</Typography>
              <Typography variant="body2">{fmtMoneda(cuotaInicialTotal, moneda)}</Typography>
            </Box>
          )}
          {cantCuotasFinal > 0 && saldoFinanciar > 0 && (
            <Box sx={{ display: "flex", justifyContent: "space-between" }}>
              <Typography variant="body2" color="text.secondary">
                {cuotaInicialTotal > 0 ? "Cuotas financiadas" : "Cuotas"}
              </Typography>
              <Typography variant="body2">
                {cantCuotasFinal} × {fmtMoneda(saldoFinanciar / cantCuotasFinal, moneda)}
              </Typography>
            </Box>
          )}
          <Divider sx={{ my: 1.5 }} />
          <Typography variant="caption" color="text.secondary">
            La solicitud quedará pendiente de aprobación. El supervisor deberá aprobarla antes de facturar.
          </Typography>
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setConfirmOpen(false)} disabled={saving}>Cancelar</Button>
          <Button
            variant="contained"
            onClick={() => { setConfirmOpen(false); handleSave(); }}
            disabled={saving}
          >
            {saving ? "Guardando..." : "Confirmar"}
          </Button>
        </DialogActions>
      </Dialog>

      {/* ═══ SELECTOR DE CUOTAS ═══ */}
      {cuotasOpen && (
        <CuotasOverlay onClick={handleSkipCuota}>
          <CuotasCard onClick={(e) => e.stopPropagation()}>
            <Box
              sx={{
                display: "flex",
                justifyContent: "space-between",
                alignItems: "center",
                mb: 2,
              }}
            >
              <Box>
                <Typography variant="h6" fontWeight={600}>
                  <Icon
                    icon="mdi:credit-card-clock"
                    width={22}
                    style={{ verticalAlign: "middle", marginRight: 6 }}
                  />
                  Seleccionar plan de cuotas
                </Typography>
                <Typography
                  variant="body2"
                  color="text.secondary"
                  sx={{ mt: 0.25 }}
                >
                  {cuotasProd?.descripcion}
                </Typography>
              </Box>
              <IconButton size="small" onClick={handleSkipCuota}>
                <Icon icon="mdi:close" width={20} />
              </IconButton>
            </Box>
            <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
              {cuotasOpciones.map((cuota) => {
                const total =
                  Number(cuota.monto_total || 0) ||
                  Number(cuota.monto_cuota) * cuota.cant_cuotas;
                return (
                  <CuotaOption
                    key={cuota.id}
                    onClick={() => handleSelectCuota(cuota)}
                  >
                    <Box>
                      <Typography variant="body1" fontWeight={600}>
                        {cuota.nombre}
                      </Typography>
                      <Typography variant="body2" color="text.secondary">
                        {cuota.cant_cuotas} cuotas de{" "}
                        {fmtMoneda(Number(cuota.monto_cuota), moneda)}
                        {cuota.cuota_inicial
                          ? ` | Entrada: ${fmtMoneda(Number(cuota.cuota_inicial), moneda)}`
                          : ""}
                      </Typography>
                    </Box>
                    <Typography variant="h6" fontWeight={700} color="primary">
                      {fmtMoneda(total, moneda)}
                    </Typography>
                  </CuotaOption>
                );
              })}
            </Box>
            <Button
              fullWidth
              variant="text"
              sx={{ mt: 2 }}
              onClick={handleSkipCuota}
            >
              Usar precio contado (
              {cuotasProd?.precio
                ? fmtMoneda(Number(cuotasProd.precio), moneda)
                : "-"}
              )
            </Button>
          </CuotasCard>
        </CuotasOverlay>
      )}
    </FormLayout>
  );
}

// ═══════════ PÁGINA PRINCIPAL ═══════════
export function NuevaSolicitudCredito() {
  const navigate = useNavigate();
  const [cliente, setCliente] = useState(null);

  return (
    <PageWrapper>
      {/* Top bar */}
      <TopBar>
        <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
          <Tooltip title="Volver a solicitudes">
            <IconButton
              size="small"
              onClick={() => navigate("/solicitudes-credito")}
            >
              <Icon icon="mdi:arrow-left" width={22} />
            </IconButton>
          </Tooltip>
          <Icon icon="mdi:file-document-plus" width={22} />
          <Typography variant="h6" fontWeight={600}>
            Nueva Solicitud de Crédito
          </Typography>
        </Box>
        <StepIndicator>
          <StepDot $active={!cliente} $done={!!cliente}>
            1
          </StepDot>
          <StepLabel $active={!cliente}>Cliente</StepLabel>
          <StepLine $done={!!cliente} />
          <StepDot $active={!!cliente}>2</StepDot>
          <StepLabel $active={!!cliente}>Productos</StepLabel>
        </StepIndicator>
      </TopBar>

      <Box sx={{ px: { xs: 1.5, sm: 2 }, pt: 1.5, pb: 0.5 }}>
        <ScreenGuia
          titulo="¿Cómo funciona la solicitud de crédito?"
          moduloPadre="Ventas"
          intro="Registrás una intención de venta a crédito. Al aprobarse, se factura y se genera el cronograma de cobros."
          pasos={[
            {
              icon: "mdi:account-search-outline",
              color: "#3b82f6",
              titulo: "1. Elegí el cliente",
              desc: "Buscá por razón social, RUC o CI. Revisá su historial crediticio antes de continuar.",
            },
            {
              icon: "mdi:cart-plus",
              color: "#22c55e",
              titulo: "2. Agregá productos",
              desc: "Cargá los productos, cantidad y monto por cuota. El total se calcula automáticamente.",
            },
            {
              icon: "mdi:calendar-clock",
              color: "#f59e0b",
              titulo: "3. Ajustá el cronograma",
              desc: "Definí frecuencia, fecha de la primera cuota y entrega inicial (opcional).",
            },
            {
              icon: "mdi:check-decagram-outline",
              color: "#8b5cf6",
              titulo: "4. Enviás a aprobación",
              desc: "Un supervisor revisa y aprueba. Después el cajero factura y las cuotas quedan visibles para el cobrador.",
            },
          ]}
          notas={[
            {
              icon: "mdi:cash-fast",
              text: "Entrega inicial: monto que el cliente paga hoy y que reduce el saldo a financiar (por eso cambia el cronograma). Se cobra hoy como cuota 1 y el saldo se reparte en las cuotas restantes; la próxima cuota vence en el período normal, no se adelanta. El total no cambia y no puede ser mayor o igual al total (eso sería venta al contado).",
            },
            {
              icon: "mdi:alert-circle-outline",
              text: "No confundir: pagar hoy una cuota del plan —entera o en parte— NO es entrega, es un abono de cuota (se registra desde Cobros). La entrega es solo la que achica el saldo antes de armar las cuotas.",
            },
            {
              icon: "mdi:shield-alert-outline",
              text: "Si el cliente tiene cuotas vencidas, evaluá el riesgo antes de aprobar un crédito nuevo — se muestra el detalle en el historial crediticio.",
            },
          ]}
        />
      </Box>

      {/* Content */}
      {!cliente ? (
        <StepCliente onSelect={setCliente} />
      ) : (
        <StepFormulario
          cliente={cliente}
          onBack={() => setCliente(null)}
          onCreated={() => navigate("/solicitudes-credito")}
        />
      )}
    </PageWrapper>
  );
}

// ═══════════ STYLED COMPONENTS ═══════════

const PageWrapper = styled.div`
  height: calc(100vh - 80px);
  margin-top: 50px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
`;

const TopBar = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 20px;
  border-bottom: 1px solid ${({ theme }) => theme.palette?.divider || "#333"};
  flex-shrink: 0;
`;

const StepIndicator = styled.div`
  display: flex;
  align-items: center;
  gap: 8px;

  @media (max-width: 600px) {
    display: none;
  }
`;

const StepDot = styled.div`
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 700;
  background: ${({ $active, $done }) =>
    $done ? "#10b981" : $active ? "#3b82f6" : "transparent"};
  color: ${({ $active, $done }) => ($active || $done ? "#fff" : "#999")};
  border: 2px solid
    ${({ $active, $done }) =>
      $done ? "#10b981" : $active ? "#3b82f6" : "#555"};
  transition: all 0.3s;
`;

const StepLabel = styled.span`
  font-size: 0.8rem;
  font-weight: ${({ $active }) => ($active ? 600 : 400)};
  color: ${({ $active }) => ($active ? "inherit" : "#999")};
`;

const StepLine = styled.div`
  width: 40px;
  height: 2px;
  background: ${({ $done }) => ($done ? "#10b981" : "#555")};
  transition: background 0.3s;
`;

const CenteredContent = styled.div`
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 40px 20px;
  overflow-y: auto;
  max-width: 560px;
  margin: 0 auto;
  width: 100%;
`;

const SearchBox = styled.div`
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-radius: 10px;
  border: 2px solid ${({ theme }) => theme.palette?.divider || "#444"};
  width: 100%;
  transition: border-color 0.2s;

  &:focus-within {
    border-color: #3b82f6;
  }

  input {
    flex: 1;
    border: none;
    background: transparent;
    outline: none;
    font-size: 1rem;
    color: inherit;
    font-family: inherit;

    &::placeholder {
      opacity: 0.5;
    }
  }

  .spin {
    animation: spin 1s linear infinite;
  }

  @keyframes spin {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(360deg);
    }
  }
`;

const ResultsList = styled.div`
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  margin-top: 16px;
  max-height: 350px;
  overflow-y: auto;
`;

const ResultCard = styled.div`
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-radius: 8px;
  cursor: pointer;
  border: 1px solid ${({ theme }) => theme.palette?.divider || "#333"};
  transition: all 0.15s;

  &:hover {
    border-color: #3b82f6;
    background: rgba(59, 130, 246, 0.06);
  }
`;

const FormLayout = styled.div`
  flex: 1;
  display: grid;
  grid-template-columns: ${({ $isMobile }) =>
    $isMobile ? "1fr" : "1fr 420px"};
  gap: 0;
  overflow: hidden;
`;

const MainPanel = styled.div`
  padding: 16px;
  overflow-y: auto;
`;

const SidePanel = styled.div`
  padding: 16px;
  overflow-y: auto;
  border-left: 1px solid ${({ theme }) => theme.palette?.divider || "#333"};

  @media (max-width: 900px) {
    border-left: none;
    border-top: 1px solid ${({ theme }) => theme.palette?.divider || "#333"};
  }
`;

const SummaryRow = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
`;


const CuotasOverlay = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1300;
  padding: 20px;
`;

const CuotasCard = styled.div`
  background: ${({ theme }) => theme.palette?.background?.paper || "#1e1e1e"};
  border-radius: 12px;
  padding: 24px;
  min-width: 380px;
  max-width: 500px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);

  @media (max-width: 600px) {
    min-width: 0;
    width: 100%;
  }
`;

const CuotaOption = styled.div`
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-radius: 8px;
  cursor: pointer;
  border: 1px solid ${({ theme }) => theme.palette?.divider || "#444"};
  transition: all 0.15s;

  &:hover {
    border-color: #3b82f6;
    background: rgba(59, 130, 246, 0.06);
  }
`;
