import { Icon } from "@iconify/react/dist/iconify.js";
import {
  Box,
  Button,
  Chip,
  CircularProgress,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Grid,
  Paper,
  TextField,
  Typography,
} from "@mui/material";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useSearchParams } from "react-router-dom";
import io from "socket.io-client";
import { toast } from "sonner";
import styled, { ThemeProvider } from "styled-components";
import { Device } from "../../styles/breakpoints";
import { blur_in } from "../../styles/keyframes";

import { UserAuth } from "../../context/AuthContent";
import { useAuthStore } from "../../store/AuthStore";
import { useSubmodulo } from "../../hooks/useSubmodulo.js";
import { useEmpresa } from "../../context/EmpresaContext";
import { hoyISO } from "../../utils/fecha";
import { useThemeStore } from "../../store/ThemeStore";
import { usePosConfigQuery } from "../../tanstack/PosConfigStack";

import {
  getDepositoPrincipal,
  getDepositos,
} from "../../api/depositos.service";
import {
  getEmpresaMonedas,
  getUltimaCotizacion,
} from "../../api/monedas.service";
import { getNumeracionesBySucursal } from "../../api/numeraciones.service";
import {
  getCondicionesPago,
  getMediosPagoActivos,
} from "../../api/pagos.service";
import { getPlanesEmpresa, obtenerPlanesAplicables } from "../../api/planes-cuotas.service";
import { getReferenciales } from "../../api/referenciales.service";
import { getAccessToken } from "../../api/api.config";
import { solicitarAutorizacion } from "../../api/pos-security.service";
import { getSucursales } from "../../api/sucursales.service";
import {
  abrirCaja,
  cerrarCaja,
  getCajasDisponibles,
  getSesionActiva,
} from "../../api/tesoreria.service";

import { getDirecciones } from "../../api/clientes.service";
import { createFactura } from "../../api/facturas.service";
import { getPedido, marcarFacturado } from "../../api/pedidos.service";
import { getOrdenVenta, getOrdenVentaItemsFacturados, marcarOrdenFacturada } from "../../api/ordenes.service";
import { facturarPresupuestoDirecto, getPresupuesto } from "../../api/presupuestos.service";
import { cantidadDesdeMonto, decimalesCantidad, permiteMontoEditable, precioNetoItem } from "../../utils/montoEditable";
import {
  getSolicitudCredito,
  marcarSolicitudFacturada,
} from "../../api/solicitud-credito.service";
import {
  AFECTACION_IVA,
  calcularSubtotales,
  construirItemDetalle,
  calcularPorcentajeDescuentoEfectivo,
  afectacionDesdeTasa,
  resolverAfectacionIva,
} from "../../utils/invoiceCalculations";
import KudePreviewModal from "../organismos/KudePreviewModal";
import { DocumentoPreviewModal } from "../organismos/DocumentoPreviewModal";
import { ActionButtons } from "../organismos/POSAdmin/ActionButtons";
import { InvoiceHeader } from "../organismos/POSAdmin/InvoiceHeader";
import { ItemsTable } from "../organismos/POSAdmin/ItemsTable";
import { PaymentPanel } from "../organismos/POSAdmin/PaymentPanel";
import { PedidosPendientesDialog } from "../organismos/POSAdmin/PedidosPendientesDialog";
import { OrdenesVentaPendientesDialog } from "../organismos/POSAdmin/OrdenesVentaPendientesDialog";
import { PresupuestosAceptadosDialog } from "../organismos/POSAdmin/PresupuestosAceptadosDialog";
import { RemisionesPendientesDialog } from "../organismos/POSAdmin/RemisionesPendientesDialog";
import { consolidarItemsRemisiones, redistribuirFifo } from "../../utils/remisionFacturacion";
import { ProductSearch } from "../organismos/POSAdmin/ProductSearch";
import { SolicitudesCreditoPendientesDialog } from "../organismos/POSAdmin/SolicitudesCreditoPendientesDialog";
import { TotalsPanel } from "../organismos/POSAdmin/TotalsPanel";
import { CerrarCajaDialog, parseMonto } from "../organismos/POSDesign/CajaDesign/CerrarCajaDialog";
import { ModalPinSupervisor } from "../organismos/POSDesign/CajaDesign/ModalPinSupervisor";
import { useRubroStore } from "../../store/RubroStore";
import { rubroDelPOS, sucursalIdDeLaCaja } from "../../utils/rubros";

const DEFAULT_CONFIG = {
  layout: "admin",
  show_product_image: false,
  show_product_stock: true,
  enable_discounts: true,
  enable_taxes: true,
  compact_mode: false,
};

// Clave estable para asociar un ítem del carrito con sus aplicaciones de remisión.
const claveRemision = (productoId, descripcion) =>
  `${productoId}||${(descripcion || "").trim().toLowerCase()}`;

export function POSAdminTemplate() {
  const { user } = UserAuth();
  const hasPermission = useAuthStore((s) => s.hasPermission);
  const puedeVerSolicitudesCredito = hasPermission("VENTAS", "VEN_SOL_SOLICITUD_VER");
  const { hasAccess: tienePedidos } = useSubmodulo("VENTAS_PEDIDOS");
  const { hasAccess: tieneOrdenVenta } = useSubmodulo("VENTAS_ORDEN_VENTA");
  const { hasAccess: tieneRendiciones } = useSubmodulo("COB_RENDICIONES");
  // La config de la empresa sale del contexto (getEmpresa), no del store viejo
  // de supabase: `dataempresa` no trae las banderas de configuración.
  const { empresa: empresaConfig } = useEmpresa();
  const { themeStyle } = useThemeStore();

  // Estado de facturación
  const [sucursalActiva, setSucursalActiva] = useState(null);
  const [puntoExpedicionActivo, setPuntoExpedicionActivo] = useState(null);
  const [numeracionActiva, setNumeracionActiva] = useState(null);
  const [fecha, setFecha] = useState(() => {
    const d = new Date();
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, "0");
    const day = String(d.getDate()).padStart(2, "0");
    return `${y}-${m}-${day}`;
  });
  const [monedaActiva, setMonedaActiva] = useState(null);
  const [tipoTransaccionActivo, setTipoTransaccionActivo] = useState(null);
  const [condicionPagoActiva, setCondicionPagoActiva] = useState(null);
  const [indicadorPresenciaActivo, setIndicadorPresenciaActivo] =
    useState(null);

  // Estado de items y cliente
  const [items, setItems] = useState([]);
  const [cliente, setCliente] = useState(null);
  const [direccionFactura, setDireccionFactura] = useState("");
  const [clienteDirecciones, setClienteDirecciones] = useState([]);
  const [clienteDireccionId, setClienteDireccionId] = useState(null);
  const [descuentoGlobal, setDescuentoGlobal] = useState(0);
  const [tipoDescuento, setTipoDescuento] = useState("ninguno");
  const [infoAdicional, setInfoAdicional] = useState("");
  // Campos complementarios comerciales de uso general (gCamGen SIFEN).
  // dOrdCompra: manual. dOrdVta: se autocompleta desde la orden/pedido de venta
  // de origen (editable). dAsiento: número de asiento contable.
  const [ordenCompra, setOrdenCompra] = useState("");
  const [ordenVenta, setOrdenVenta] = useState("");
  const [asientoContable, setAsientoContable] = useState("");
  const [showInfoAdicionalDialog, setShowInfoAdicionalDialog] = useState(false);

  // Estado de cotización
  const [cotizacionActiva, setCotizacionActiva] = useState(null);
  const [showCotizacionAlert, setShowCotizacionAlert] = useState(false);
  const [cotizacionPendiente, setCotizacionPendiente] = useState(null);
  const [cotizacionRequerida, setCotizacionRequerida] = useState(false); // true si moneda != PYG y no hay cotización

  // Estado de pagos y cuotas
  const [pagos, setPagos] = useState([]);
  const [cuotas, setCuotas] = useState([]);
  const [planCuotaSeleccionado, setPlanCuotaSeleccionado] = useState(null);
  const [validacionPago, setValidacionPago] = useState({
    valido: false,
    mensaje: "",
  });

  // Estado para diálogo de confirmación
  const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
  const [isSaving, setIsSaving] = useState(false);

  // Estado para manejo de caja
  const [sesionCaja, setSesionCaja] = useState(null);
  // Rubro con el que el POS filtra el catálogo: el de la sucursal de la caja
  // abierta (ver utils/rubros.js). Sin esto se buscaban y ofrecían productos de
  // otro rubro de la empresa.
  const rubroElegido = useRubroStore((state) => state.rubroId);
  const rubroPOS = rubroDelPOS(sesionCaja, rubroElegido);
  const [cajasDisponibles, setCajasDisponibles] = useState([]);
  const [cajaSeleccionada, setCajaSeleccionada] = useState(null);
  const [montoApertura, setMontoApertura] = useState("");
  const [montoCierre, setMontoCierre] = useState("");
  const [observacionesCierre, setObservacionesCierre] = useState("");
  const [isLoadingCaja, setIsLoadingCaja] = useState(false);
  const [showCerrarCajaDialog, setShowCerrarCajaDialog] = useState(false);

  // Fase 1: estado de autorización supervisor
  const [showPinModal, setShowPinModal] = useState(false);
  const [pinOperacion, setPinOperacion] = useState(null);
  const [tokenAutorizacion, setTokenAutorizacion] = useState(null);
  const [pendingArqueoDetalle, setPendingArqueoDetalle] = useState(null);

  // Descuento por encima del tope: bloquea el guardado hasta que un supervisor
  // autorice, presencial (PIN) o remoto (websocket vía PanelSupervisor). Ver
  // docs/plan-motor-precios-rentabilidad.md §10.
  const [descuentoAuthOpen, setDescuentoAuthOpen] = useState(false);
  const [descuentoBloqueoDetalle, setDescuentoBloqueoDetalle] = useState("");
  const [autorizacionDescuentoId, setAutorizacionDescuentoId] = useState(null);
  const [remoteAuthWaiting, setRemoteAuthWaiting] = useState(false);
  const remoteAuthIdRef = useRef(null);
  // El efecto de sucursal por caja se declara antes que el handler.
  const handleSucursalChangeRef = useRef(null);
  const descuentoSocketRef = useRef(null);

  // Estado para depósito de inventario
  const [depositoActivo, setDepositoActivo] = useState(null);
  const sucursalDepositoRef = useRef(null);

  // Estado para vendedor y cobrador asignados (separados)
  const [vendedorActivo, setVendedorActivo] = useState(null);
  const [cobradorActivo, setCobradorActivo] = useState(null);
  // Cobro en ruta: factura contado cuyo pago lo recauda el cobrador en campo
  const [cobroDiferido, setCobroDiferido] = useState(false);

  // Estado para preview KUDE
  const [kudePreview, setKudePreview] = useState({
    open: false,
    facturaId: null,
  });

  // Estado para cobro de pedidos
  const [showPedidosDialog, setShowPedidosDialog] = useState(false);
  const [pedidoActivo, setPedidoActivo] = useState(null);

  // Estado para solicitudes de crédito
  const [showSolicitudesDialog, setShowSolicitudesDialog] = useState(false);
  // Estado para órdenes de venta
  const [showOrdenesDialog, setShowOrdenesDialog] = useState(false);
  const [showPresupuestosDialog, setShowPresupuestosDialog] = useState(false);
  const [ordenActiva, setOrdenActiva] = useState(null);
  const [searchParams, setSearchParams] = useSearchParams();
  const ordenDeepLinkRef = useRef(false);
  const [presupuestoActivo, setPresupuestoActivo] = useState(null);
  const presupuestoDeepLinkRef = useRef(false);
  const [solicitudActiva, setSolicitudActiva] = useState(null);
  // Estado para facturar desde remisiones (sin documento asociado)
  const [showRemisionesDialog, setShowRemisionesDialog] = useState(false);
  // Remisiones actualmente cargadas en el carrito (para indicador + pre-marcar).
  const [remisionesActivas, setRemisionesActivas] = useState([]);
  // Map clave(`producto_id||descripcion`) -> aplicaciones_remision[]. Se mantiene
  // aparte del ítem porque el POS reconstruye los ítems en recálculos.
  const [aplicacionesRemision, setAplicacionesRemision] = useState({});

  // Queries para cargar datos
  const { data: sucursalesData } = useQuery({
    queryKey: ["sucursales"],
    queryFn: () => getSucursales(),
  });

  const { data: monedasData } = useQuery({
    queryKey: ["empresaMonedas"],
    queryFn: getEmpresaMonedas,
  });

  const { data: condicionesPagoData } = useQuery({
    queryKey: ["condicionesPago"],
    queryFn: getCondicionesPago,
  });

  const { data: mediosPagoData } = useQuery({
    queryKey: ["mediosPagoActivos"],
    queryFn: getMediosPagoActivos,
  });

  // Fase 7: filtra por categoría/precio de los productos del carrito actual —
  // firma en la queryKey para que se recalcule cuando cambian los ítems.
  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 el toggle de precio financiado o por una edición manual.
    precioContado: i.precio_original ?? i.precio_unitario,
  }));
  // Monto del carrito, para que el backend excluya los planes cuyo monto
  // mínimo/máximo no lo admiten (si no, el selector ofrece un plan que después
  // falla al calcular con "El monto total debe ser mayor a X").
  const montoParaPlanes = items.reduce(
    (s, i) => s + (Number(i.precio_unitario) || 0) * (Number(i.cantidad) || 0),
    0,
  );

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

  const {
    data: planesCuotasData,
    isSuccess: planesCuotasResueltas,
    isFetching: planesCuotasCargando,
  } = useQuery({
    queryKey: ["planesCuotasActivos", 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 el parpadeo del
    // bloque de plan automático en cada cambio del carrito).
    placeholderData: keepPreviousData,
    staleTime: 60 * 1000,
  });

  const { data: referencialesData } = useQuery({
    queryKey: ["referenciales"],
    queryFn: getReferenciales,
  });

  // Cargar numeraciones cuando cambia la sucursal (sin filtro de tipo documento)
  const { data: numeracionesData } = useQuery({
    queryKey: ["numeraciones", sucursalActiva?.id],
    queryFn: () => getNumeracionesBySucursal(sucursalActiva?.id),
    enabled: !!sucursalActiva?.id,
  });

  // Cargar depósitos de la sucursal activa; si no hay, cargar todos los de la empresa
  const { data: depositosSucursalData } = useQuery({
    queryKey: ["depositos", sucursalActiva?.id],
    queryFn: () => getDepositos(sucursalActiva?.id),
    enabled: !!sucursalActiva?.id,
  });
  const { data: depositosEmpresaData } = useQuery({
    queryKey: ["depositos-empresa"],
    queryFn: () => getDepositos(),
    enabled:
      !!sucursalActiva?.id &&
      Array.isArray(depositosSucursalData) &&
      depositosSucursalData.length === 0,
  });

  // Cargar depósito principal de la sucursal
  const { data: depositoPrincipal } = useQuery({
    queryKey: ["deposito-principal", sucursalActiva?.id],
    queryFn: () => getDepositoPrincipal(sucursalActiva?.id),
    enabled: !!sucursalActiva?.id,
  });

  // Config del POS (por caja)
  const posConfigCajaId = cajaSeleccionada?.id || sesionCaja?.caja_id;
  const { data: posConfig } = usePosConfigQuery(posConfigCajaId);
  // Memoizado: sin esto, `config` tiene identidad nueva cada render y anula la
  // memoización de los hijos que lo reciben como prop.
  const config = useMemo(() => ({ ...DEFAULT_CONFIG, ...posConfig }), [posConfig]);

  // Extraer datos de las queries
  const sucursales = sucursalesData?.data || sucursalesData || [];
  const monedas = monedasData?.data || monedasData || [];
  const condicionesPago =
    condicionesPagoData?.data || condicionesPagoData || [];
  const mediosPago = mediosPagoData?.data || mediosPagoData || [];
  const planesCuotas = planesCuotasData?.data || [];
  const tiposTransaccion = referencialesData?.tipo_transaccion || [];
  const indicadoresPresencia = referencialesData?.indicador_presencia || [];
  const tarjetas = referencialesData?.tarjeta || [];
  const depositosSucursal = Array.isArray(depositosSucursalData) ? depositosSucursalData : [];
  const depositosEmpresa = Array.isArray(depositosEmpresaData) ? depositosEmpresaData : [];
  const depositos = depositosSucursal.length > 0 ? depositosSucursal : depositosEmpresa;

  // Extraer puntos de expedición de las numeraciones.
  //
  // Se filtran por la caja abierta: el endpoint devuelve las numeraciones de
  // TODA la sucursal, así que sin este filtro al cajero se le ofrecían también
  // los puntos de otras cajas. Facturaba con el punto de otra caja y el
  // movimiento igual caía en la suya, porque el backend numera por la
  // numeración elegida y registra el pago en la sesión abierta, sin cruzarlas.
  //
  // Si ningún punto tiene caja asignada (configuración vieja) no se filtra:
  // dejar la lista vacía impediría facturar.
  const puntosExpedicion = useMemo(() => {
    if (!numeracionesData) return [];
    const puntosMap = new Map();
    (numeracionesData || []).forEach((n) => {
      if (n.punto_expedicion && !puntosMap.has(n.punto_expedicion.id)) {
        puntosMap.set(n.punto_expedicion.id, n.punto_expedicion);
      }
    });
    // Los puntos dados de baja no deben ofrecerse para emitir. El lookup del
    // backend ya los filtra, pero se descartan también acá para no depender de
    // la versión desplegada.
    const todos = Array.from(puntosMap.values()).filter((pe) => pe.active !== false);

    const cajaActivaId = cajaSeleccionada?.id || sesionCaja?.caja_id;
    const algunoTieneCaja = todos.some((pe) => pe.caja_id);
    const visibles =
      cajaActivaId && algunoTieneCaja
        ? todos.filter((pe) => pe.caja_id === cajaActivaId)
        : todos;

    // El punto marcado como default primero: antes se preseleccionaba
    // `puntosExpedicion[0]`, que salía del orden de las NUMERACIONES
    // (`es_default` de la numeración), no del punto. Por eso el POS podía
    // arrancar en un punto que no era el default de la caja.
    return [...visibles].sort((a, b) => (b.es_default ? 1 : 0) - (a.es_default ? 1 : 0));
  }, [numeracionesData, cajaSeleccionada?.id, sesionCaja?.caja_id]);

  // Calcular numeración siguiente
  const numeracionSiguiente = useMemo(() => {
    if (!numeracionActiva) return "";
    const pe = numeracionActiva.punto_expedicion;
    const establecimiento =
      pe?.empresas_sucursales?.punto_establecimiento || pe?.codigo || "001";
    const punto =
      pe?.punto_expedicion || numeracionActiva.codigo_punto || "001";
    const numero = String(numeracionActiva.numero_actual).padStart(7, "0");
    return `${establecimiento}-${punto}-${numero}`;
  }, [numeracionActiva]);

  // Inicializar valores por defecto
  // Sucursal por defecto: la de la caja abierta. Después el usuario puede
  // cambiarla a mano (handleSucursalChange); sólo se vuelve a aplicar si cambia
  // la caja. Antes arrancaba en `user.sucursal_id` o en la primera de la lista,
  // y un usuario sin asignación facturaba en la caja del taller descontando
  // del depósito de otra sucursal.
  const sucursalCajaId = sucursalIdDeLaCaja(sesionCaja);
  const sucursalCajaAplicadaRef = useRef(null);
  useEffect(() => {
    if (sucursales.length === 0) return;
    const deLaCaja = sucursalCajaId ? sucursales.find((s) => s.id === sucursalCajaId) : null;
    if (deLaCaja && sucursalCajaAplicadaRef.current !== deLaCaja.id) {
      sucursalCajaAplicadaRef.current = deLaCaja.id;
      if (sucursalActiva?.id !== deLaCaja.id) handleSucursalChangeRef.current?.(deLaCaja);
      return;
    }
    if (!sucursalActiva) {
      const defaultSuc =
        sucursales.find((s) => s.id === user?.sucursal_id) || sucursales[0];
      setSucursalActiva(defaultSuc);
    }
  }, [sucursales, user?.sucursal_id, sucursalActiva, sucursalCajaId]);

  // Establecer depósito principal por defecto solo cuando cambia la sucursal
  useEffect(() => {
    if (!sucursalActiva?.id) return;
    // Solo resetear si cambia la sucursal
    if (sucursalDepositoRef.current === sucursalActiva.id) return;
    sucursalDepositoRef.current = sucursalActiva.id;
    if (depositoPrincipal) {
      setDepositoActivo(depositoPrincipal);
    } else if (depositos.length > 0) {
      setDepositoActivo(depositos[0]);
    }
  }, [sucursalActiva?.id, depositoPrincipal, depositos]);

  useEffect(() => {
    // Moneda principal por defecto
    if (monedas.length > 0 && !monedaActiva) {
      const defaultMon =
        monedas.find((m) => m.es_principal && m.activo) || monedas[0];
      setMonedaActiva(defaultMon);
    }
  }, [monedas, monedaActiva]);

  // Verificar cotización cuando cambia la moneda
  useEffect(() => {
    const verificarCotizacion = async () => {
      // Si es PYG no necesita cotización
      if (!monedaActiva || monedaActiva?.moneda?.codigo === "PYG") {
        setCotizacionActiva(null);
        setShowCotizacionAlert(false);
        setCotizacionRequerida(false);
        return;
      }

      const monedaId = monedaActiva.moneda_id;
      const hoy = hoyISO();

      try {
        // Obtener última cotización disponible
        const ultimaCotizacion = await getUltimaCotizacion(monedaId);

        if (!ultimaCotizacion) {
          toast.error(
            `No hay cotizaciones cargadas para ${monedaActiva.moneda?.descripcion || monedaActiva.moneda?.codigo}. Debe cargar una cotización para poder facturar.`,
          );
          setCotizacionActiva(null);
          setShowCotizacionAlert(false);
          setCotizacionRequerida(true);
          return;
        }

        // Verificar si la cotización es del día
        const fechaCotizacion = ultimaCotizacion.fecha?.split("T")[0];

        if (fechaCotizacion === hoy) {
          // Cotización del día, usar directamente
          setCotizacionActiva(ultimaCotizacion);
          setShowCotizacionAlert(false);
          setCotizacionRequerida(false);
          toast.success(
            `Cotización del día: ${ultimaCotizacion.cotizacion_compra?.toLocaleString("es-PY")} / ${ultimaCotizacion.cotizacion_venta?.toLocaleString("es-PY")}`,
          );
        } else {
          // Cotización anterior, mostrar alerta para confirmar
          setCotizacionPendiente(ultimaCotizacion);
          setShowCotizacionAlert(true);
          setCotizacionRequerida(true); // Bloqueado hasta que acepte o cargue nueva
        }
      } catch (error) {
        toast.error(
          `No hay cotizaciones cargadas para ${monedaActiva.moneda?.descripcion || monedaActiva.moneda?.codigo}. Debe cargar una cotización para poder facturar.`,
        );
        setCotizacionActiva(null);
        setCotizacionRequerida(true);
      }
    };

    verificarCotizacion();
  }, [monedaActiva]);

  // Handlers para aceptar o rechazar cotización pendiente
  const handleAceptarCotizacion = () => {
    setCotizacionActiva(cotizacionPendiente);
    setShowCotizacionAlert(false);
    setCotizacionRequerida(false);
    toast.success("Cotización aplicada");
  };

  const handleRechazarCotizacion = () => {
    setShowCotizacionAlert(false);
    setCotizacionPendiente(null);
    toast.info("Debe cargar una cotización del día para continuar");
  };

  useEffect(() => {
    // Condición de pago por defecto (Contado)
    if (condicionesPago.length > 0 && !condicionPagoActiva) {
      const defaultCond =
        condicionesPago.find((c) =>
          c.nombre?.toLowerCase().includes("contado"),
        ) || condicionesPago[0];
      setCondicionPagoActiva(defaultCond);
    }
  }, [condicionesPago, condicionPagoActiva]);

  // Al cambiar condición de pago manualmente, resetear pagos y cuotas
  // para evitar que queden pagos de contado en facturas a crédito (o viceversa)
  const handleCondicionPagoChange = (nuevaCond) => {
    setCondicionPagoActiva(nuevaCond);
    setPagos([]);
    setCuotas([]);
    setValidacionPago({ valido: false, mensaje: "" });
  };

  useEffect(() => {
    // Tipo transacción por defecto (Venta de mercadería = 1)
    if (tiposTransaccion.length > 0 && !tipoTransaccionActivo) {
      const defaultTT =
        tiposTransaccion.find((t) => t.id === 1) || tiposTransaccion[0];
      setTipoTransaccionActivo(defaultTT);
    }
  }, [tiposTransaccion, tipoTransaccionActivo]);

  useEffect(() => {
    // Indicador presencia por defecto (Operación presencial = 1)
    if (indicadoresPresencia.length > 0 && !indicadorPresenciaActivo) {
      const defaultIP =
        indicadoresPresencia.find((i) => i.id === 1) || indicadoresPresencia[0];
      setIndicadorPresenciaActivo(defaultIP);
    }
  }, [indicadoresPresencia, indicadorPresenciaActivo]);

  useEffect(() => {
    // Punto de expedición por defecto: el marcado como default de la caja, y
    // solo si no lo hay se cae al primero de la lista ya filtrada por caja.
    if (puntosExpedicion.length > 0) {
      const preferido =
        puntosExpedicion.find((pe) => pe.es_default) || puntosExpedicion[0];
      setPuntoExpedicionActivo(preferido);
    } else {
      setPuntoExpedicionActivo(null);
    }
  }, [puntosExpedicion]);

  useEffect(() => {
    // Numeración por defecto cuando cambia el punto de expedición
    // Filtrar por tipo de documento "Factura electrónica" (codigo 1)
    if (numeracionesData && puntoExpedicionActivo) {
      const numPE = numeracionesData.find(
        (n) =>
          n.punto_expedicion_id === puntoExpedicionActivo.id &&
          n.tipo_documento?.codigo === 1,
      );
      setNumeracionActiva(numPE || null);
    }
  }, [numeracionesData, puntoExpedicionActivo]);

  // Cargar sesión de caja activa y cajas disponibles
  useEffect(() => {
    const cargarDatosCaja = async () => {
      try {
        // Ambas requests son independientes → en paralelo (antes eran 2 round-trips en serie).
        const [cajas, sesion] = await Promise.all([getCajasDisponibles(), getSesionActiva()]);
        setCajasDisponibles(cajas || []);

        if (sesion) {
          setSesionCaja(sesion);
          const cajaActiva = (cajas || []).find((c) => c.id === sesion.caja_id);
          if (cajaActiva) {
            setCajaSeleccionada(cajaActiva);
          }
        }
      } catch (error) {
        console.error("Error cargando datos de caja:", error);
      }
    };
    cargarDatosCaja();
  }, []);

  // Funciones para manejo de caja
  const handleAbrirCaja = async (tokenOrEvent) => {
    if (!cajaSeleccionada) {
      toast.error("Seleccione una caja");
      return;
    }
    const token = typeof tokenOrEvent === "string" ? tokenOrEvent : null;
    const authToken = token || tokenAutorizacion;
    // Verificar si requiere autorización
    if (posConfig?.requiere_autorizacion_apertura && !authToken) {
      setPinOperacion("apertura_caja");
      setShowPinModal(true);
      return;
    }
    const monto = parseInt(montoApertura.replace(/\D/g, "") || "0");

    setIsLoadingCaja(true);
    try {
      const response = await abrirCaja({
        caja_id: cajaSeleccionada.id,
        monto_apertura: monto,
        sucursal_id: sucursalActiva?.id,
        token_autorizacion: authToken || undefined,
      });
      // abrirCaja devuelve { message, data: sesion } — extraer la sesión real
      const sesion = response?.data || response;
      setSesionCaja(sesion);
      setMontoApertura("");
      setTokenAutorizacion(null);
      toast.success("Caja abierta exitosamente");
    } catch (error) {
      console.error("Error abriendo caja:", error);
      toast.error(error.response?.data?.message || "Error al abrir la caja");
      setTokenAutorizacion(null);
    } finally {
      setIsLoadingCaja(false);
    }
  };

  const handleCerrarCaja = async (tokenOrEvent, arqueoDetalle) => {
    if (!sesionCaja || !sesionCaja.id) {
      toast.error("No hay caja abierta o sesión inválida. Recargue la página.");
      return;
    }
    // Guardar arqueoDetalle si viene (para recuperarlo tras PIN)
    if (arqueoDetalle) {
      setPendingArqueoDetalle(arqueoDetalle);
    }
    const token = typeof tokenOrEvent === "string" ? tokenOrEvent : null;
    const authToken = token || tokenAutorizacion;
    // Verificar si requiere autorización
    if (posConfig?.requiere_autorizacion_cierre && !authToken) {
      setPinOperacion("cierre_caja");
      setShowPinModal(true);
      return;
    }
    // Usar arqueoDetalle del argumento o el pendiente
    const detalle = arqueoDetalle || pendingArqueoDetalle;
    // parseMonto y no parseInt(replace(/\D/g)): ese replace borra el separador
    // decimal, así que "2.492.687,3" se habría enviado como 24.926.873.
    const monto = parseMonto(montoCierre);

    setIsLoadingCaja(true);
    try {
      await cerrarCaja(sesionCaja.id, {
        monto_cierre: monto,
        observaciones: observacionesCierre || null,
        token_autorizacion: authToken || undefined,
        arqueo_detalle: detalle || undefined,
      });
      setSesionCaja(null);
      setCajaSeleccionada(null);
      setShowCerrarCajaDialog(false);
      setMontoCierre("");
      setObservacionesCierre("");
      setTokenAutorizacion(null);
      setPendingArqueoDetalle(null);
      toast.success("Caja cerrada exitosamente");
      const cajas = await getCajasDisponibles();
      setCajasDisponibles(cajas || []);
    } catch (error) {
      console.error("Error cerrando caja:", error);
      toast.error(error.response?.data?.message || "Error al cerrar la caja");
      setTokenAutorizacion(null);
    } finally {
      setIsLoadingCaja(false);
    }
  };

  const handlePinSuccess = useCallback(
    (data) => {
      setTokenAutorizacion(data.token);
      setShowPinModal(false);
      // Re-ejecutar la operación con el token
      if (pinOperacion === "apertura_caja") {
        setTimeout(() => handleAbrirCaja(data.token), 100);
      } else if (pinOperacion === "cierre_caja") {
        setTimeout(() => handleCerrarCaja(data.token), 100);
      } else if (pinOperacion === "descuento") {
        // PIN presencial ya aprueba+registra la autorización de una: reintentar guardado.
        // Se pasa el id explícito (no el estado) porque setAutorizacionDescuentoId
        // recién se refleja en el próximo render — handleConfirmSave corre 100ms
        // después pero con el mismo closure viejo si se confía solo en el estado.
        setAutorizacionDescuentoId(data.autorizacion_id);
        setDescuentoAuthOpen(false);
        setTimeout(() => handleConfirmSave(data.autorizacion_id), 100);
      }
    },
    [pinOperacion],
  );

  // Cierra y limpia el socket de espera de autorización remota.
  const cerrarSocketDescuento = useCallback(() => {
    descuentoSocketRef.current?.disconnect();
    descuentoSocketRef.current = null;
    remoteAuthIdRef.current = null;
  }, []);

  useEffect(() => () => cerrarSocketDescuento(), [cerrarSocketDescuento]);

  // Vía remota: pide autorización a un supervisor conectado a PanelSupervisor
  // (aprueba con su propio PIN desde cualquier lugar, sin estar en el salón de
  // ventas). Reutiliza /autorizaciones-caja + el gateway /pos, que ya emite
  // autorizacion:aprobada a toda la empresa vía WebSocket.
  const handleSolicitarAutorizacionRemota = async () => {
    const authSessionToken = getAccessToken();
    if (!authSessionToken) return;
    setRemoteAuthWaiting(true);
    try {
      const resultado = await solicitarAutorizacion({
        tipo: "descuento",
        sesion_caja_id: sesionCaja?.id || undefined,
        motivo: descuentoBloqueoDetalle,
        contexto: { detalle: descuentoBloqueoDetalle },
      });
      const autorizacionId = resultado?.data?.id;
      if (!autorizacionId) throw new Error("No se pudo crear la solicitud de autorización");
      remoteAuthIdRef.current = autorizacionId;

      const baseUrl = import.meta.env.VITE_API_BASE_URL || window.location.origin;
      const wsUrl = baseUrl.replace("/api/v1", "").replace(/\/$/, "");
      const socket = io(`${wsUrl}/pos`, {
        auth: { token: authSessionToken },
        transports: ["websocket", "polling"],
      });
      descuentoSocketRef.current = socket;

      socket.on("autorizacion:aprobada", (data) => {
        if (data?.autorizacion_id !== remoteAuthIdRef.current) return;
        toast.success(`Descuento autorizado por ${data.supervisor?.username || "supervisor"}`);
        setAutorizacionDescuentoId(data.autorizacion_id);
        setRemoteAuthWaiting(false);
        setDescuentoAuthOpen(false);
        cerrarSocketDescuento();
        setTimeout(() => handleConfirmSave(data.autorizacion_id), 100);
      });
      socket.on("autorizacion:rechazada", (data) => {
        if (data?.autorizacion_id !== remoteAuthIdRef.current) return;
        toast.error(`Autorización rechazada${data.motivo_rechazo ? `: ${data.motivo_rechazo}` : ""}`);
        setRemoteAuthWaiting(false);
        cerrarSocketDescuento();
      });

      toast.info("Solicitud enviada al supervisor. Esperando aprobación...");
    } catch (error) {
      console.error("Error solicitando autorización remota:", error);
      toast.error(error.response?.data?.message || "No se pudo solicitar autorización remota");
      setRemoteAuthWaiting(false);
    }
  };

  // Handler para cambio de sucursal
  const handleSucursalChange = (suc) => {
    setSucursalActiva(suc);
    setPuntoExpedicionActivo(null);
    setNumeracionActiva(null);
  };
  handleSucursalChangeRef.current = handleSucursalChange;

  // Handler para cambio de punto de expedición
  const handlePuntoExpedicionChange = (pe) => {
    setPuntoExpedicionActivo(pe);
    if (numeracionesData && pe) {
      const numPE = numeracionesData.find(
        (n) =>
          n.punto_expedicion_id === pe.id && n.tipo_documento?.codigo === 1,
      );
      setNumeracionActiva(numPE || null);
    }
  };

  // Remisiones que efectivamente se van a facturar según el carrito actual
  // (derivado, no estático): si borrás/editás ítems, el indicador se actualiza.
  const remisionesEnCarrito = useMemo(() => {
    if (remisionesActivas.length === 0) return [];
    const ids = new Set();
    for (const item of items) {
      const aps = aplicacionesRemision[claveRemision(item.producto_id, item.descripcion)];
      if (!aps?.length) continue;
      for (const a of redistribuirFifo(aps, item.cantidad)) {
        if (a.cantidad > 0 && a.remision_id) ids.add(a.remision_id);
      }
    }
    return remisionesActivas.filter((r) => ids.has(r.id));
  }, [items, aplicacionesRemision, remisionesActivas]);

  // Calcular totales usando las funciones de invoiceCalculations
  const totales = useMemo(() => {
    const subtotalesCalculados = calcularSubtotales(items);

    // Aplicar descuento global si existe
    let descuentoGlobalTotal = 0;
    if (tipoDescuento === "porcentaje") {
      descuentoGlobalTotal =
        subtotalesCalculados.total_neto_operacion * (descuentoGlobal / 100);
    } else if (tipoDescuento === "monto") {
      descuentoGlobalTotal = descuentoGlobal;
    }

    // Redondeo a los decimales de la moneda: con cantidades fraccionadas
    // (modo estación: 11,1235 L x 8.990) el total quedaba en 100.000,265, y en
    // guaraníes no existen los centavos. Para cantidades enteras es un no-op.
    const decimalesMonedaFactura =
      (monedaActiva?.moneda?.codigo || "PYG") === "PYG"
        ? 0
        : monedaActiva?.moneda?.decimales_estandar ?? 2;
    const factorRedondeo = 10 ** decimalesMonedaFactura;
    const totalFinal =
      Math.round(
        (subtotalesCalculados.total_neto_operacion - descuentoGlobalTotal) * factorRedondeo,
      ) / factorRedondeo;

    // Retornar con los mismos nombres que factura_form.js calculateTotals
    return {
      ...subtotalesCalculados,
      // Campos adicionales para compatibilidad
      total_descuento_global: descuentoGlobalTotal,
      total: totalFinal,
    };
  }, [items, descuentoGlobal, tipoDescuento, monedaActiva]);

  const handleAddProduct = (producto) => {
    console.log("producto", producto);

    // Modo estación: el monto se carga en la misma fila del buscador
    // (ProductSearch), que ya manda la cantidad calculada. Antes había acá un
    // diálogo que nunca llegaba a abrirse porque la fila siempre mandaba cantidad.

    // Validar stock según configuración
    if (
      config.permitir_venta_sin_stock === false &&
      producto.maneja_inventario &&
      producto.stock_disponible !== undefined &&
      producto.stock_disponible <= 0
    ) {
      toast.error(
        "Sin stock disponible. La venta sin stock está deshabilitada.",
      );
      return;
    }
    if (
      producto.maneja_inventario &&
      producto.stock_disponible !== undefined &&
      producto.stock_disponible <= 0
    ) {
      toast.warning("Producto sin stock. Se generará stock negativo.");
    }

    // Obtener afectaciones IVA de referenciales
    const afectacionesIva = referencialesData?.afectacion_iva || [];

    // Opción C: ProductSearch ya envía precioBase como precio_venta y descuento combinado
    const datosEdicion = {
      cantidad: producto.cantidad || 1,
      precio: producto.precio_venta || 0,
      descuento: producto.descuento || 0,
      tipoDescuento: producto.tipoDescuento || "porcentaje",
      descripcion: producto.descripcion,
      unidad_medida: producto.unidades_de_medida?.representacion || "UNI",
    };

    const nuevoItem = construirItemDetalle(
      producto,
      datosEdicion,
      cliente,
      afectacionesIva,
      {
        cotizacion: cotizacionActiva?.cotizacion_venta || null,
        codigoMoneda: monedaActiva?.moneda?.codigo || "PYG",
        precioListaInfo: { topeDescuentoPct: producto.tope_descuento_pct },
      },
    );

    // Adjuntar metadata de oferta al item
    if (producto._oferta) {
      nuevoItem._oferta = producto._oferta;
    }

    // Verificar si ya existe el producto (mismo id y mismos parámetros)
    const existingIndex = items.findIndex(
      (i) =>
        i.producto_id === nuevoItem.producto_id &&
        i.precio_unitario === nuevoItem.precio_unitario &&
        i.porcentaje_descuento_item === nuevoItem.porcentaje_descuento_item,
    );

    if (existingIndex >= 0) {
      // Actualizar cantidad del item existente y recalcular
      const newItems = [...items];
      const itemExistente = newItems[existingIndex];
      const nuevaCantidad = itemExistente.cantidad + nuevoItem.cantidad;

      // Recalcular con nueva cantidad
      const datosActualizados = {
        ...datosEdicion,
        cantidad: nuevaCantidad,
        precio: itemExistente.precio_unitario,
        descuento: itemExistente.porcentaje_descuento_item,
        tipoDescuento: "porcentaje",
      };

      newItems[existingIndex] = construirItemDetalle(
        { ...producto, id: itemExistente.producto_id },
        datosActualizados,
        cliente,
        afectacionesIva,
        {
          cotizacion: cotizacionActiva?.cotizacion_venta || null,
          codigoMoneda: monedaActiva?.moneda?.codigo || "PYG",
          precioListaInfo: { topeDescuentoPct: producto.tope_descuento_pct },
        },
      );
      newItems[existingIndex].id = itemExistente.id;
      // Preservar metadata de oferta
      if (itemExistente._oferta) {
        newItems[existingIndex]._oferta = itemExistente._oferta;
      }

      setItems(newItems);
    } else {
      setItems(prev => [...prev, nuevoItem]);
    }
    console.log("nuevoItem", nuevoItem);
  };

  const handleUpdateItem = (index, field, value) => {
    let valorFinal = value;

    // Los ítems del POS guardan la unidad en `cod_unidad_medida` (lo que arma
    // construirItemDetalle y se factura); la grilla la edita como `unidad_medida`.
    if (field === "unidad_medida") field = "cod_unidad_medida";

    // Modo estación de servicio: se carga el MONTO y se deriva la cantidad.
    // Se traduce a un cambio de `cantidad` para caer en el recálculo de siempre
    // (construirItemDetalle), en vez de duplicar la lógica de IVA y totales.
    if (field === "monto") {
      const actual = items[index];
      if (!permiteMontoEditable(actual, config)) return;
      const derivada = cantidadDesdeMonto(
        valorFinal,
        precioNetoItem(actual),
        decimalesCantidad(config),
      );
      if (derivada === null) {
        toast.warning("El ítem no tiene precio unitario: no se puede derivar la cantidad desde el monto.");
        return;
      }
      valorFinal = derivada;
      field = "cantidad";
    }

    // Ítems de remisión: no permitir facturar más de lo remisionado disponible.
    if (field === "cantidad") {
      const actual = items[index];
      const aps = actual && aplicacionesRemision[claveRemision(actual.producto_id, actual.descripcion)];
      if (aps?.length) {
        const maxDisp = aps.reduce((a, x) => a + Number(x.cantidad || 0), 0);
        if (Number(valorFinal) > maxDisp) {
          valorFinal = maxDisp;
          toast.warning(`Máximo facturable desde remisión: ${maxDisp} (${actual.descripcion})`);
        }
      }
    }

    const newItems = [...items];
    const item = { ...newItems[index], [field]: valorFinal };

    // Cambiar la tasa desde el select de la grilla es cambiar la afectación: 0%
    // es Exento (3) y 5/10% es Gravado (1). Sin esto el ítem quedaba con la tasa
    // nueva pero con la afectación y la liquidación de IVA viejas.
    if (field === "porcentaje_iva") {
      const gravado = Number(valorFinal) > 0;
      item.codigo_afectacion_iva = gravado
        ? AFECTACION_IVA.GRAVADO_TOTAL
        : AFECTACION_IVA.EXENTO;
      item.descripcion_afectacion_iva = gravado ? "Gravado IVA" : "Exento";
      item.proporcion_iva = gravado ? 100 : 0;
    }

    // Si cambia cantidad, precio o afectación, recalcular todos los campos derivados
    if (
      field === "cantidad" ||
      field === "precio_unitario" ||
      field === "porcentaje_iva"
    ) {
      const afectacionesIva = referencialesData?.afectacion_iva || [];
      const recalculado = construirItemDetalle(
        {
          id: item.producto_id,
          cod_producto: item.cod_producto,
          codigo_barra: item.codigo_referencial,
          descripcion: item.descripcion,
          codigo_afectacion_iva: item.codigo_afectacion_iva,
          porcentaje_iva: item.porcentaje_iva,
          proporcion_iva: item.proporcion_iva,
          descripcion_afectacion_iva: item.descripcion_afectacion_iva,
        },
        {
          cantidad: field === "cantidad" ? valorFinal : item.cantidad,
          precio: field === "precio_unitario" ? valorFinal : item.precio_unitario,
          descuento: item.descuento_particular_precio_unitario_item ?? 0,
          tipoDescuento: "monto",
          descripcion: item.descripcion,
          unidad_medida: item.cod_unidad_medida,
        },
        cliente,
        afectacionesIva,
        {
          cotizacion: cotizacionActiva?.cotizacion_venta || null,
          codigoMoneda: monedaActiva?.moneda?.codigo || "PYG",
          precioListaInfo: { topeDescuentoPct: item.tope_descuento_pct },
        },
      );
      // Preservar id, metadata de oferta y precio de catálogo (contado) previo a
      // cualquier precio financiado — si no, itemsParaPlanes (línea ~254) volvería a
      // mandar el precio financiado como precioContado al filtro de planes por rango.
      recalculado.id = item.id;
      if (item._oferta) recalculado._oferta = item._oferta;
      if (item.precio_original !== undefined) recalculado.precio_original = item.precio_original;
      if (item.precio_financiado_activo !== undefined) recalculado.precio_financiado_activo = item.precio_financiado_activo;
      newItems[index] = recalculado;
    } else {
      newItems[index] = item;
    }

    setItems(newItems);
  };

  const handleRemoveItem = (index) => {
    setItems(items.filter((_, i) => i !== index));
  };

  const handlePrecioFinanciadoToggle = (
    productoId,
    precioFinanciado,
    activo,
  ) => {
    const afectacionesIva = referencialesData?.afectacion_iva || [];
    const newItems = items.map((item) => {
      if (item.producto_id !== productoId) return item;

      // Guardar precio original la primera vez
      const precioOriginal = item.precio_original ?? item.precio_unitario;
      const nuevoPrecio = activo ? precioFinanciado : precioOriginal;

      // Reconstruir item con nuevo precio
      const datosEdicion = {
        cantidad: item.cantidad,
        precio: nuevoPrecio,
        descuento: item.porcentaje_descuento_item || 0,
        tipoDescuento: "porcentaje",
        descripcion: item.descripcion,
        unidad_medida: item.cod_unidad_medida,
      };

      const itemRecalculado = construirItemDetalle(
        {
          id: item.producto_id,
          codigo: item.cod_producto,
          codigo_barra: item.codigo_referencial,
          descripcion: item.descripcion,
          unidad_medida: item.cod_unidad_medida,
          codigo_afectacion_iva: item.codigo_afectacion_iva,
          porcentaje_iva: item.porcentaje_iva,
          proporcion_iva: item.proporcion_iva,
          descripcion_afectacion_iva: item.descripcion_afectacion_iva,
        },
        datosEdicion,
        cliente,
        afectacionesIva,
        {
          cotizacion: cotizacionActiva?.cotizacion_venta || null,
          codigoMoneda: monedaActiva?.moneda?.codigo || "PYG",
          precioListaInfo: { topeDescuentoPct: item.tope_descuento_pct },
        },
      );

      return {
        ...itemRecalculado,
        id: item.id,
        precio_original: precioOriginal,
        precio_financiado_activo: activo,
      };
    });

    setItems(newItems);
  };

  const handleClienteChange = (c) => {
    setCliente(c);
    setDireccionFactura(c?.personas?.direccion || c?.direccion || "");
    if (c?.cobrador_id && c?.cobrador) {
      setCobradorActivo({ id: c.cobrador_id, ...c.cobrador });
    }
  };

  useEffect(() => {
    if (!cliente?.id) {
      setClienteDirecciones([]);
      setClienteDireccionId(null);
      return;
    }
    getDirecciones(cliente.id).then((dirs) => {
      setClienteDirecciones(dirs || []);
      const principal = (dirs || []).find((d) => d.es_principal) || dirs?.[0];
      if (principal) {
        setClienteDireccionId(principal.id);
        if (principal.direccion) setDireccionFactura(principal.direccion);
      } else {
        setClienteDireccionId(null);
      }
    }).catch(() => {});
  }, [cliente?.id]);

  const handleClearAll = () => {
    setItems([]);
    setAutorizacionDescuentoId(null);
    setCliente(null);
    setDireccionFactura("");
    setDescuentoGlobal(0);
    setTipoDescuento("ninguno");
    setPagos([]);
    setCuotas([]);
    setValidacionPago({ valido: false, mensaje: "" });
    setPedidoActivo(null);
    setOrdenActiva(null);
    setPresupuestoActivo(null);
    setSolicitudActiva(null);
    setRemisionesActivas([]);
    setAplicacionesRemision({});
    setClienteDirecciones([]);
    setClienteDireccionId(null);
    setInfoAdicional("");
    setOrdenCompra("");
    setOrdenVenta("");
    setAsientoContable("");
    setCobroDiferido(false);
    // Restaurar condición de pago al default (contado)
    if (condicionesPago.length > 0) {
      const defaultCond =
        condicionesPago.find((c) =>
          c.nombre?.toLowerCase().includes("contado"),
        ) || condicionesPago[0];
      setCondicionPagoActiva(defaultCond);
    }
  };

  // ═══════════ COBRO DE PEDIDOS MAYORISTAS ═══════════

  const handleSelectPedido = async (pedidoBase) => {
    try {
      // 0. Obtener pedido completo con detalle desde el backend
      const pedidoCompleto = await getPedido(pedidoBase.id);
      const pedido = { ...pedidoBase, ...pedidoCompleto };

      // 1. Limpiar formulario actual
      setItems([]);
      setPagos([]);
      setCuotas([]);
      setDescuentoGlobal(0);
      setTipoDescuento("ninguno");

      // 3. Cargar cliente del pedido
      const clientePedido = pedido.cliente;
      if (clientePedido) {
        const c = {
          id: clientePedido.id,
          personas: clientePedido.personas || {
            razon_social: clientePedido.nombre_fantasia,
            ruc: clientePedido.personas?.ruc,
          },
          ...clientePedido,
        };
        setCliente(c);
        setDireccionFactura(c.personas?.direccion || c.direccion || "");
      }

      // 4. Setear moneda del pedido si existe
      if (pedido.moneda_id && monedas.length > 0) {
        const mon = monedas.find(
          (m) =>
            m.moneda_id === pedido.moneda_id ||
            m.moneda?.id === pedido.moneda_id,
        );
        if (mon) setMonedaActiva(mon);
      }

      // 5. Setear condición de pago si existe
      if (pedido.condicion_operacion_id && condicionesPago.length > 0) {
        const cond = condicionesPago.find(
          (c) =>
            c.condicion_operacion_id === pedido.condicion_operacion_id ||
            c.cod_condicion_venta === (pedido.condicion_operacion?.codigo || 0),
        );
        if (cond) setCondicionPagoActiva(cond);
      }

      // 6. Setear depósito si viene en el pedido
      if (pedido.deposito_id && depositos.length > 0) {
        const dep = depositos.find((d) => d.id === pedido.deposito_id);
        if (dep) setDepositoActivo(dep);
      }

      // 6b. Setear vendedor del pedido si existe
      if (pedido.vendedor_id) {
        setVendedorActivo({ id: pedido.vendedor_id, ...pedido.vendedor });
      }

      // 7. Mapear items del pedido a items de factura (Opción C)
      const afectacionesIva = referencialesData?.afectacion_iva || [];
      const detalles = pedido.detalle || [];
      // Determinar moneda del pedido para construir items correctamente
      const monPedido = monedas.find(
        (m) =>
          m.moneda_id === pedido.moneda_id || m.moneda?.id === pedido.moneda_id,
      );
      const codigoMonedaPedido =
        monPedido?.moneda?.codigo || pedido.moneda?.codigo || "PYG";

      const itemsFactura = detalles.map((det) => {
        // Fuente de verdad comercial: precio negociado y descuento explícito.
        // Se evita inferir descuentos implícitos desde precio_lista para no
        // distorsionar montos en monedas no base (ej. USD).
        const precioUnitario = Number(det.precio_negociado ?? det.precio_lista ?? 0) || 0;
        const descuentoTotal = Math.round((Number(det.descuento_porcentaje ?? 0) || 0) * 100) / 100;

        const datosEdicion = {
          cantidad: Number(det.cantidad) || 1,
          precio: precioUnitario,
          descuento: descuentoTotal,
          tipoDescuento: "porcentaje",
          descripcion: det.descripcion || det.producto?.descripcion || "",
          unidad_medida: det.unidad_medida || "UNI",
        };

        const producto = {
          id: det.producto_id,
          codigo: det.producto?.codigo || det.producto?.cod_producto || "",
          codigo_barra: det.producto?.codigo_barra || "",
          descripcion: det.descripcion || det.producto?.descripcion || "",
          ...afectacionDesdeTasa(det.porcentaje_iva, det.proporcion_iva),
          unidad_medida: det.unidad_medida || "UNI",
        };

        const item = construirItemDetalle(
          producto,
          datosEdicion,
          clientePedido,
          afectacionesIva,
          {
            cotizacion: cotizacionActiva?.cotizacion_venta || null,
            codigoMoneda: codigoMonedaPedido,
          },
        );

        // Preservar referencia al detalle del pedido
        item._pedido_detalle_id = det.id;

        return item;
      });

      setItems(itemsFactura);

      // 8. Aplicar descuento global del pedido si existe
      if (Number(pedido.descuento_global_porcentaje) > 0) {
        setDescuentoGlobal(Number(pedido.descuento_global_porcentaje));
        setTipoDescuento("porcentaje");
      }

      // 9. Guardar referencia al pedido activo
      setPedidoActivo(pedido);

      // Autocompletar Nº orden de venta (gCamGen dOrdVta) desde el pedido de origen,
      // sin pisar un valor cargado manualmente. SIFEN limita a 15 caracteres.
      if (pedido.numero_pedido) {
        setOrdenVenta((prev) => prev || String(pedido.numero_pedido).slice(0, 15));
      }

      toast.success(
        `Pedido ${pedido.numero_pedido} cargado — ${detalles.length} items`,
      );
    } catch (err) {
      console.error("Error cargando pedido:", err);
      toast.error(
        err?.response?.data?.message || "Error al cargar pedido en caja",
      );
    }
  };

  // ═══════════ FACTURAR DESDE REMISIONES (sin documento asociado) ═══════════

  const handleSelectRemisiones = (remisiones) => {
    try {
      if (!remisiones?.length) return;
      const afectacionesIva = referencialesData?.afectacion_iva || [];
      // Consolidar por producto + descripción (una línea, cantidad sumada), con
      // el desglose de origen (aplicaciones_remision) para facturación parcial.
      const consolidadas = consolidarItemsRemisiones(remisiones);
      if (consolidadas.length === 0) {
        toast.warning("Las remisiones seleccionadas no tienen ítems disponibles para facturar");
        return;
      }

      const mapAplicaciones = {};
      const itemsFactura = consolidadas.map((linea) => {
        // cunimed (factura_det) es String; asegurar el tipo.
        const unidad = linea.unidad_medida != null ? String(linea.unidad_medida) : "UNI";
        const datosEdicion = {
          cantidad: linea.cantidad,
          precio: linea.precio,
          descuento: 0,
          tipoDescuento: "porcentaje",
          descripcion: linea.descripcion,
          unidad_medida: unidad,
        };
        const producto = {
          id: linea.producto_id,
          codigo: linea.cod_producto || "",
          descripcion: linea.descripcion,
          ...afectacionDesdeTasa(linea.porcentaje_iva),
          unidad_medida: unidad,
        };
        const item = construirItemDetalle(producto, datosEdicion, cliente, afectacionesIva, {
          cotizacion: cotizacionActiva?.cotizacion_venta || null,
          codigoMoneda: monedaActiva?.moneda?.codigo || "PYG",
        });
        // Guardar las aplicaciones en el map (clave estable, no en el ítem).
        mapAplicaciones[claveRemision(linea.producto_id, linea.descripcion)] = linea.aplicaciones_remision;
        return item;
      });

      setAplicacionesRemision(mapAplicaciones);
      setRemisionesActivas(remisiones);
      setItems(itemsFactura);
      const numeros = remisiones.map((r) => r.numero).filter(Boolean);
      toast.success(
        `Cargada(s): ${numeros.join(", ") || `${remisiones.length} remisión(es)`} — ${itemsFactura.length} ítem(s)`,
      );
    } catch (err) {
      console.error("Error cargando remisiones:", err);
      toast.error("Error al cargar remisiones en caja");
    }
  };

  // ═══════════ CARGA DE ORDEN DE VENTA ═══════════

  const handleSelectOrden = async (ordenBase) => {
    try {
      const ordenCompleta = await getOrdenVenta(ordenBase.id);
      const orden = { ...ordenBase, ...ordenCompleta };

      setItems([]);
      setPagos([]);
      setCuotas([]);
      setDescuentoGlobal(0);
      setTipoDescuento("ninguno");

      const clienteOrden = orden.cliente;
      if (clienteOrden) {
        const c = {
          id: clienteOrden.id,
          personas: clienteOrden.personas || { razon_social: clienteOrden.nombre_fantasia },
          ...clienteOrden,
        };
        setCliente(c);
        setDireccionFactura(c.personas?.direccion || c.direccion || "");
      }

      if (orden.moneda_id && monedas.length > 0) {
        const mon = monedas.find((m) => m.moneda_id === orden.moneda_id || m.moneda?.id === orden.moneda_id);
        if (mon) setMonedaActiva(mon);
      }

      if (orden.condicion_operacion_id && condicionesPago.length > 0) {
        const cond = condicionesPago.find(
          (c) => c.condicion_operacion_id === orden.condicion_operacion_id || c.cod_condicion_venta === (orden.condicion_operacion?.codigo || 0),
        );
        if (cond) setCondicionPagoActiva(cond);
      }

      if (orden.deposito_id && depositos.length > 0) {
        const dep = depositos.find((d) => d.id === orden.deposito_id);
        if (dep) setDepositoActivo(dep);
      }

      if (orden.vendedor_id) {
        setVendedorActivo({ id: orden.vendedor_id, ...orden.vendedor });
      }

      const afectacionesIva = referencialesData?.afectacion_iva || [];
      const detalles = orden.detalle || [];
      const monOrden = monedas.find((m) => m.moneda_id === orden.moneda_id || m.moneda?.id === orden.moneda_id);
      const codigoMonedaOrden = monOrden?.moneda?.codigo || orden.moneda?.codigo || "PYG";

      // Obtener cantidades ya facturadas para evitar facturar de más
      let pendienteMap = {};
      try {
        const resFacturado = await getOrdenVentaItemsFacturados(orden.id);
        for (const item of resFacturado.items || []) {
          pendienteMap[item.pedido_detalle_id] = item.cantidad_pendiente;
        }
      } catch (_) {
        // Si falla esta consulta, se usa la cantidad completa del detalle.
      }

      const itemsFactura = detalles
        .filter((det) => {
          const pendiente = pendienteMap[det.id] ?? Number(det.cantidad ?? 1);
          return pendiente > 0; // excluir ítems completamente facturados
        })
        .map((det) => {
        // Fuente de verdad comercial: precio negociado y descuento explícito.
        // No inferir descuento implícito por diferencia contra precio_lista.
        const precioUnitario = Number(det.precio_negociado ?? det.precio_lista ?? 0) || 0;
        const descuentoTotal = Math.round((Number(det.descuento_porcentaje ?? 0) || 0) * 100) / 100;

        const cantPendiente = pendienteMap[det.id] ?? Number(det.cantidad) ?? 1;
        const datosEdicion = {
          cantidad: cantPendiente || 1,
          precio: precioUnitario,
          descuento: descuentoTotal,
          tipoDescuento: "porcentaje",
          descripcion: det.descripcion || det.producto?.descripcion || "",
          unidad_medida: det.unidad_medida || "UNI",
        };

        const producto = {
          id: det.producto_id,
          codigo: det.producto?.codigo || det.producto?.cod_producto || "",
          codigo_barra: det.producto?.codigo_barra || "",
          descripcion: det.descripcion || det.producto?.descripcion || "",
          ...afectacionDesdeTasa(det.porcentaje_iva, det.proporcion_iva),
          unidad_medida: det.unidad_medida || "UNI",
        };

        const item = construirItemDetalle(producto, datosEdicion, clienteOrden, afectacionesIva, {
          cotizacion: cotizacionActiva?.cotizacion_venta || null,
          codigoMoneda: codigoMonedaOrden,
        });

        item._orden_venta_id = orden.id;
        item._orden_detalle_id = det.id;

        return item;
      });

      setItems(itemsFactura);

      // El descuento global lo trae la propia orden, y es la única fuente: no hay que
      // recuperarlo del presupuesto de origen (si no, se contaría doble). En las OV
      // convertidas desde presupuesto, `presupuestos-conversion.service` copia el
      // descuento global del presupuesto a `descuento_global_porcentaje` de la orden
      // SIN plegarlo en los ítems — el descuento de cada ítem es sólo el suyo propio.
      // Así el desglose ítem/global se mantiene separado y los totales coinciden.
      const descuentoGlobalOrden = Number(orden.descuento_global_porcentaje) || 0;
      if (descuentoGlobalOrden > 0) {
        setDescuentoGlobal(descuentoGlobalOrden);
        setTipoDescuento("porcentaje");
      }

      setOrdenActiva(orden);

      // Autocompletar Nº orden de venta (gCamGen dOrdVta) desde la orden de origen,
      // sin pisar un valor cargado manualmente. SIFEN limita a 15 caracteres.
      if (orden.numero_pedido) {
        setOrdenVenta((prev) => prev || String(orden.numero_pedido).slice(0, 15));
      }

      toast.success(`Orden ${orden.numero_pedido} cargada — ${detalles.length} items`);
    } catch (err) {
      console.error("Error cargando orden de venta:", err);
      toast.error(err?.response?.data?.message || "Error al cargar la orden de venta");
    }
  };

  // Deep-link: /pos?orden=<id> precarga esa orden de venta para facturarla
  // (usado por el botón "Facturar" del detalle de OV). Espera a que carguen los
  // catálogos (monedas) para mapear bien moneda/condición/depósito.
  useEffect(() => {
    const ordenId = searchParams.get("orden");
    if (!ordenId || ordenDeepLinkRef.current) return;
    if (monedas.length === 0) return;
    ordenDeepLinkRef.current = true;
    handleSelectOrden({ id: ordenId });
    const next = new URLSearchParams(searchParams);
    next.delete("orden");
    setSearchParams(next, { replace: true });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [searchParams, monedas.length]);

  // Deep-link: /pos?pedido=<id> precarga un pedido mayorista para cobrarlo/facturarlo.
  // Lo usa el botón "Facturar" de los pedidos ecommerce entregados o retirados:
  // antes el operador tenía que buscar el pedido a mano en el POS.
  const pedidoDeepLinkRef = useRef(false);
  useEffect(() => {
    const pedidoId = searchParams.get("pedido");
    if (!pedidoId || pedidoDeepLinkRef.current) return;
    if (monedas.length === 0) return;
    pedidoDeepLinkRef.current = true;
    handleSelectPedido({ id: pedidoId });
    const next = new URLSearchParams(searchParams);
    next.delete("pedido");
    setSearchParams(next, { replace: true });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [searchParams, monedas.length]);

  // ═══════════ CARGA DE PRESUPUESTO (facturación sin OV) ═══════════
  // Sólo se usa si la empresa habilitó `presupuesto_facturar_sin_ov`. El circuito
  // normal sigue siendo presupuesto → Orden de Venta → factura.

  const IVA_POR_CATEGORIA = { EXENTO: 0, IVA_5: 5, IVA_10: 10 };

  const handleSelectPresupuesto = async (presupuestoBase) => {
    try {
      const presupuesto = await getPresupuesto(presupuestoBase.id);
      if (!presupuesto) throw new Error("Presupuesto no encontrado");

      setItems([]);
      setPagos([]);
      setCuotas([]);
      setDescuentoGlobal(0);
      setTipoDescuento("ninguno");

      const clientePres = presupuesto.clientes;
      if (clientePres) {
        const c = {
          id: clientePres.id,
          personas: clientePres.personas || { razon_social: clientePres.nombre_fantasia },
          ...clientePres,
        };
        setCliente(c);
        setDireccionFactura(c.personas?.direccion || c.direccion || "");
      }

      if (presupuesto.moneda_id && monedas.length > 0) {
        const mon = monedas.find(
          (m) => m.moneda_id === presupuesto.moneda_id || m.moneda?.id === presupuesto.moneda_id,
        );
        if (mon) setMonedaActiva(mon);
      }

      if (presupuesto.condicion_pago_id && condicionesPago.length > 0) {
        const cond = condicionesPago.find(
          (c) => c.condicion_operacion_id === presupuesto.condicion_pago_id,
        );
        if (cond) setCondicionPagoActiva(cond);
      }

      if (presupuesto.vendedor_id) {
        setVendedorActivo({ id: presupuesto.vendedor_id, ...presupuesto.vendedor });
      }

      const afectacionesIva = referencialesData?.afectacion_iva || [];
      const monPres = monedas.find(
        (m) => m.moneda_id === presupuesto.moneda_id || m.moneda?.id === presupuesto.moneda_id,
      );
      const codigoMonedaPres = monPres?.moneda?.codigo || presupuesto.moneda?.codigo || "PYG";

      // Igual que en la conversión a OV: sólo las líneas con producto se facturan.
      // Subtítulos, texto libre y descuento global son informativos.
      const convertibles = (presupuesto.items || []).filter((it) => it.producto_id);
      const omitidas = (presupuesto.items || []).filter(
        (it) => !it.producto_id && !["SUBTITULO", "TEXTO_LIBRE", "DESCUENTO_GLOBAL"].includes(it.tipo),
      );

      const itemsFactura = convertibles.map((it) => {
        // El descuento propio del ítem (sea % o monto) ya está reflejado en
        // subtotal_con_iva. Lo convertimos a su % equivalente, igual que
        // presupuestos-conversion.service, para que los totales coincidan.
        const cantidad = Number(it.cantidad) || 0;
        const precioUnitario = Number(it.precio_unitario) || 0;
        const bruto = precioUnitario * cantidad;
        const netoItem = Number(it.subtotal_con_iva) || 0;
        const descItemPct =
          bruto > 0 ? Math.min(Math.max((1 - netoItem / bruto) * 100, 0), 100) : 0;

        const datosEdicion = {
          cantidad: cantidad || 1,
          precio: precioUnitario,
          descuento: Math.round(descItemPct * 100) / 100,
          tipoDescuento: "porcentaje",
          descripcion: it.descripcion || it.productos?.descripcion || "",
          unidad_medida: it.unidad_medida || "UNI",
        };

        const producto = {
          id: it.producto_id,
          codigo: it.productos?.codigo || it.productos?.cod_producto || "",
          codigo_barra: it.productos?.codigo_barra || "",
          descripcion: it.descripcion || it.productos?.descripcion || "",
          ...afectacionDesdeTasa(IVA_POR_CATEGORIA[it.iva_categoria] ?? 10),
          unidad_medida: it.unidad_medida || "UNI",
        };

        const item = construirItemDetalle(producto, datosEdicion, clientePres, afectacionesIva, {
          cotizacion: cotizacionActiva?.cotizacion_venta || null,
          codigoMoneda: codigoMonedaPres,
        });

        item._presupuesto_id = presupuesto.id;
        item._presupuesto_detalle_id = it.id;

        return item;
      });

      if (itemsFactura.length === 0) {
        toast.error("El presupuesto no tiene líneas con producto para facturar");
        return;
      }

      setItems(itemsFactura);

      // A diferencia de la OV convertida, acá el descuento global NO viene plegado
      // en los ítems: se aplica aparte, como lo guarda el presupuesto.
      const descuentoGlobalPres = Number(presupuesto.descuento_global_pct) || 0;
      if (descuentoGlobalPres > 0) {
        setDescuentoGlobal(descuentoGlobalPres);
        setTipoDescuento("porcentaje");
      }

      setPresupuestoActivo(presupuesto);

      if (omitidas.length > 0) {
        toast.warning(
          `${omitidas.length} línea(s) sin producto no se cargaron (informativas o sin producto asociado).`,
        );
      }
      toast.success(`Presupuesto ${presupuesto.numero} cargado — ${itemsFactura.length} items`);
    } catch (err) {
      console.error("Error cargando presupuesto:", err);
      toast.error(err?.response?.data?.message || "Error al cargar el presupuesto");
    }
  };

  // Deep-link: /pos?presupuesto=<id> precarga ese presupuesto para facturarlo
  // directamente (botón "Facturar" del detalle de presupuesto).
  useEffect(() => {
    const presupuestoId = searchParams.get("presupuesto");
    if (!presupuestoId || presupuestoDeepLinkRef.current) return;
    if (monedas.length === 0) return;
    presupuestoDeepLinkRef.current = true;
    handleSelectPresupuesto({ id: presupuestoId });
    const next = new URLSearchParams(searchParams);
    next.delete("presupuesto");
    setSearchParams(next, { replace: true });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [searchParams, monedas.length]);

  const handleSelectSolicitudCredito = async (solicitudBase) => {
    try {
      const solicitudCompleta = await getSolicitudCredito(solicitudBase.id);
      const solicitud = { ...solicitudBase, ...solicitudCompleta };

      // Limpiar formulario actual
      setItems([]);
      setPagos([]);
      setCuotas([]);
      setDescuentoGlobal(0);
      setTipoDescuento("ninguno");

      // Cargar cliente
      const clienteSol = solicitud.cliente;
      if (clienteSol) {
        const c = {
          id: clienteSol.id,
          personas: clienteSol.personas || {
            razon_social: clienteSol.nombre_fantasia,
            ruc: clienteSol.personas?.ruc,
          },
          ...clienteSol,
        };
        setCliente(c);
        setDireccionFactura(c.personas?.direccion || c.direccion || "");
      }

      // Mapear items de la solicitud a items de factura
      const afectacionesIva = referencialesData?.afectacion_iva || [];
      const detalles = solicitud.detalle || [];
      const cronogramaLen = Array.isArray(solicitud.cronograma)
        ? solicitud.cronograma.length
        : 0;

      // Setear condición de pago: la solicitud es la fuente de verdad.
      // Siempre crear condición con los datos reales de la solicitud (intervalo, cuotas).
      {
        const intervalo = solicitud.intervalo_dias || 30;
        const frecuencia =
          intervalo === 7
            ? "Semanal"
            : intervalo === 15
              ? "Quincenal"
              : intervalo === 30
                ? "Mensual"
                : `Cada ${intervalo} días`;
        const cuotasTotal =
          solicitud.cant_cuotas_total ||
          detalles.reduce((m, d) => Math.max(m, Number(d.cant_cuotas) || 0), 0) ||
          cronogramaLen;
        const condBase =
          solicitud.condicion_pago_id && condicionesPago.length > 0
            ? condicionesPago.find(
                (c) =>
                  c.id === solicitud.condicion_pago_id ||
                  c.condicion_operacion_id === solicitud.condicion_pago_id,
              )
            : null;

        setCondicionPagoActiva({
          ...(condBase || {}),
          id: condBase?.id || null,
          descripcion: `Crédito ${frecuencia} — ${cuotasTotal} cuotas`,
          cod_condicion_venta: 2,
          cod_tipo_credito: 2,
          cuotas: cuotasTotal,
          intervalo_dias: intervalo,
          dias_plazo: intervalo,
          cuota_inicial: Number(solicitud.cuota_inicial_total) > 0,
          _solicitud_credito: true,
        });
      }

      // Setear vendedor y cobrador desde la solicitud
      if (solicitud.vendedor_id) {
        setVendedorActivo({ id: solicitud.vendedor_id, ...solicitud.vendedor });
      }
      if (solicitud.cobrador_id) {
        setCobradorActivo({ id: solicitud.cobrador_id, ...solicitud.cobrador });
      }

      const itemsFactura = detalles.map((det) => {
        const precioUnitario =
          Number(det.precio_total) / (Number(det.cantidad) || 1);

        const datosEdicion = {
          cantidad: Number(det.cantidad) || 1,
          precio: precioUnitario,
          descuento: 0,
          tipoDescuento: "ninguno",
          descripcion: det.descripcion || det.producto?.descripcion || "",
          unidad_medida: det.producto?.unidad_medida || "UNI",
        };

        const producto = {
          id: det.producto_id,
          codigo: det.producto?.codigo || det.producto?.cod_producto || "",
          codigo_barra: det.producto?.codigo_barra || "",
          descripcion: det.descripcion || det.producto?.descripcion || "",
          ...afectacionDesdeTasa(det.porcentaje_iva),
          unidad_medida: det.producto?.unidad_medida || "UNI",
        };

        const item = construirItemDetalle(
          producto,
          datosEdicion,
          clienteSol,
          afectacionesIva,
          {
            cotizacion: cotizacionActiva?.cotizacion_venta || null,
            codigoMoneda: solicitud.moneda || "PYG",
          },
        );

        item._solicitud_credito_detalle_id = det.id;
        item._producto_precio_cuota_id = det.producto_precio_cuota_id;

        return item;
      });

      setItems(itemsFactura);
      setSolicitudActiva(solicitud);
      // Las cuotas y la entrega inicial se cargan automáticamente en PaymentPanel
      // via la prop solicitudActiva (efecto interno del componente).

      const entregaInicial = Number(solicitud.cuota_inicial_total) || 0;
      const maxCuotas = detalles.reduce(
        (m, d) => Math.max(m, Number(d.cant_cuotas) || 0),
        0,
      );

      toast.success(
        `Solicitud ${solicitud.numero_solicitud || ""} cargada — ${detalles.length} productos` +
          (entregaInicial > 0
            ? ` | Entrega: ${entregaInicial.toLocaleString()}`
            : "") +
          (maxCuotas > 0 ? ` | ${maxCuotas} cuotas` : ""),
      );
    } catch (err) {
      console.error("Error cargando solicitud:", err);
      toast.error(
        err?.response?.data?.message || "Error al cargar solicitud en caja",
      );
    }
  };

  // Verificar si el cliente es "Sin nombre" (innominado)
  const esClienteSinNombre = (cli) => {
    if (!cli) return false;
    return (
      cli.cod_cliente === "SN001" ||
      cli.personas?.razon_social?.toLowerCase() === "sin nombre" ||
      cli.personas?.tipo_documento?.codigo === 5 // Innominado
    );
  };

  const handleSave = () => {
    // Validaciones básicas antes de mostrar confirmación
    if (items.length === 0) {
      toast.error("Agregue al menos un producto");
      return;
    }
    if (!cliente) {
      toast.error("Seleccione un cliente");
      return;
    }
    if (!validacionPago.valido) {
      toast.error(validacionPago.mensaje || "Complete el pago");
      return;
    }
    if (cobroDiferido && !cobradorActivo?.id) {
      toast.error("Asigne un cobrador para usar cobro en ruta");
      return;
    }
    if (!numeracionActiva) {
      toast.error("Seleccione un punto de expedición");
      return;
    }
    if (!monedaActiva) {
      toast.error("Seleccione una moneda");
      return;
    }
    if (!condicionPagoActiva) {
      toast.error("Seleccione una condición de pago");
      return;
    }

    // Validar que cliente no sea "Sin nombre" para ventas a crédito
    const esCredito = condicionPagoActiva?.cod_condicion_venta === 2;
    if (esCredito && esClienteSinNombre(cliente)) {
      toast.error(
        "No se puede facturar a crédito con cliente 'Sin nombre'. Seleccione un cliente nominado.",
      );
      return;
    }

    // Mostrar diálogo de confirmación
    setConfirmDialogOpen(true);
  };

  const handleConfirmSave = async (autorizacionIdOverride) => {
    // DocumentoPreviewModal invoca onConfirm={handleConfirmSave} directo, así que
    // en un click normal este argumento es el SyntheticEvent del botón, no un id
    // — de ahí el chequeo de tipo. El override explícito evita depender del
    // estado (stale closures: handlePinSuccess/el listener de socket llaman a
    // esta misma función 100ms después de un setState, antes de que React
    // vuelva a renderizar con el valor nuevo).
    const autorizacionActual =
      typeof autorizacionIdOverride === "string" ? autorizacionIdOverride : autorizacionDescuentoId;
    setConfirmDialogOpen(false);
    setIsSaving(true);

    try {
      // Determinar condición de crédito
      const esCredito = condicionPagoActiva?.cod_condicion_venta === 2;
      const esCuotasCredito = condicionPagoActiva?.cod_tipo_credito === 2;

      // Obtener condicion_operacion de referenciales (Contado=1, Crédito=2)
      const condicionOperacion = referencialesData?.condicion_operacion?.find(
        (c) => c.codigo === condicionPagoActiva?.cod_condicion_venta,
      );

      // Obtener forma_procesamiento_pago "Otro" de referenciales (código 9)
      const formaProcesamiento =
        referencialesData?.forma_procesamiento_pago?.find(
          (f) => f.descripcion?.toLowerCase() === "otro" || f.codigo === 9,
        );
      console.log({ cotizacionActiva });

      // Construir cuotas
      // Si viene de solicitud de crédito, usar cronograma de la solicitud como fuente de verdad
      // en vez de confiar en el estado interno de PaymentPanel (que puede no haber propagado aún).
      // Fase 7 (fix revisión final, Important 2): esto se calcula ACÁ, antes de `cabecera`,
      // porque `dcuotas` (más abajo, en ambas ramas con/sin solicitud) tiene que salir de
      // `cuotasFactura.length` — la cantidad real que se va a facturar — y no de
      // `solicitudActiva.cant_cuotas_total`, que YA incluye la fila de entrega cuando viene
      // de un cronograma persistido (ver NuevaSolicitudCredito.jsx, `cant_cuotas:
      // cronograma.length`), lo que producía `dcuotas = cuotasFactura.length + 1`.
      const entregaSolicitud = solicitudActiva
        ? Number(solicitudActiva.cuota_inicial_total) || 0
        : 0;
      const vencimientoHoy = hoyISO();
      const codigoMonedaFactura = monedaActiva?.moneda?.codigo || "PYG";

      // Fuente de cuotas: cronograma de la solicitud (si aplica) o el estado de PaymentPanel.
      // Convención: el cronograma persistido de la solicitud YA incluye la entrega como cuota 1.
      // PaymentPanel carga ESE MISMO cronograma en su estado `cuotas`, así que cuando la solicitud
      // tiene cronograma persistido la entrega ya está — venga `cuotasFuente` de PaymentPanel o del
      // fallback directo de acá abajo. Por eso el flag depende de la solicitud, no de la fuente:
      // calcularlo solo dentro de `cuotas.length === 0` hacía que, con PaymentPanel ya poblado,
      // quedara en false y se prependiera la entrega DOS veces (doble cuota-entrega en la factura).
      // Los únicos casos que NO incluyen la entrega son los fallbacks sin cronograma persistido.
      const solicitudTieneCronograma =
        !!solicitudActiva &&
        Array.isArray(solicitudActiva.cronograma) &&
        solicitudActiva.cronograma.length > 0;

      let cuotasFuente = cuotas; // cuotas del state (PaymentPanel → onCuotasChange)
      let cronogramaIncluyeEntrega = solicitudTieneCronograma && entregaSolicitud > 0;
      if (solicitudActiva && cuotas.length === 0) {
        const cronograma = solicitudActiva.cronograma;
        if (cronograma && cronograma.length > 0) {
          cuotasFuente = cronograma.map((c) => ({
            monto_cuota: Number(c.monto_cuota),
            vencimiento: c.fecha_vencimiento || c.vencimiento,
            moneda_iso: solicitudActiva.moneda || codigoMonedaFactura,
          }));
        } else {
          // Fallback (solicitudes antiguas sin cronograma persistido): construir por intervalo
          const intervalo = Number(solicitudActiva.intervalo_dias) || 30;
          const cantCuotas = Number(solicitudActiva.cant_cuotas_total) || 1;
          const totalSol = Number(solicitudActiva.monto_total) || 0;
          const saldo = Math.max(0, totalSol - entregaSolicitud);
          const montoCuota = cantCuotas > 0 ? Math.round(saldo / cantCuotas) : saldo;
          const fechaBase = solicitudActiva.fecha_primera_cuota
            ? new Date(solicitudActiva.fecha_primera_cuota)
            : new Date(Date.now() + intervalo * 24 * 60 * 60 * 1000);
          cuotasFuente = Array.from({ length: cantCuotas }, (_, i) => {
            const f = new Date(fechaBase);
            f.setDate(f.getDate() + i * intervalo);
            return {
              monto_cuota: montoCuota,
              vencimiento: f.toISOString().split("T")[0],
              moneda_iso: solicitudActiva.moneda || codigoMonedaFactura,
            };
          });
        }
      }

      const cuotasFactura = [];
      // Solo prepender entrega si el cronograma NO la incluye (fallback / sin solicitud con entrega manual)
      if (entregaSolicitud > 0 && !cronogramaIncluyeEntrega) {
        cuotasFactura.push({
          nro_cuota: 1,
          monto_cuota: entregaSolicitud,
          vencimiento: vencimientoHoy,
          moneda_iso: codigoMonedaFactura,
        });
      }
      const offset = cuotasFactura.length;
      cuotasFuente.forEach((cuota, idx) => {
        cuotasFactura.push({
          nro_cuota: offset + idx + 1,
          monto_cuota: cuota.monto_cuota,
          vencimiento: cuota.vencimiento,
          moneda_iso: cuota.moneda_iso || codigoMonedaFactura,
        });
      });

      // Construir cabecera - el backend reservará el número dentro de la transacción
      const cabecera = {
        cliente_id: cliente.id,
        numeracion_id: numeracionActiva.id, // El backend genera dest, dpunexp, dnumdoc
        deposito_id: depositoActivo?.id || null, // Depósito para descuento de inventario
        vendedor_id: vendedorActivo?.id || null, // Vendedor asignado a la factura
        cobrador_id: cobradorActivo?.id || null, // Cobrador asignado a la factura
        cobro_diferido: cobroDiferido === true,
        pedido_id: ordenActiva?.id || pedidoActivo?.id || null, // Pedido/orden vinculado
        solicitud_credito_id: solicitudActiva?.id || null, // Solicitud de crédito vinculada
        dfeemide: (() => { const now = new Date(); const t = [now.getHours(), now.getMinutes(), now.getSeconds()].map(n => String(n).padStart(2, "0")).join(":"); return `${fecha} ${t}`; })(),
        dserienum: numeracionActiva.serie || null,
        dcondticam: monedaActiva?.moneda?.codigo !== "PYG" ? "1" : null,
        dticam: cotizacionActiva?.cotizacion_venta || null,
        tipo_transaccion_id: tipoTransaccionActivo?.id || null,
        tipo_impuesto_id:
          referencialesData?.tipo_impuesto?.find((t) => t.codigo === 1)?.id ||
          null,
        moneda_id: monedaActiva?.moneda_id || null,
        indicador_presencia_id: indicadorPresenciaActivo?.id || null,
        condicion_operacion_id: condicionOperacion?.id || null,
        tipo_operacion_id: cliente?.tipo_operacion_id || null,
        // Campos de crédito
        // Si viene de solicitud de crédito, usar los datos de la solicitud como fuente de verdad
        // ignorando condicionPagoActiva (que puede ser la condición por defecto del selector).
        ...(() => {
          if (!esCredito) return { icondcred: null, dplazocre: null, dcuotas: null };
          if (solicitudActiva) {
            const cantCuotas = Number(solicitudActiva.cant_cuotas_total) || 0;
            // Si hay más de 1 cuota → en cuotas (icondcred=2); si es 1 cuota → a plazo (icondcred=1)
            const esCuotas = cantCuotas > 1;
            const intervalo = Number(solicitudActiva.intervalo_dias) || 30;
            const plazoDesc = esCuotas ? null : `${intervalo} días`;
            return {
              icondcred: esCuotas ? "2" : "1",
              dplazocre: esCuotas ? null : plazoDesc,
              // Fase 7 (fix Important 2): dcuotas sale de `cuotasFactura` (lo que
              // realmente se factura), no de `cant_cuotas_total` de la solicitud —
              // ver nota arriba, en la construcción de `cuotasFactura`, sobre el
              // off-by-one que esto corregía. Minor 4: `> 1` también cubre el caso
              // de 0 o 1 cuota (devuelve null en vez de un valor inválido).
              dcuotas: esCredito && cuotasFactura.length > 1 ? cuotasFactura.length : null,
            };
          }
          // Sin solicitud: usar condicionPagoActiva normalmente
          return {
            icondcred: esCuotasCredito ? "2" : "1",
            dplazocre: !esCuotasCredito ? condicionPagoActiva?.descripcion : null,
            // Fase 7: dcuotas sale de la cantidad real de cuotas que se van a
            // facturar (cuotasFactura.length), no de la fila de condiciones_pago
            // elegida a mano — evita que se declare a SIFEN un número que no
            // coincide con lo realmente facturado. Ver
            // docs/plan-motor-precios-rentabilidad.md §12.
            dcuotas: esCredito && cuotasFactura.length > 1 ? cuotasFactura.length : null,
          };
        })(),
        plan_cuota_id: planCuotaSeleccionado?.id || undefined,
        dmonent: (() => {
          if (!esCredito) return null;
          // Si viene de solicitud de crédito, la entrega va como cuota 1 (no como dmonent)
          if (solicitudActiva) return null;
          // Sin solicitud: informar pagos realizados en caja como entrega
          const totalPagos = pagos.reduce(
            (sum, p) => sum + (p.monto_pago || 0),
            0,
          );
          return totalPagos > 0 ? totalPagos : null;
        })(),
        dinfadic: infoAdicional || null,
        dordcompra: ordenCompra?.trim() || null,
        dordvta: ordenVenta?.trim() || null,
        dasiento: asientoContable?.trim() || null,
        ddirrec: direccionFactura?.trim() || null,
        cliente_direccion_id: clienteDireccionId || null,
      };

      // Calcular porcentaje de descuento global para distribuir a items
      const totalNetoSinDescGlobal = totales.total_neto_operacion || 0;
      let porcentajeDescuentoGlobal = 0;
      if (tipoDescuento === "porcentaje") {
        porcentajeDescuentoGlobal = descuentoGlobal;
      } else if (tipoDescuento === "monto" && totalNetoSinDescGlobal > 0) {
        porcentajeDescuentoGlobal =
          (descuentoGlobal / totalNetoSinDescGlobal) * 100;
      }

      // Construir items con descuento global distribuido
      const itemsFactura = items.map((item) => {
        // Calcular descuento global unitario para este item
        const descuentoGlobalUnitario =
          (item.precio_unitario * porcentajeDescuentoGlobal) / 100;
        // Recalcular total_operacion con descuento global
        const totalOperacionConDescGlobal =
          (item.precio_unitario -
            (item.descuento_particular_precio_unitario_item || 0) -
            descuentoGlobalUnitario) *
          item.cantidad;

        // Recalcular base gravada y liquidación IVA con el nuevo total_operacion
        // Se resuelve con el helper y no con `|| 10` / `|| 100`: la tasa 0 y la
        // proporción 0 de un exento son válidas, y ese default las convertía en
        // 10 / 100, mandando al DE un ítem con iAfecIVA=3 y dTasaIVA=10.
        const {
          codigo: codigoAfectacion,
          porcentaje: porcentajeIva,
          proporcion: proporcionIva,
        } = resolverAfectacionIva(item);

        let baseGravadaRecalc = 0;
        let liquidacionIvaRecalc = 0;
        let baseGravadaExentaRecalc = 0;

        // Solo recalcular si hay descuento global
        if (porcentajeDescuentoGlobal > 0) {
          // Fórmula según NT SIFEN: base_gravada = [100 * total_operacion * proporcion_iva] / [10000 + porcentaje_iva * proporcion_iva]
          if (codigoAfectacion !== 2 && codigoAfectacion !== 3) {
            baseGravadaRecalc =
              (100 * totalOperacionConDescGlobal * proporcionIva) /
              (10000 + porcentajeIva * proporcionIva);
            liquidacionIvaRecalc = baseGravadaRecalc * (porcentajeIva / 100);
          }
          // Base exenta para gravado parcial (código 4)
          if (codigoAfectacion === 4) {
            baseGravadaExentaRecalc =
              (100 * totalOperacionConDescGlobal * (100 - proporcionIva)) /
              (10000 + porcentajeIva * proporcionIva);
          }
        } else {
          // Sin descuento global, usar valores originales
          baseGravadaRecalc = item.base_gravada || 0;
          liquidacionIvaRecalc = item.liquidacion_iva || 0;
          baseGravadaExentaRecalc = item.base_gravada_exenta || 0;
        }

        return {
          producto_id: item.producto_id,
          unidad_medida: item.cod_unidad_medida || "UNI",
          descripcion: item.descripcion,
          cantidad: item.cantidad,
          precio_unitario: item.precio_unitario,
          total_bruto:
            item.total_bruto_operacion_item ||
            item.precio_unitario * item.cantidad,
          descuento: item.descuento_particular_precio_unitario_item || 0,
          porcentaje_descuento: item.porcentaje_descuento_item || 0,
          descuento_global: descuentoGlobalUnitario,
          total_operacion: totalOperacionConDescGlobal,
          total_gs:
            monedaActiva?.moneda?.codigo !== "PYG"
              ? totalOperacionConDescGlobal *
                (cotizacionActiva?.cotizacion_venta || 1)
              : 0,
          afectacion_iva: codigoAfectacion,
          porcentaje_iva: porcentajeIva,
          proporcion_iva: proporcionIva,
          base_gravada: baseGravadaRecalc,
          liquidacion_iva: liquidacionIvaRecalc,
          base_gravada_exenta: baseGravadaExentaRecalc,
          // Facturar desde remisión: distribuir la cantidad (posiblemente editada)
          // entre las remisiones de origen en orden FIFO. Se busca por clave estable.
          ...(() => {
            const aps = aplicacionesRemision[claveRemision(item.producto_id, item.descripcion)];
            if (!aps?.length) return {};
            return {
              aplicaciones_remision: redistribuirFifo(aps, item.cantidad).map((a) => ({
                nota_remision_det_id: a.nota_remision_det_id,
                cantidad: a.cantidad,
              })),
            };
          })(),
        };
      });

      // Bloqueo de tope de descuento: si el descuento propio de un ítem más la
      // parte proporcional del descuento global supera su tope (calculado por el
      // backend a partir de costo/precio mínimo real), no se envía la factura.
      // Ítems sin dato de tope conocido (aún no resuelto en el cliente) no se
      // bloquean acá — el backend igual revalida todo al crear la factura.
      // Ver docs/plan-motor-precios-rentabilidad.md §10.
      const itemsExcedenTope = itemsFactura
        .map((itemFactura, idx) => {
          const topeItem = items[idx]?.tope_descuento_pct;
          if (topeItem == null || (itemFactura.total_bruto || 0) <= 0) return null;
          const descuentoAplicadoPct = calcularPorcentajeDescuentoEfectivo(
            itemFactura.total_bruto,
            itemFactura.total_operacion,
          );
          if (descuentoAplicadoPct > topeItem + 0.05) {
            return { descripcion: itemFactura.descripcion, descuentoAplicadoPct, topeItem };
          }
          return null;
        })
        .filter(Boolean);

      if (itemsExcedenTope.length > 0 && !autorizacionActual) {
        const detalle = itemsExcedenTope
          .map((i) => `${i.descripcion}: ${i.descuentoAplicadoPct.toFixed(1)}% (máx. ${i.topeItem.toFixed(1)}%)`)
          .join("; ");
        setDescuentoBloqueoDetalle(detalle);
        setDescuentoAuthOpen(true);
        return;
      }

      // Descuento por encima del tope pero ya autorizado en este intento (PIN o
      // remoto): viaja en la cabecera para que el backend consuma esa autorización.
      if (autorizacionActual) {
        cabecera.autorizacion_descuento_id = autorizacionActual;
      }

      // Construir pagos - usar forma_procesamiento_pago "Otro" por defecto
      const pagosFactura = pagos.map((pago) => ({
        medio_pago_id: pago.medio_pago_id,
        moneda_id: monedaActiva?.moneda_id || null,
        monto_pago: pago.monto_pago,
        sesion_caja_id: sesionCaja?.id || null,
        numero_cheque: pago.numero_cheque || null,
        banco_emisor: pago.banco_emisor || null,
        numero_tarjeta: pago.numero_tarjeta || null,
        nro_autorizacion_procesadora: pago.nro_autorizacion_procesadora || null,
        nombre_titular_tarjeta: pago.nombre_titular_tarjeta || null,
        tarjeta_id: pago.tarjeta_id || null,
        forma_procesamiento_pago_id:
          pago.forma_procesamiento_pago_id || formaProcesamiento?.id || null,
        razon_social_procesadora: null,
        ruc_procesadora: null,
        dv_procesadora: null,
        tarjeta_descripcion_otro: null,
      }));

      // Calcular totales recalculados desde los items con descuento global
      const totalesRecalculados = itemsFactura.reduce(
        (acc, item) => {
          acc.totalDescuentoGlobal += item.descuento_global * item.cantidad;
          acc.totalOperacion += item.total_operacion;
          acc.totalLiquidacionIva += item.liquidacion_iva;
          acc.totalBaseGravada += item.base_gravada;

          const afectacion = parseInt(item.afectacion_iva) || 1;
          const tasaIva = parseInt(item.porcentaje_iva) || 10;

          // Exento (código 3)
          if (afectacion === 3) {
            acc.subtotalExento += item.total_operacion;
          }
          // Exonerado (código 2)
          else if (afectacion === 2) {
            acc.subtotalExonerado += item.total_operacion;
          }
          // Gravado Total (código 1) o Gravado Parcial (código 4)
          else if (afectacion === 1 || afectacion === 4) {
            if (tasaIva === 5) {
              acc.liquidacionIva5 += item.liquidacion_iva;
              acc.baseGravada5 += item.base_gravada;
              acc.subtotalIva5 += item.total_operacion;
            } else if (tasaIva === 10) {
              acc.liquidacionIva10 += item.liquidacion_iva;
              acc.baseGravada10 += item.base_gravada;
              acc.subtotalIva10 += item.total_operacion;
            }
          }

          return acc;
        },
        {
          totalDescuentoGlobal: 0,
          totalOperacion: 0,
          totalLiquidacionIva: 0,
          totalBaseGravada: 0,
          liquidacionIva5: 0,
          liquidacionIva10: 0,
          baseGravada5: 0,
          baseGravada10: 0,
          subtotalIva5: 0,
          subtotalIva10: 0,
          subtotalExento: 0,
          subtotalExonerado: 0,
        },
      );

      // Construir subtotal con valores recalculados
      const subtotal = {
        subtotal_exento:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.subtotalExento
            : totales.subtotal_operacion_exenta || 0,
        subtotal_exonerado:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.subtotalExonerado
            : totales.subtotal_operacion_exonerada || 0,
        subtotal_5:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.subtotalIva5
            : totales.subtotal_operacion_iva5 || 0,
        subtotal_10:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.subtotalIva10
            : totales.subtotal_operacion_iva10 || 0,
        liquidacion_iva_5:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.liquidacionIva5
            : totales.total_liquidacion_iva5 || 0,
        liquidacion_iva_10:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.liquidacionIva10
            : totales.total_liquidacion_iva10 || 0,
        liquidacion_redondeo_5: 0,
        liquidacion_redondeo_10: 0,
        liquidacion_iva_comision: 0,
        total_iva:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.totalLiquidacionIva
            : totales.total_liquidacion_iva || 0,
        total_operacion:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.totalOperacion
            : totales.total || 0,
        total_descuento:
          (totales.total_descuento_particular_item || 0) +
          totalesRecalculados.totalDescuentoGlobal,
        total_descuento_global_item: totalesRecalculados.totalDescuentoGlobal,
        total_descuento_particular_item:
          totales.total_descuento_particular_item || 0,
        total_bruto_operacion:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.totalOperacion
            : totales.total || 0,
        total_anticipo_item: 0,
        total_anticipo_global_item: 0,
        porcentaje_descuento_total: porcentajeDescuentoGlobal,
        total_anticipo_operacion: 0,
        total_redondeo: 0,
        total_comision: 0,
        total_general_operacion:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.totalOperacion
            : totales.total || 0,
        total_base_gravada_5:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.baseGravada5
            : totales.total_base_gravada_iva5 || 0,
        total_base_gravada_10:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.baseGravada10
            : totales.total_base_gravada_iva10 || 0,
        total_base_gravada:
          porcentajeDescuentoGlobal > 0
            ? totalesRecalculados.totalBaseGravada
            : totales.total_base_gravada_iva || 0,
        total_gs:
          monedaActiva?.moneda?.codigo !== "PYG"
            ? (porcentajeDescuentoGlobal > 0
                ? totalesRecalculados.totalOperacion
                : totales.total) * cotizacionActiva?.cotizacion_venta
            : 0,
      };

      // Estructura final de la factura
      const facturaData = {
        cabecera,
        items: itemsFactura,
        pagos: pagosFactura,
        subtotal,
        cuotas: cuotasFactura,
      };

      console.log("Factura a enviar:", facturaData);

      // Enviar al API
      const response = await createFactura(facturaData);

      toast.success("Factura creada exitosamente");

      // Obtener ID de la factura creada y abrir preview KUDE
      const facturaCreada = response?.data || response;

      // Avisos no bloqueantes del backend: el caso típico es que no se haya podido
      // resolver depósito y la venta NO haya descontado stock. La factura es válida,
      // pero el inventario quedó intacto — hay que verlo, no puede pasar en silencio.
      (facturaCreada?.advertencias || []).forEach((msg) =>
        toast.warning(msg, { duration: 12000 }),
      );
      const facturaId = facturaCreada?.id || facturaCreada?.facturaId;

      if (facturaId) {
        const numero = facturaCreada?.dnumdoc
          ? `${facturaCreada.dest}-${facturaCreada.dpunexp}-${facturaCreada.dnumdoc}`
          : null;
        setKudePreview({ open: true, facturaId, numero });
      }

      // Si es cobro de pedido mayorista, el backend ya lo marca como facturado
      // dentro de la transacción (vía pedido_id en cabecera).
      // Fallback: si no se envió pedido_id, marcar manualmente
      if (pedidoActivo?.id && facturaId) {
        if (!facturaData.cabecera.pedido_id) {
          try {
            await marcarFacturado(pedidoActivo.id, facturaId);
          } catch (err) {
            console.error("Error marcando pedido como facturado:", err);
            toast.warning(
              "Factura creada, pero no se pudo vincular al pedido. Actualice manualmente.",
            );
          }
        }
        toast.success(
          `Pedido ${pedidoActivo.numero_pedido} marcado como facturado`,
        );
      }

      // Marcar orden de venta como facturada
      if (ordenActiva?.id && facturaId) {
        try {
          await marcarOrdenFacturada(ordenActiva.id, facturaId);
          toast.success(`Orden ${ordenActiva.numero_pedido} marcada como facturada`);
        } catch (err) {
          console.error("Error marcando orden como facturada:", err);
          toast.warning("Factura creada, pero no se pudo vincular a la orden de venta.");
        }
      }

      // Cerrar el presupuesto facturado directamente (sin OV). El backend revalida
      // que la empresa tenga la opción habilitada.
      if (presupuestoActivo?.id && facturaId) {
        try {
          await facturarPresupuestoDirecto(presupuestoActivo.id, facturaId);
          toast.success(`Presupuesto ${presupuestoActivo.numero} marcado como facturado`);
        } catch (err) {
          console.error("Error marcando presupuesto como facturado:", err);
          toast.warning("Factura creada, pero no se pudo vincular al presupuesto.");
        }
      }

      // Marcar solicitud de crédito como facturada
      if (solicitudActiva?.id && facturaId) {
        try {
          await marcarSolicitudFacturada(solicitudActiva.id, facturaId);
          toast.success(
            `Solicitud ${solicitudActiva.numero_solicitud || ""} marcada como facturada`,
          );
        } catch (err) {
          console.error("Error marcando solicitud como facturada:", err);
          toast.warning(
            "Factura creada, pero no se pudo vincular a la solicitud de crédito.",
          );
        }
      }

      // Limpiar formulario
      handleClearAll();

      return response;
    } catch (error) {
      console.error("Error al crear factura:", error);
      const payload = error?.response?.data;
      const mensaje =
        payload?.message || error?.message || "Error al crear factura";
      const errores = Array.isArray(payload?.errores) ? payload.errores : [];
      if (errores.length > 0) {
        toast.error(
          <div>
            <div style={{ fontWeight: 600, marginBottom: 4 }}>{mensaje}</div>
            <ul style={{ margin: 0, paddingLeft: 18 }}>
              {errores.map((e, i) => (
                <li key={i}>{e}</li>
              ))}
            </ul>
          </div>,
          { autoClose: 10000 },
        );
      } else {
        toast.error(mensaje);
      }
      // La autorización pudo haber sido rechazada por el backend (vencida, ya
      // usada). Limpiarla obliga a pedir una nueva en el siguiente intento.
      setAutorizacionDescuentoId(null);
    } finally {
      setIsSaving(false);
    }
  };

  const handlePrint = () => {
    // Si hay una factura recién creada, abrir el preview
    if (kudePreview.facturaId) {
      setKudePreview((prev) => ({ ...prev, open: true }));
    } else {
      toast.info("Cree una factura primero para imprimir");
    }
  };

  // Si no hay sesión de caja activa, mostrar pantalla de apertura
  if (!sesionCaja) {
    return (
      <ThemeProvider theme={themeStyle}>
        <Container>
          <Box
            sx={{
              display: "flex",
              flexDirection: "column",
              alignItems: "center",
              justifyContent: "center",
              minHeight: "calc(100vh - 100px)",
              p: 4,
            }}
          >
            <Icon
              icon="lucide:lock-open"
              width={64}
              style={{ color: "#f88533" }}
            />
            <Typography variant="h4" fontWeight={700} sx={{ mt: 3 }}>
              Apertura de Caja
            </Typography>
            <Typography
              variant="body1"
              color="text.secondary"
              sx={{ mt: 1, mb: 4 }}
            >
              Selecciona una caja disponible para comenzar a operar
            </Typography>

            {cajasDisponibles.length === 0 ? (
              <Box sx={{ textAlign: "center" }}>
                <Icon
                  icon="lucide:alert-circle"
                  width={48}
                  style={{ color: "#f88533" }}
                />
                <Typography variant="h6" sx={{ mt: 2, fontWeight: 600 }}>
                  Sin cajas disponibles
                </Typography>
                <Typography
                  variant="body2"
                  color="text.secondary"
                  sx={{ mt: 1, maxWidth: 400 }}
                >
                  No hay cajas disponibles para abrir. Verifica que existan
                  cajas configuradas en tu sucursal.
                </Typography>
                <Button
                  variant="outlined"
                  startIcon={<Icon icon="lucide:settings" />}
                  sx={{ mt: 3 }}
                  onClick={() => (window.location.href = "/configuracion")}
                >
                  Ir a Configuración
                </Button>
              </Box>
            ) : (
              <>
                <Grid
                  container
                  spacing={3}
                  sx={{ maxWidth: 800, justifyContent: "center" }}
                >
                  {cajasDisponibles.map((caja) => {
                    const isSelected = cajaSeleccionada?.id === caja.id;
                    const enUso = caja.en_uso;

                    return (
                      <Grid item xs={12} sm={6} md={4} key={caja.id}>
                        <Paper
                          elevation={isSelected ? 8 : 2}
                          sx={{
                            p: 3,
                            cursor: enUso ? "not-allowed" : "pointer",
                            opacity: enUso ? 0.6 : 1,
                            border: isSelected
                              ? "2px solid #f88533"
                              : "2px solid transparent",
                            transition: "all 0.2s",
                            "&:hover": {
                              transform: enUso ? "none" : "translateY(-4px)",
                              boxShadow: enUso ? 2 : 8,
                            },
                          }}
                          onClick={() => !enUso && setCajaSeleccionada(caja)}
                        >
                          <Box
                            sx={{
                              display: "flex",
                              alignItems: "flex-start",
                              justifyContent: "space-between",
                              mb: 2,
                            }}
                          >
                            <Box
                              sx={{
                                width: 48,
                                height: 48,
                                borderRadius: 2,
                                display: "flex",
                                alignItems: "center",
                                justifyContent: "center",
                                bgcolor: enUso
                                  ? "error.main"
                                  : isSelected
                                    ? "warning.main"
                                    : "success.main",
                              }}
                            >
                              <Icon
                                icon={enUso ? "lucide:lock" : "lucide:unlock"}
                                width={24}
                                color="white"
                              />
                            </Box>
                            <Chip
                              label={enUso ? "En uso" : "Disponible"}
                              size="small"
                              color={enUso ? "error" : "success"}
                              variant={enUso ? "filled" : "outlined"}
                            />
                          </Box>
                          <Typography variant="h6" fontWeight={600}>
                            {caja.descripcion || "Caja"}
                          </Typography>
                          <Box
                            sx={{
                              display: "flex",
                              alignItems: "center",
                              gap: 1,
                              mt: 1,
                            }}
                          >
                            <Icon icon="lucide:store" width={16} color="#888" />
                            <Typography variant="body2" color="text.secondary">
                              {/* El backend devuelve la relación como
                                  `empresas_sucursales`; leer `caja.sucursal`
                                  daba siempre undefined y caía al fallback,
                                  etiquetando toda caja con la sucursal activa
                                  aunque fuera de otra. */}
                              {caja.empresas_sucursales?.descripcion ||
                                caja.sucursal?.descripcion ||
                                "Sin sucursal"}
                            </Typography>
                          </Box>
                        </Paper>
                      </Grid>
                    );
                  })}
                </Grid>

                {cajaSeleccionada && (
                  <Paper sx={{ mt: 4, p: 3, maxWidth: 400, width: "100%" }}>
                    <Typography
                      variant="subtitle1"
                      fontWeight={600}
                      gutterBottom
                    >
                      Monto de Apertura
                    </Typography>
                    <TextField
                      fullWidth
                      placeholder="0"
                      value={
                        montoApertura
                          ? parseInt(
                              montoApertura.replace(/\D/g, "") || 0,
                            ).toLocaleString("es-PY")
                          : ""
                      }
                      onChange={(e) => {
                        const numericValue = e.target.value.replace(/\D/g, "");
                        setMontoApertura(numericValue);
                      }}
                      InputProps={{
                        startAdornment: (
                          <Typography sx={{ mr: 1, color: "text.secondary" }}>
                            Gs.
                          </Typography>
                        ),
                      }}
                      sx={{ mb: 2 }}
                    />
                    <Button
                      variant="contained"
                      fullWidth
                      size="large"
                      disabled={isLoadingCaja}
                      onClick={handleAbrirCaja}
                      sx={{
                        bgcolor: "#f88533",
                        "&:hover": { bgcolor: "#e07020" },
                      }}
                      startIcon={<Icon icon="lucide:unlock" />}
                    >
                      {isLoadingCaja ? "Abriendo..." : "Abrir Caja"}
                    </Button>
                  </Paper>
                )}
              </>
            )}
          </Box>
        </Container>
      </ThemeProvider>
    );
  }

  return (
    <ThemeProvider theme={themeStyle}>
      <Container>

        {/* Alerta de cotización pendiente */}
        {showCotizacionAlert && cotizacionPendiente && (
          <CotizacionAlert>
            <div className="alert-content">
              <Icon icon="mdi:alert-circle" width="24" />
              <div className="alert-text">
                <strong>
                  No hay cotización del día para{" "}
                  {monedaActiva?.moneda?.descripcion ||
                    monedaActiva?.moneda?.codigo}
                </strong>
                <p>
                  Última cotización disponible:{" "}
                  <strong>
                    {new Date(cotizacionPendiente.fecha).toLocaleDateString(
                      "es-PY",
                    )}
                  </strong>
                  <br />
                  Compra:{" "}
                  <strong>
                    {cotizacionPendiente.cotizacion_compra?.toLocaleString(
                      "es-PY",
                    )}
                  </strong>{" "}
                  | Venta:{" "}
                  <strong>
                    {cotizacionPendiente.cotizacion_venta?.toLocaleString(
                      "es-PY",
                    )}
                  </strong>
                </p>
              </div>
            </div>
            <div className="alert-actions">
              <button onClick={handleAceptarCotizacion} className="accept">
                <Icon icon="mdi:check" /> Usar esta cotización
              </button>
              <button onClick={handleRechazarCotizacion} className="reject">
                <Icon icon="mdi:close" /> Cargar nueva
              </button>
            </div>
          </CotizacionAlert>
        )}

        <Header>
          <div className="title">
            <Icon icon="mdi:file-document-edit" width="28" />
            <h1>Nueva Factura</h1>
          </div>
          <div className="info">
            <Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
              {sesionCaja && (
                <Chip
                  icon={<Icon icon="mdi:cash-register" width={18} />}
                  label={`Caja: ${cajaSeleccionada?.descripcion || "Abierta"}`}
                  color="success"
                  size="small"
                  variant="outlined"
                />
              )}
              <Button
                variant="outlined"
                color="error"
                size="small"
                startIcon={<Icon icon="mdi:lock" width={18} />}
                onClick={() => setShowCerrarCajaDialog(true)}
                sx={{ textTransform: "none" }}
              >
                Cerrar Caja
              </Button>
              <span className="user">
                <Icon icon="mdi:account" /> {user?.nombres || ""}{" "}
                {user?.apellidos || ""}
              </span>
            </Box>
          </div>
        </Header>

        <Content>
          <MainSection>
            <InvoiceHeader
              // Datos de facturación
              sucursales={sucursales}
              sucursalActiva={sucursalActiva}
              onSucursalChange={handleSucursalChange}
              puntosExpedicion={puntosExpedicion}
              puntoExpedicionActivo={puntoExpedicionActivo}
              onPuntoExpedicionChange={handlePuntoExpedicionChange}
              numeracionSiguiente={numeracionSiguiente}
              fecha={fecha}
              onFechaChange={setFecha}
              // Monedas
              monedas={monedas}
              monedaActiva={monedaActiva}
              onMonedaChange={setMonedaActiva}
              // Referenciales
              tiposTransaccion={tiposTransaccion}
              tipoTransaccionActivo={tipoTransaccionActivo}
              onTipoTransaccionChange={setTipoTransaccionActivo}
              condicionesPago={condicionesPago}
              condicionPagoActiva={condicionPagoActiva}
              onCondicionPagoChange={handleCondicionPagoChange}
              indicadoresPresencia={indicadoresPresencia}
              indicadorPresenciaActivo={indicadorPresenciaActivo}
              onIndicadorPresenciaChange={setIndicadorPresenciaActivo}
              // Depósitos para inventario
              depositos={depositos}
              depositoActivo={depositoActivo}
              onDepositoChange={setDepositoActivo}
              // Vendedor y Cobrador
              vendedorActivo={vendedorActivo}
              onVendedorChange={setVendedorActivo}
              cobradorActivo={cobradorActivo}
              onCobradorChange={setCobradorActivo}
              solicitudActiva={solicitudActiva}
              // Cliente
              cliente={cliente}
              onClienteChange={handleClienteChange}
              direccionFactura={direccionFactura}
              onDireccionChange={setDireccionFactura}
              clienteDirecciones={clienteDirecciones}
              clienteDireccionId={clienteDireccionId}
              onClienteDireccionChange={(dir) => {
                setClienteDireccionId(dir.id);
                setDireccionFactura(dir.direccion || "");
              }}
              onVerPedidos={
                tienePedidos ? () => setShowPedidosDialog(true) : null
              }
              onVerSolicitudesCredito={
                puedeVerSolicitudesCredito
                  ? () => setShowSolicitudesDialog(true)
                  : null
              }
              onVerOrdenes={
                tieneOrdenVenta ? () => setShowOrdenesDialog(true) : null
              }
              onVerPresupuestos={
                cliente?.id ? () => setShowPresupuestosDialog(true) : null
              }
              onVerRemisiones={
                cliente?.id ? () => setShowRemisionesDialog(true) : null
              }
              remisionesActivas={remisionesEnCarrito}
              onQuitarRemisiones={() => {
                setRemisionesActivas([]);
                setAplicacionesRemision({});
                setItems((prev) => prev.filter((it) => !aplicacionesRemision[claveRemision(it.producto_id, it.descripcion)]));
              }}
            />

            <ProductSearch
              onProductSelect={handleAddProduct}
              referenciales={referencialesData}
              cliente={cliente}
              condicionPago={condicionPagoActiva}
              disabled={cotizacionRequerida}
              depositoActivo={depositoActivo}
              sucursalId={sucursalActiva?.id}
              moneda={monedaActiva}
              rubroId={rubroPOS}
              posConfig={config}
            />

            <ItemsTable
              items={items}
              onUpdateItem={handleUpdateItem}
              onRemoveItem={handleRemoveItem}
              moneda={monedaActiva}
              enableDiscounts={config.enable_discounts}
              posConfig={config}
            />
          </MainSection>

          <SidePanel>
            <TotalsPanel
              totales={totales}
              moneda={monedaActiva}
              descuentoGlobal={descuentoGlobal}
              tipoDescuento={tipoDescuento}
              onDescuentoChange={setDescuentoGlobal}
              onTipoDescuentoChange={setTipoDescuento}
              enableDiscounts={config.enable_discounts}
            />

            <PaymentPanel
              key={condicionPagoActiva?.id || condicionPagoActiva?.cod_condicion_venta || "default"}
              totalFactura={totales.total || 0}
              monedaActiva={monedaActiva}
              condicionPago={condicionPagoActiva}
              mediosPago={mediosPago}
              tarjetas={tarjetas}
              planesCuotas={planesCuotas}
              planesCuotasListo={planesCuotasResueltas && !planesCuotasCargando}
              items={items}
              onPagosChange={setPagos}
              onCuotasChange={setCuotas}
              onPlanCuotaChange={setPlanCuotaSeleccionado}
              onPrecioFinanciadoToggle={handlePrecioFinanciadoToggle}
              onValidacionChange={setValidacionPago}
              tieneItems={items.length > 0}
              tieneCliente={!!cliente}
              solicitudActiva={solicitudActiva}
            />

            {condicionPagoActiva?.cod_condicion_venta === 1 && tieneRendiciones && (
              <label
                style={{
                  display: "flex",
                  alignItems: "flex-start",
                  gap: 8,
                  padding: "0.6rem 0.75rem",
                  border: "1px solid rgba(148,163,184,0.3)",
                  borderRadius: 8,
                  fontSize: "0.82rem",
                  cursor: cobradorActivo?.id ? "pointer" : "not-allowed",
                  opacity: cobradorActivo?.id ? 1 : 0.55,
                  marginTop: "0.5rem",
                }}
                title={
                  cobradorActivo?.id
                    ? "El medio de pago se declara para SIFEN pero la plata NO entra a caja. La factura queda con saldo en la cartera del cobrador, que la cobra en ruta y la rinde."
                    : "Asigne primero un cobrador a la factura"
                }
              >
                <input
                  type="checkbox"
                  checked={cobroDiferido}
                  disabled={!cobradorActivo?.id}
                  onChange={(e) => setCobroDiferido(e.target.checked)}
                  style={{ marginTop: 2 }}
                />
                <span>
                  <strong>Cobro en ruta</strong>
                  <br />
                  <span style={{ opacity: 0.7, fontSize: "0.74rem" }}>
                    El pago se declara para SIFEN pero no entra a caja. La factura va a la cartera del cobrador para que la rinda.
                  </span>
                </span>
              </label>
            )}

            <ActionButtons
              onSave={handleSave}
              onPrint={handlePrint}
              onClear={handleClearAll}
              onInfoAdicional={() => setShowInfoAdicionalDialog(true)}
              tieneDatosAdicionales={
                !!(
                  infoAdicional ||
                  ordenCompra ||
                  ordenVenta ||
                  asientoContable
                )
              }
              pedidoActivo={pedidoActivo}
              solicitudActiva={solicitudActiva}
              disabled={
                items.length === 0 ||
                cotizacionRequerida ||
                !validacionPago.valido
              }
            />

            {/* Diálogo de Información Adicional */}
            {showInfoAdicionalDialog && (
              <div
                style={{
                  position: "fixed",
                  inset: 0,
                  background: "rgba(0,0,0,0.5)",
                  zIndex: 9999,
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                }}
                onClick={() => setShowInfoAdicionalDialog(false)}
              >
                <div
                  style={{
                    background: "var(--bg2, #1a1a2e)",
                    borderRadius: 12,
                    padding: 24,
                    width: "90%",
                    maxWidth: 600,
                    maxHeight: "80vh",
                    overflow: "auto",
                  }}
                  onClick={(e) => e.stopPropagation()}
                >
                  <div
                    style={{
                      display: "flex",
                      justifyContent: "space-between",
                      alignItems: "center",
                      marginBottom: 16,
                    }}
                  >
                    <h3 style={{ margin: 0, fontSize: "1rem" }}>
                      Datos Adicionales
                    </h3>
                    <button
                      onClick={() => setShowInfoAdicionalDialog(false)}
                      style={{
                        background: "none",
                        border: "none",
                        color: "inherit",
                        cursor: "pointer",
                        fontSize: "1.2rem",
                      }}
                    >
                      ✕
                    </button>
                  </div>

                  {/* Campos complementarios comerciales (gCamGen SIFEN) */}
                  <div
                    style={{
                      display: "grid",
                      gridTemplateColumns: "1fr 1fr",
                      gap: 12,
                      marginBottom: 16,
                    }}
                  >
                    <label
                      style={{
                        display: "flex",
                        flexDirection: "column",
                        gap: 4,
                        fontSize: "0.75rem",
                        opacity: 0.85,
                      }}
                    >
                      Nº orden de compra
                      <input
                        type="text"
                        value={ordenCompra}
                        onChange={(e) =>
                          setOrdenCompra(e.target.value.slice(0, 15))
                        }
                        placeholder="Ej: OC-000123"
                        maxLength={15}
                        style={{
                          background: "var(--bg3, #2a2a3e)",
                          color: "inherit",
                          border: "1px solid var(--color2, #444)",
                          borderRadius: 8,
                          padding: "8px 10px",
                          fontSize: "0.85rem",
                          fontFamily: "inherit",
                        }}
                      />
                    </label>

                    <label
                      style={{
                        display: "flex",
                        flexDirection: "column",
                        gap: 4,
                        fontSize: "0.75rem",
                        opacity: 0.85,
                      }}
                    >
                      Nº orden de venta
                      <input
                        type="text"
                        value={ordenVenta}
                        onChange={(e) =>
                          setOrdenVenta(e.target.value.slice(0, 15))
                        }
                        placeholder="Autocompletado desde el origen"
                        maxLength={15}
                        style={{
                          background: "var(--bg3, #2a2a3e)",
                          color: "inherit",
                          border: "1px solid var(--color2, #444)",
                          borderRadius: 8,
                          padding: "8px 10px",
                          fontSize: "0.85rem",
                          fontFamily: "inherit",
                        }}
                      />
                    </label>

                    <label
                      style={{
                        display: "flex",
                        flexDirection: "column",
                        gap: 4,
                        fontSize: "0.75rem",
                        opacity: 0.85,
                      }}
                    >
                      Nº asiento contable
                      <input
                        type="text"
                        value={asientoContable}
                        onChange={(e) =>
                          setAsientoContable(e.target.value.slice(0, 10))
                        }
                        placeholder="Ej: 000789"
                        maxLength={10}
                        style={{
                          background: "var(--bg3, #2a2a3e)",
                          color: "inherit",
                          border: "1px solid var(--color2, #444)",
                          borderRadius: 8,
                          padding: "8px 10px",
                          fontSize: "0.85rem",
                          fontFamily: "inherit",
                        }}
                      />
                    </label>
                  </div>

                  <p
                    style={{
                      fontSize: "0.8rem",
                      opacity: 0.6,
                      marginBottom: 12,
                    }}
                  >
                    Información adicional: este texto se incluirá en la factura
                    electrónica (campo dInfAdic del SIFEN). Máximo 5.000
                    caracteres.
                  </p>
                  <textarea
                    value={infoAdicional}
                    onChange={(e) =>
                      setInfoAdicional(e.target.value.slice(0, 5000))
                    }
                    placeholder="Ej: Solicitud de crédito SC-000001. Condiciones especiales, referencias, notas para el cliente..."
                    maxLength={5000}
                    rows={8}
                    style={{
                      width: "100%",
                      background: "var(--bg3, #2a2a3e)",
                      color: "inherit",
                      border: "1px solid var(--color2, #444)",
                      borderRadius: 8,
                      padding: 12,
                      fontSize: "0.85rem",
                      resize: "vertical",
                      fontFamily: "inherit",
                    }}
                  />
                  <div
                    style={{
                      display: "flex",
                      justifyContent: "space-between",
                      alignItems: "center",
                      marginTop: 8,
                    }}
                  >
                    <span style={{ fontSize: "0.7rem", opacity: 0.5 }}>
                      {infoAdicional.length}/5.000 caracteres
                    </span>
                    <div style={{ display: "flex", gap: 8 }}>
                      {(infoAdicional ||
                        ordenCompra ||
                        ordenVenta ||
                        asientoContable) && (
                        <button
                          onClick={() => {
                            setInfoAdicional("");
                            setOrdenCompra("");
                            setOrdenVenta("");
                            setAsientoContable("");
                          }}
                          style={{
                            padding: "8px 16px",
                            borderRadius: 8,
                            border: "1px solid #dc3545",
                            background: "transparent",
                            color: "#dc3545",
                            cursor: "pointer",
                            fontSize: "0.8rem",
                          }}
                        >
                          Limpiar
                        </button>
                      )}
                      <button
                        onClick={() => setShowInfoAdicionalDialog(false)}
                        style={{
                          padding: "8px 20px",
                          borderRadius: 8,
                          border: "none",
                          background: "var(--primary, #3b82f6)",
                          color: "white",
                          cursor: "pointer",
                          fontWeight: 600,
                          fontSize: "0.85rem",
                        }}
                      >
                        {infoAdicional ||
                        ordenCompra ||
                        ordenVenta ||
                        asientoContable
                          ? "Guardar"
                          : "Cerrar"}
                      </button>
                    </div>
                  </div>
                </div>
              </div>
            )}
          </SidePanel>
        </Content>

        {/* Preview de la factura antes de registrar */}
        <DocumentoPreviewModal
          open={confirmDialogOpen}
          onClose={() => setConfirmDialogOpen(false)}
          onConfirm={handleConfirmSave}
          loading={isSaving}
          titulo={
            pedidoActivo
              ? `Facturar Pedido ${pedidoActivo.numero_pedido}`
              : solicitudActiva
                ? `Facturar Solicitud ${solicitudActiva.numero_solicitud || ""}`
                : "Confirmar Factura"
          }
          confirmLabel="Registrar factura"
          moneda={monedaActiva?.moneda?.codigo || "PYG"}
          campos={[
            { label: "Número", value: numeracionSiguiente || "—" },
            { label: "Fecha", value: fecha ? new Date(`${fecha}T00:00:00`).toLocaleDateString("es-PY") : "—" },
            {
              label: "Cliente",
              value:
                cliente?.personas?.razon_social ||
                cliente?.personas?.nombre ||
                cliente?.razon_social ||
                cliente?.nombre ||
                "Sin nombre",
            },
            cliente?.personas?.ruc && { label: "RUC / Doc.", value: `${cliente.personas.ruc}${cliente.personas.dv != null ? `-${cliente.personas.dv}` : ""}` },
            { label: "Condición", value: condicionPagoActiva?.descripcion || "-" },
            { label: "Moneda", value: monedaActiva?.moneda?.descripcion || monedaActiva?.moneda?.codigo || "PYG" },
            sucursalActiva && { label: "Sucursal", value: sucursalActiva?.descripcion || sucursalActiva?.nombre },
            depositoActivo && { label: "Depósito", value: depositoActivo?.descripcion || depositoActivo?.nombre },
            tipoTransaccionActivo && { label: "Tipo transacción", value: tipoTransaccionActivo?.descripcion },
            indicadorPresenciaActivo && { label: "Operación", value: indicadorPresenciaActivo?.descripcion },
            vendedorActivo && { label: "Vendedor", value: vendedorActivo?.razon_social || vendedorActivo?.nombre },
            cobradorActivo && { label: "Cobrador", value: cobradorActivo?.razon_social || cobradorActivo?.nombre },
            direccionFactura && { label: "Dirección", value: direccionFactura },
            pedidoActivo && { label: "Pedido", value: pedidoActivo.numero_pedido },
            solicitudActiva && { label: "Solicitud", value: solicitudActiva.numero_solicitud || "S/N" },
          ]}
          items={items.map((it) => ({
            descripcion: it.descripcion,
            cantidad: it.cantidad,
            precio: it.precio_unitario,
            descuento: (Number(it.descuento_particular_precio_unitario_item) || 0) * (Number(it.cantidad) || 1),
            iva: it.porcentaje_iva != null ? it.porcentaje_iva : null,
            total: it.total_operacion_item || 0,
          }))}
          totales={[
            { label: "Total bruto", value: totales.total_bruto_operacion || 0 },
            (totales.total_descuento_particular_item || 0) > 0 && {
              label: "Desc. por ítem",
              value: -(totales.total_descuento_particular_item || 0),
              color: "warning.main",
            },
            (totales.total_descuento_global || 0) > 0 && {
              label: "Descuento global",
              value: -(totales.total_descuento_global || 0),
              color: "warning.main",
            },
            (totales.subtotal_operacion_exenta || 0) > 0 && { label: "Exentas", value: totales.subtotal_operacion_exenta },
            (totales.total_base_gravada_iva || 0) > 0 && { label: "Base gravada", value: totales.total_base_gravada_iva },
            (totales.total_liquidacion_iva10 || 0) > 0 && { label: "IVA 10%", value: totales.total_liquidacion_iva10 },
            (totales.total_liquidacion_iva5 || 0) > 0 && { label: "IVA 5%", value: totales.total_liquidacion_iva5 },
            (totales.total_liquidacion_iva || 0) > 0 && { label: "Total IVA", value: totales.total_liquidacion_iva },
            { label: "TOTAL", value: totales.total || 0, fuerte: true, color: "primary.main" },
          ]}
          pagos={pagos.map((p) => ({
            label: mediosPago.find((m) => m.id === p.medio_pago_id)?.descripcion || "Pago",
            value: p.monto_pago || 0,
          }))}
          notas={infoAdicional}
        />

        {/* Modal de preview KUDE */}
        <KudePreviewModal
          open={kudePreview.open}
          onClose={() => setKudePreview({ open: false, facturaId: null })}
          documentoId={kudePreview.facturaId}
          tipoDocumento={1}
          numeroDocumento={kudePreview.numero}
        />

        {/* Diálogo de cerrar caja */}
        <CerrarCajaDialog
          open={showCerrarCajaDialog}
          onClose={() => setShowCerrarCajaDialog(false)}
          sesionCaja={sesionCaja}
          cajaDescripcion={cajaSeleccionada?.descripcion}
          mediosPago={mediosPago}
          montoCierre={montoCierre}
          setMontoCierre={setMontoCierre}
          observacionesCierre={observacionesCierre}
          setObservacionesCierre={setObservacionesCierre}
          onCerrarCaja={handleCerrarCaja}
          isLoading={isLoadingCaja}
          requiereArqueo={!!posConfig?.requiere_arqueo_cierre}
        />

        {/* Fase 1: Modal PIN Supervisor */}
        <ModalPinSupervisor
          open={showPinModal}
          onClose={() => setShowPinModal(false)}
          onSuccess={handlePinSuccess}
          tipoOperacion={pinOperacion || "apertura_caja"}
          contexto={
            pinOperacion === "descuento"
              ? { detalle: descuentoBloqueoDetalle }
              : { caja_id: cajaSeleccionada?.id || sesionCaja?.caja_id }
          }
        />

        {/* Descuento por encima del tope: elegir cómo autorizar */}

        <Dialog open={descuentoAuthOpen} onClose={() => !remoteAuthWaiting && setDescuentoAuthOpen(false)} maxWidth="xs" fullWidth>
          <DialogTitle>Autorización de supervisor requerida</DialogTitle>
          <DialogContent>
            <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
              El descuento aplicado supera el máximo permitido para uno o más productos:
            </Typography>
            <Typography variant="body2" sx={{ mb: 2, fontWeight: 500 }}>
              {descuentoBloqueoDetalle}
            </Typography>
            {remoteAuthWaiting ? (
              <Box sx={{ display: "flex", alignItems: "center", gap: 1.5, py: 1 }}>
                <CircularProgress size={20} />
                <Typography variant="body2">Esperando aprobación del supervisor...</Typography>
              </Box>
            ) : (
              <Typography variant="body2" color="text.secondary">
                Un supervisor puede autorizar con su PIN acá mismo, o de forma remota desde el
                Panel de Supervisor.
              </Typography>
            )}
          </DialogContent>
          <DialogActions sx={{ px: 3, pb: 2 }}>
            <Button onClick={() => setDescuentoAuthOpen(false)} disabled={remoteAuthWaiting}>
              Cancelar
            </Button>
            <Button
              variant="outlined"
              onClick={handleSolicitarAutorizacionRemota}
              disabled={remoteAuthWaiting}
            >
              Pedir autorización remota
            </Button>
            <Button
              variant="contained"
              onClick={() => {
                setPinOperacion("descuento");
                setShowPinModal(true);
              }}
              disabled={remoteAuthWaiting}
            >
              Ingresar PIN de supervisor
            </Button>
          </DialogActions>
        </Dialog>

        {/* Dialog de pedidos pendientes para cobro */}
        {tienePedidos && (
          <PedidosPendientesDialog
            open={showPedidosDialog}
            onClose={() => setShowPedidosDialog(false)}
            onSelectPedido={handleSelectPedido}
            sucursalId={sucursalActiva?.id}
            clienteId={cliente?.id}
          />
        )}

        {/* Dialog de órdenes de venta aprobadas/en proceso */}
        {tieneOrdenVenta && (
          <OrdenesVentaPendientesDialog
            open={showOrdenesDialog}
            onClose={() => setShowOrdenesDialog(false)}
            onSelectOrden={handleSelectOrden}
            clienteId={cliente?.id}
          />
        )}

        {/* Presupuestos aceptados: se convierten a OV y esa OV entra al carrito
            por el mismo camino que las órdenes, así no se duplica lógica. */}
        <PresupuestosAceptadosDialog
          open={showPresupuestosDialog}
          onClose={() => setShowPresupuestosDialog(false)}
          onSelectOrden={handleSelectOrden}
          onSelectPresupuesto={handleSelectPresupuesto}
          facturarSinOv={Boolean(empresaConfig?.presupuesto_facturar_sin_ov)}
          clienteId={cliente?.id}
        />

        {/* Dialog de remisiones sin factura pendientes de facturar */}
        <RemisionesPendientesDialog
          open={showRemisionesDialog}
          onClose={() => setShowRemisionesDialog(false)}
          onSelect={handleSelectRemisiones}
          clienteId={cliente?.id}
          seleccionadasIds={remisionesEnCarrito.map((r) => r.id)}
        />

        {/* Dialog de solicitudes de crédito aprobadas */}
        {puedeVerSolicitudesCredito && (
          <SolicitudesCreditoPendientesDialog
            open={showSolicitudesDialog}
            onClose={() => setShowSolicitudesDialog(false)}
            onSelectSolicitud={handleSelectSolicitudCredito}
            sucursalId={sucursalActiva?.id}
            clienteId={cliente?.id}
          />
        )}
      </Container>
    </ThemeProvider>
  );
}

const Container = styled.div`
  min-height: 100vh;
  background: ${({ theme }) => theme?.bg || "inherit"};
  color: ${({ theme }) => theme?.text || "inherit"};
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  animation: ${blur_in} 0.3s ease;

  @media ${Device.tablet} {
    padding: 24px;
  }
`;

const CotizacionAlert = styled.div`
  background: ${({ theme }) => theme.bg2};
  border: 1px solid #5c6bc0;
  border-left: 4px solid #5c6bc0;
  border-radius: 8px;
  padding: 14px 18px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;

  .alert-content {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    color: ${({ theme }) => theme.text};

    svg {
      flex-shrink: 0;
      margin-top: 2px;
      color: #5c6bc0;
    }

    .alert-text {
      strong {
        display: block;
        font-size: 0.9rem;
        margin-bottom: 4px;
        color: #5c6bc0;
      }

      p {
        margin: 0;
        font-size: 0.8rem;
        opacity: 0.8;
        line-height: 1.5;
      }
    }
  }

  .alert-actions {
    display: flex;
    gap: 8px;
    flex-shrink: 0;

    button {
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 8px 14px;
      border: none;
      border-radius: 6px;
      font-size: 0.8rem;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s;

      &.accept {
        background: #5c6bc0;
        color: #fff;

        &:hover {
          background: #7986cb;
        }
      }

      &.reject {
        background: transparent;
        border: 1px solid ${({ theme }) => theme.color2};
        color: ${({ theme }) => theme.textSecondary};

        &:hover {
          background: ${({ theme }) => theme.bg3};
        }
      }
    }
  }

  @media (max-width: 768px) {
    flex-direction: column;
    align-items: stretch;

    .alert-actions {
      justify-content: flex-end;
    }
  }
`;

const Header = styled.header`
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  background: ${({ theme }) => theme.bg2};
  border-radius: 12px;
  border: 1px solid ${({ theme }) => theme.color2};

  .title {
    display: flex;
    align-items: center;
    gap: 12px;

    h1 {
      margin: 0;
      font-size: 1.4rem;
      font-weight: 600;
    }

    svg {
      color: ${({ theme }) => theme.primary};
    }
  }

  .info {
    display: flex;
    gap: 20px;

    span {
      display: flex;
      align-items: center;
      gap: 6px;
      font-size: 0.9rem;
      color: ${({ theme }) => theme.textSecondary};
    }
  }

  @media (max-width: 768px) {
    flex-direction: column;
    gap: 12px;

    .info {
      width: 100%;
      justify-content: space-between;
    }
  }
`;

const Content = styled.div`
  display: flex;
  gap: 20px;
  flex: 1;

  @media (max-width: 1024px) {
    flex-direction: column;
  }
`;

const MainSection = styled.main`
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
`;

const SidePanel = styled.aside`
  width: 380px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  flex-shrink: 0;

  @media (max-width: 1024px) {
    width: 100%;
  }
`;
