import { Icon } from "@iconify/react";
import {
  Alert,
  Autocomplete,
  Box,
  Button,
  Card,
  CardContent,
  Chip,
  Collapse,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider as MuiDivider,
  FormControl,
  Grid,
  IconButton,
  InputLabel,
  MenuItem,
  Paper,
  Select,
  Skeleton,
  Tab,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Tabs,
  TextField,
  Tooltip,
  Typography,
  useMediaQuery,
  useTheme,
} from "@mui/material";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { toast } from "sonner";
import styled from "styled-components";
import { anularCompra, createCompra, getCompra, getComprasConfig, getComprasConfigMoneda, getCompras, updateCompra, verificarComprobanteDuplicado } from "../../api/compras.service";
import { getOrdenCompra, getOrdenesCompra } from "../../api/ordenes-compra.service";
import { getRequisicion } from "../../api/requisiciones.service";
import { getDepositoPrincipal, getDepositos } from "../../api/depositos.service";
import { getImportaciones } from "../../api/importaciones.service";
import { getLotesStatus } from "../../api/lotes.service";
import { getEmpresaMonedas } from "../../api/monedas.service";
import { getMediosPagoActivos } from "../../api/pagos.service";
import { getReferenciales } from "../../api/referenciales.service";
import { getSucursales } from "../../api/sucursales.service";
import { getConfigCompras as getConfigComprasFlujo } from "../../api/pagos-proveedor.service";
import { useSubmodulo } from "../../hooks/useSubmodulo.js";
import { usePermission } from "../../hooks/usePermission";
import { useNumberMask } from "../../hooks/useNumberMask";
import { UserAuth } from "../../context/AuthContent";
import { fmtMoneda } from "../../components/_standards/enums/monedas";
import {
  COMPROBANTE_CAMPOS,
  COMPROBANTE_PLACEHOLDERS,
  ComprobanteProveedorFields,
  CotizacionHint,
  DATOS_FISCALES_COMPRA_INICIALES,
  DatosFiscalesCompraFields,
  datosFiscalesDe,
  DetailNumberButton,
  ListToolbar,
  SwitchConAyuda,
  TablePaginationBar,
  TIPOS_COMPROBANTE_COMPRA,
  TIPOS_COMPROBANTE_PARA_COMPRAS,
  TIPOS_TIMBRADO,
} from "../_standards";
import { useCotizacionVigente } from "../../hooks/useCotizacionVigente";
import { useObligacionesFiscalesQuery } from "../../tanstack/MarangatuStack";
import { MonedaInput } from "../common/MonedaInput";
import { ScreenGuia } from "../_standards";
import { GastosTemplate } from "./GastosTemplate";
import { ComprasProductSearch } from "../organismos/ComprasDesign/ComprasProductSearch";
import { ItemsTable } from "../organismos/POSAdmin/ItemsTable";
import { ProveedorSelector } from "../organismos/ProveedoresDesign/ProveedorSelector";
import { OrdenesCompraTab } from "../organismos/ComprasDesign/OrdenesCompraTab";
import { RecepcionesCompraTab } from "../organismos/ComprasDesign/RecepcionesCompraTab";
import { RequisicionesTab } from "../organismos/ComprasDesign/RequisicionesTab";
import { TiposGastoTab } from "../organismos/ComprasDesign/TiposGastoTab";
import { CuentasPagarTab } from "../organismos/ComprasDesign/CuentasPagarTab";
import { ComprasPagosTab } from "../organismos/ComprasDesign/ComprasPagosTab";
import { PlanPagosProveedores } from "../organismos/ComprasDesign/PlanPagosProveedores";
import { NotasCreditoCompraTab } from "../organismos/ComprasDesign/NotasCreditoCompraTab";
import { MarangatuSyncPanel } from "../marangatu/MarangatuSyncPanel";
import { MarangatuPendientesPanel } from "../marangatu/MarangatuPendientesPanel";
import { MarangatuGuia } from "../marangatu/MarangatuGuia";
import { usePendientesQuery } from "../../tanstack/MarangatuStack";
import { ConfirmDialog } from "../ui/ConfirmDialog";
import { TourTrigger } from "../../tours/components/TourTrigger";
import {
  buildCompraFormSteps,
  buildComprasBienvenidaSteps,
  buildComprasFacturasSteps,
  buildComprasGastosSteps,
  buildComprasOrdenesSteps,
  buildComprasRecepcionesSteps,
  buildComprasRequisicionesSteps,
} from "../../tours/definitions/comprasTour";
import { DEFAULT_FLOW_CONFIG, normalizeFlowConfig, resolveComprasActiveTab, resolveComprasFlowUiState } from "../../features/compras/flowConfig";
import { resolveComprasRoundingConfig, roundCompraValue } from "../../features/compras/rounding";
import { hoyISO } from "../../utils/fecha";

const emptyForm = {
  cabecera: {
    proveedor_id: "",
    deposito_id: "",
    sucursal_id: "",
    condicion_operacion_id: "",
    plazo_credito: 30,
    moneda_id: "",
    cotizacion: 1,
    requisicion_id: "",
    orden_compra_id: "",
    // Importación: cuando hay embarque, esta compra es la factura de mercadería
    // del proveedor del exterior. No va al Libro IVA ni mueve stock acá.
    embarque_id: "",
    concepto_costo_id: "",
    convertir_componente_id: "",
    fecha_emision: hoyISO(),
    timbrado_proveedor: "",
    establecimiento: "",
    punto_expedicion: "",
    numero_factura: "",
    // Los comprobantes cargados por esta pantalla son preimpresos salvo aviso: los
    // electrónicos entran solos por la sincronización con Marangatu.
    es_preimpreso: true,
    // Tipo de comprobante e imputación para Marangatu (RG 90). Imputación null = la de la empresa.
    ...DATOS_FISCALES_COMPRA_INICIALES,
    observaciones: "",
    descuento_tipo: "por_item",
    descuento_global_pct: 0,
    descuento_global_monto: 0,
  },
  items: [],
  pagos: [],
  cuotas: [],
};

const parseNumber = (v, fallback = 0) => {
  const n = Number(v);
  return Number.isFinite(n) ? n : fallback;
};

const roundAmount = (value, decimals = 2) => {
  const factor = 10 ** decimals;
  return Math.round(parseNumber(value, 0) * factor) / factor;
};

const computeIvaFromSubtotal = (subtotal, porcentajeIva, roundingConfig) => {
  const ivaRate = parseNumber(porcentajeIva, 0);
  if (ivaRate <= 0) return 0;
  const divisor = (100 + ivaRate) / 100;
  if (!Number.isFinite(divisor) || divisor <= 0) return 0;
  const base = subtotal / divisor;
  return roundCompraValue(subtotal - base, roundingConfig.decimalesMonto, roundingConfig.modo);
};

const formatMontoByMoneda = (value, moneda, amountDecimals) => {
  const amount = parseNumber(value, 0);
  const codigo = moneda?.moneda?.codigo || "PYG";
  const simbolo = moneda?.moneda?.simbolo || "₲";
  const posicionSimbolo = moneda?.moneda?.posicion_simbolo || "L";
  const decimals = Number.isInteger(amountDecimals) ? Math.min(Math.max(amountDecimals, 0), 6) : codigo !== "PYG" ? (moneda?.decimales_visualizar ?? moneda?.moneda?.decimales_estandar ?? 2) : 0;

  const formatted = amount.toLocaleString("es-PY", {
    minimumFractionDigits: decimals,
    maximumFractionDigits: decimals,
  });

  return posicionSimbolo === "R" ? `${formatted} ${simbolo}` : `${simbolo} ${formatted}`;
};

const enrichCompraItem = (item = {}, roundingConfig) => {
  const resolvedRounding = resolveComprasRoundingConfig(roundingConfig);
  const roundAmountForCompra = (value) => roundCompraValue(value, resolvedRounding.decimalesMonto, resolvedRounding.modo);
  const roundPercentageForCompra = (value) => roundCompraValue(value, resolvedRounding.decimalesPorcentaje, resolvedRounding.modo);
  const cantidad = parseNumber(item.cantidad, 1);
  const precioUnitario = parseNumber(item.precio_unitario, 0);
  const descuentoTipo = item.descuento_tipo === "monto" ? "monto" : "porcentaje";
  const subtotalBruto = roundAmountForCompra(cantidad * precioUnitario);
  const descuentoMonto =
    descuentoTipo === "monto"
      ? Math.min(Math.max(roundAmountForCompra(parseNumber(item.descuento_monto, 0)), 0), subtotalBruto)
      : roundAmountForCompra(subtotalBruto * (Math.min(Math.max(roundPercentageForCompra(parseNumber(item.descuento_pct ?? item.descuento_porcentaje, 0)), 0), 100) / 100));
  const descuentoPct =
    descuentoTipo === "monto"
      ? subtotalBruto > 0
        ? roundPercentageForCompra((descuentoMonto / subtotalBruto) * 100)
        : 0
      : Math.min(Math.max(roundPercentageForCompra(parseNumber(item.descuento_pct ?? item.descuento_porcentaje, 0)), 0), 100);
  const codigo = item.cod_producto || item.codigo || "S/C";

  return {
    ...item,
    cod_producto: codigo,
    codigo,
    unidad_medida: item.unidad_medida || "UNI",
    maneja_lote: Boolean(item.maneja_lote),
    descuento_tipo: descuentoTipo,
    descuento_pct: descuentoPct,
    descuento_monto: descuentoMonto,
    subtotal_bruto: subtotalBruto,
    descuento_particular_precio_unitario_item: roundAmountForCompra(precioUnitario * (descuentoPct / 100)),
    total_operacion_item: roundAmountForCompra(subtotalBruto - descuentoMonto),
  };
};

const itemRequiresLote = (item, lotesEnabled) => Boolean(lotesEnabled && item?.maneja_lote && (item?.afecta_stock ?? true));

const toIsoDate = (date) => {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
};

const getStartOfWeek = (date) => {
  const d = new Date(date);
  const day = d.getDay();
  const diff = day === 0 ? -6 : 1 - day;
  d.setDate(d.getDate() + diff);
  d.setHours(0, 0, 0, 0);
  return d;
};

const formatCompraDate = (rawValue) => {
  if (!rawValue) return "-";

  if (rawValue instanceof Date) {
    if (Number.isNaN(rawValue.getTime())) return "-";
    return rawValue.toLocaleDateString("es-PY", {
      day: "2-digit",
      month: "2-digit",
      year: "numeric",
    });
  }

  if (typeof rawValue === "string") {
    const dateOnlyMatch = rawValue.match(/^(\d{4})-(\d{2})-(\d{2})/);
    if (dateOnlyMatch) {
      const [, year, month, day] = dateOnlyMatch;
      return `${day}/${month}/${year}`;
    }

    const parsed = new Date(rawValue);
    if (!Number.isNaN(parsed.getTime())) {
      return parsed.toLocaleDateString("es-PY", {
        day: "2-digit",
        month: "2-digit",
        year: "numeric",
      });
    }
    return "-";
  }

  if (typeof rawValue === "object") {
    if (rawValue?.$date) return formatCompraDate(rawValue.$date);
    if (rawValue?.$d) return formatCompraDate(rawValue.$d);

    if (typeof rawValue?.toDate === "function") {
      try {
        return formatCompraDate(rawValue.toDate());
      } catch (_) {
        return "-";
      }
    }

    if (typeof rawValue?.toISOString === "function") {
      try {
        return formatCompraDate(rawValue.toISOString());
      } catch (_) {
        return "-";
      }
    }
  }

  return "-";
};

const buildFacturaCompraNumero = (compra) => {
  if (!compra) return "-";
  const est = String(compra?.establecimiento || "").trim();
  const pexp = String(compra?.punto_expedicion || "").trim();
  const nro = String(compra?.numero_factura || "").trim();
  const parts = [est, pexp, nro].filter(Boolean);
  if (parts.length === 3) return `${est}-${pexp}-${nro}`;
  return nro || "Sin número";
};

const hasPendienteFacturaEnOrden = (orden) => (orden?.orden_compra_det || []).some((det) => parseNumber(det?.cantidad_pendiente_factura, 0) > 0.0001);

const getOrdenCompraVinculoLabel = (orden) => {
  if (!orden) return "";
  const numero = String(orden?.numero_orden || "").trim() || "OC sin número";
  const proveedor = orden?.proveedores?.personas?.razon_social || "Proveedor";
  const fecha = formatCompraDate(orden?.fecha_emision);
  return `${numero} - ${proveedor} - ${fecha}`;
};

// Los nombres salen del estándar compartido (_standards/comprobanteProveedor):
// antes esta pantalla decía "Timbrado proveedor" y "Número factura" mientras
// Gastos y Notas de crédito usaban otros dos juegos de nombres para lo mismo.
const CABECERA_FIELD_RULES = {
  timbrado_proveedor: { maxLength: 8, numericOnly: true, exactLength: 8, label: COMPROBANTE_CAMPOS.timbrado.label },
  establecimiento: { maxLength: 3, numericOnly: true, exactLength: 3, label: COMPROBANTE_CAMPOS.establecimiento.label },
  punto_expedicion: { maxLength: 3, numericOnly: true, exactLength: 3, label: COMPROBANTE_CAMPOS.punto_expedicion.label },
  numero_factura: { maxLength: 7, numericOnly: true, exactLength: 7, label: COMPROBANTE_CAMPOS.numero_factura.label },
  observaciones: { maxLength: 255, numericOnly: false, label: "Observaciones" },
};

const CABECERA_PLACEHOLDERS = {
  timbrado_proveedor: COMPROBANTE_PLACEHOLDERS.timbrado,
  establecimiento: COMPROBANTE_PLACEHOLDERS.establecimiento,
  punto_expedicion: COMPROBANTE_PLACEHOLDERS.punto_expedicion,
  numero_factura: COMPROBANTE_PLACEHOLDERS.numero_factura,
  observaciones: "Observación interna de la compra (opcional)",
};

const CABECERA_ZERO_PAD_FIELDS = new Set(["establecimiento", "punto_expedicion", "numero_factura"]);

const CABECERA_REQUIRED_FIELDS = ["timbrado_proveedor", "establecimiento", "punto_expedicion", "numero_factura"];

const KPI_CARD_SX = {
  width: "100%",
  height: { xs: 74, sm: 96 },
};

const KPI_CARD_CONTENT_SX = {
  height: "100%",
  display: "flex",
  flexDirection: "column",
  justifyContent: "space-between",
  p: { xs: 0.75, sm: 1.5 },
  "&:last-child": { pb: { xs: 0.75, sm: 1.5 } },
  "& .MuiTypography-h5, & .MuiTypography-h6": {
    whiteSpace: "nowrap",
    overflow: "hidden",
    textOverflow: "ellipsis",
    lineHeight: 1.15,
    fontSize: "clamp(1.2rem, 3.8vw, 1.7rem)",
  },
};

const KPI_CAPTION_SX = {
  display: "flex",
  alignItems: "center",
  gap: 0.35,
  fontSize: { xs: "0.52rem", sm: "0.7rem" },
  minHeight: { xs: 14, sm: 20 },
  whiteSpace: "nowrap",
  overflow: "hidden",
  textOverflow: "ellipsis",
};

const KPI_GRID_CONTAINER_SX = {
  display: "grid",
  gridTemplateColumns: { xs: "repeat(2, minmax(0, 1fr))", sm: "repeat(6, minmax(0, 1fr))" },
  gap: { xs: 1, sm: 2 },
  mb: { xs: 2, sm: 3 },
};

const KPI_GRID_ITEM_SX = {
  minWidth: 0,
};

const normalizeCabeceraInput = (field, value) => {
  const rules = CABECERA_FIELD_RULES[field];
  if (!rules || typeof value !== "string") return value;

  let next = value;
  if (rules.numericOnly) next = next.replace(/\D/g, "");
  if (rules.maxLength) next = next.slice(0, rules.maxLength);
  return next;
};

const padCabeceraWithZeros = (field, value) => {
  if (!CABECERA_ZERO_PAD_FIELDS.has(field)) return value;

  const rules = CABECERA_FIELD_RULES[field];
  if (!rules?.exactLength) return value;

  const normalized = String(value || "").replace(/\D/g, "");
  if (!normalized) return "";
  return normalized.slice(-rules.exactLength).padStart(rules.exactLength, "0");
};

const getCabeceraZeroPaddedValues = (cabecera = {}) => {
  const next = {};
  CABECERA_ZERO_PAD_FIELDS.forEach((field) => {
    next[field] = padCabeceraWithZeros(field, cabecera[field]);
  });
  return next;
};

const normalizeApiMessages = (value) => {
  if (!value) return [];
  if (Array.isArray(value)) return value.flatMap((item) => normalizeApiMessages(item));
  if (typeof value === "string") return [value];
  if (typeof value === "object") {
    const nested = [];
    if (value?.constraints && typeof value.constraints === "object") {
      nested.push(...Object.values(value.constraints).flatMap((item) => normalizeApiMessages(item)));
    }
    if (Array.isArray(value?.children)) {
      nested.push(...normalizeApiMessages(value.children));
    }
    if (value?.message) nested.push(...normalizeApiMessages(value.message));
    if (value?.messages) nested.push(...normalizeApiMessages(value.messages));
    if (value?.errors) nested.push(...normalizeApiMessages(value.errors));
    if (value?.error?.message) nested.push(...normalizeApiMessages(value.error.message));
    if (value?.error_description) nested.push(...normalizeApiMessages(value.error_description));
    if (value?.detail) nested.push(...normalizeApiMessages(value.detail));
    if (value?.details) nested.push(...normalizeApiMessages(value.details));
    if (value?.mensaje) nested.push(...normalizeApiMessages(value.mensaje));
    return nested;
  }
  return [];
};

const extractApiErrorMessages = (err, fallback) => {
  const data = err?.response?.data;
  const messages = [
    ...normalizeApiMessages(data?.message),
    ...normalizeApiMessages(data?.messages),
    ...normalizeApiMessages(data?.error),
    ...normalizeApiMessages(data?.errors),
    ...normalizeApiMessages(data?.details),
    ...normalizeApiMessages(err?.message),
  ]
    .map((msg) => String(msg || "").trim())
    .filter(Boolean);

  const uniqueMessages = Array.from(new Set(messages));
  if (uniqueMessages.length === 0) return [fallback];
  return uniqueMessages;
};

const toArray = (value) => (Array.isArray(value) ? value : value?.data || value?.items || []);

const getMonedaOptionId = (moneda) => moneda?.moneda_id || moneda?.moneda?.id || moneda?.id || "";

const getMonedaCodigo = (moneda) => moneda?.moneda?.codigo || moneda?.codigo || "";

const getMonedaDescripcion = (moneda) => moneda?.moneda?.descripcion || moneda?.descripcion || "";

const normalizeEmpresaMonedaOption = (moneda) => {
  const id = getMonedaOptionId(moneda);
  const codigo = getMonedaCodigo(moneda);
  const descripcion = getMonedaDescripcion(moneda);
  const active = moneda?.active ?? moneda?.activo ?? moneda?.moneda?.active ?? true;

  return {
    ...moneda,
    id,
    moneda_id: id,
    codigo,
    descripcion,
    activo: active,
    moneda: moneda?.moneda || {
      id,
      codigo,
      descripcion,
      simbolo: moneda?.simbolo,
      posicion_simbolo: moneda?.posicion_simbolo,
      decimales_estandar: moneda?.decimales_estandar,
    },
  };
};

const normalizeEmpresaMonedas = (monedas = []) => {
  const byId = new Map();
  for (const moneda of monedas) {
    const normalized = normalizeEmpresaMonedaOption(moneda);
    if (!normalized.id || byId.has(normalized.id)) continue;
    byId.set(normalized.id, normalized);
  }
  return Array.from(byId.values());
};

export function ComprasTemplate() {
  const { user } = UserAuth();
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
  const { hasAccess: canViewGastos } = useSubmodulo("TES_GASTOS");
  const { hasAccess: canViewNotasCredito } = useSubmodulo("COMP_NOTAS_CREDITO");
  const { can: canCompras } = usePermission("COMPRAS");
  const canVerNotasCredito = canCompras("CMP_NCC_NOTA_CREDITO_VER");
  const canVerGastos = canCompras("TES_GAS_GASTO_VER");
  const canVerCuentasPagar = canCompras("CMP_CXP_CUENTA_PAGAR_VER");
  const canVerOrdenPago = canCompras("CMP_OP_ORDEN_PAGO_VER");
  // Submódulo COMP_PLAN_PAGOS: si el plan contratado no lo incluye, la pestaña no aparece.
  const canVerPlanPagos = canCompras("CMP_PP_PLAN_PAGOS_VER");
  const { hasAccess: tieneMarangatu } = useSubmodulo("ADM_BOT_MARANGATU");
  const { data: marangatuPendientes = [] } = usePendientesQuery({ enabled: tieneMarangatu });

  const [loading, setLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [compras, setCompras] = useState([]);
  const [total, setTotal] = useState(0);
  // KPIs agregados por el backend sobre el filtro completo (no la página).
  const [estadoStats, setEstadoStats] = useState({ total: 0, pendiente: 0, parcial: 0, pagada: 0, anulada: 0 });
  const [totalesPorMoneda, setTotalesPorMoneda] = useState({});
  const [page, setPage] = useState(1);
  const [limit, setLimit] = useState(20);
  const [activeTab, setActiveTab] = useState(() => {
    // Permite abrir una tab específica vía ?tab= (ej. al volver de "Nueva NC de compra").
    if (typeof window !== "undefined") {
      const t = new URLSearchParams(window.location.search).get("tab");
      if (t) return t;
    }
    return "facturas";
  });
  const [estadoFilter, setEstadoFilter] = useState("");
  const [searchText, setSearchText] = useState("");
  // Búsqueda server-side con debounce. Cuando hay texto, se busca en TODO el
  // historial ignorando el filtro de período (ver ui-standards).
  const [searchDebounced, setSearchDebounced] = useState("");
  const [showForm, setShowForm] = useState(false);
  const [searchParams, setSearchParams] = useSearchParams();
  const convertirRef = useRef(false);

  const [fechaDesde, setFechaDesde] = useState(() => toIsoDate(getStartOfWeek(new Date())));
  const [fechaHasta, setFechaHasta] = useState(() => toIsoDate(new Date()));
  const [filtroRapido, setFiltroRapido] = useState("semana");
  const comprasRequestIdRef = useRef(0);
  const [showFilters, setShowFilters] = useState(false);
  const [createConfirmOpen, setCreateConfirmOpen] = useState(false);
  const [pendingCreatePayload, setPendingCreatePayload] = useState(null);
  const [createValidationMessages, setCreateValidationMessages] = useState([]);
  const [anularConfirmOpen, setAnularConfirmOpen] = useState(false);
  const [pendingAnularCompra, setPendingAnularCompra] = useState(null);
  const [anulandoCompra, setAnulandoCompra] = useState(false);
  const [usarCuotasManuales, setUsarCuotasManuales] = useState(false);

  const [form, setForm] = useState(emptyForm);
  const [editingId, setEditingId] = useState(null);
  // Aviso (no bloqueante) de comprobante ya existente como compra o gasto.
  const [dupComprobante, setDupComprobante] = useState(null);

  const [selectedProveedor, setSelectedProveedor] = useState(null);
  const [depositos, setDepositos] = useState([]);
  // Embarques a los que se puede imputar la factura del exterior. Los
  // CERRADO/SELLADO ya no aceptan costos nuevos, así que no se ofrecen.
  const [embarquesImputables, setEmbarquesImputables] = useState([]);
  const [sucursales, setSucursales] = useState([]);
  const [monedas, setMonedas] = useState([]);
  const [comprasMonedaConfigs, setComprasMonedaConfigs] = useState([]);
  const [mediosPago, setMediosPago] = useState([]);
  const [condicionesOperacion, setCondicionesOperacion] = useState([]);
  const [lotesEnabled, setLotesEnabled] = useState(false);
  const [creditoRequiereCuotas, setCreditoRequiereCuotas] = useState(false);
  const [flowConfig, setFlowConfig] = useState(DEFAULT_FLOW_CONFIG);
  const [flowConfigReady, setFlowConfigReady] = useState(false);
  const [linkedOrdenNumero, setLinkedOrdenNumero] = useState("");
  const [linkedRequisicionNumero, setLinkedRequisicionNumero] = useState("");
  const [ordenesFacturables, setOrdenesFacturables] = useState([]);
  const [loadingOrdenesFacturables, setLoadingOrdenesFacturables] = useState(false);
  const [detalleDialog, setDetalleDialog] = useState({
    open: false,
    loading: false,
    compra: null,
  });

  const monedaActiva = useMemo(() => monedas.find((m) => m.id === form.cabecera.moneda_id) || null, [monedas, form.cabecera.moneda_id]);
  const comprasMonedaConfigActiva = useMemo(() => comprasMonedaConfigs.find((config) => config.moneda_id === form.cabecera.moneda_id) || null, [comprasMonedaConfigs, form.cabecera.moneda_id]);
  const roundingConfig = useMemo(() => resolveComprasRoundingConfig(comprasMonedaConfigActiva, monedaActiva), [comprasMonedaConfigActiva, monedaActiva]);
  const roundFormAmount = useCallback((value) => roundCompraValue(value, roundingConfig.decimalesMonto, roundingConfig.modo), [roundingConfig.decimalesMonto, roundingConfig.modo]);
  const roundFormPercentage = useCallback((value) => roundCompraValue(value, roundingConfig.decimalesPorcentaje, roundingConfig.modo), [roundingConfig.decimalesPorcentaje, roundingConfig.modo]);
  const formatMontoCompra = useCallback((value) => formatMontoByMoneda(value, monedaActiva, roundingConfig.decimalesMonto), [monedaActiva, roundingConfig.decimalesMonto]);

  const descuentoGlobalPctMaskRef = useNumberMask({
    value: parseNumber(form.cabecera.descuento_global_pct, 0),
    onValueChange: (value) =>
      setForm((prev) => ({
        ...prev,
        cabecera: {
          ...prev.cabecera,
          descuento_global_pct: Math.min(Math.max(value, 0), 100),
        },
      })),
    max: 100,
    allowDecimal: roundingConfig.decimalesPorcentaje > 0,
    decimalScale: roundingConfig.decimalesPorcentaje,
  });

  const totalItems = useMemo(
    () =>
      roundCompraValue(
        form.items.reduce((acc, item) => acc + parseNumber(item.total_operacion_item, 0), 0),
        roundingConfig.decimalesMonto,
        roundingConfig.modo,
      ),
    [form.items, roundingConfig.decimalesMonto, roundingConfig.modo],
  );
  const resumenCompra = useMemo(() => {
    const summary = {
      total_bruto: 0,
      total_descuento_items: 0,
      descuento_global_monto: 0,
      total_descuento: 0,
      total_iva_10: 0,
      total_iva_5: 0,
      total_iva: 0,
      base_gravada: 0,
      total_exentas: 0,
      total_operacion: 0,
    };

    // Paso 1 — Aplicar descuento por ítem (siempre); registrar subtotal post-ítem.
    const itemsPostItem = form.items.map((item) => {
      const cantidad = parseNumber(item?.cantidad, 0);
      const costo = parseNumber(item?.precio_unitario, 0);
      const subtotalBruto = roundFormAmount(cantidad * costo);
      const descuentoMonto = Math.min(Math.max(parseNumber(item?.descuento_monto, 0), 0), subtotalBruto);
      const subtotalPostItem = roundFormAmount(subtotalBruto - descuentoMonto);
      const porcentajeIvaRaw = parseNumber(item?.porcentaje_iva, 10);
      const porcentajeIva = porcentajeIvaRaw === 5 ? 5 : porcentajeIvaRaw === 0 ? 0 : 10;

      summary.total_bruto += subtotalBruto;
      summary.total_descuento_items += descuentoMonto;

      return { subtotalPostItem, porcentajeIva };
    });

    // Paso 2 — Descuento global (prorrateado sobre subtotal post-ítem).
    const tipoDescuento = form.cabecera.descuento_tipo || "por_item";
    const subtotalPostItemTotal = itemsPostItem.reduce((acc, i) => acc + i.subtotalPostItem, 0);
    let descuentoGlobalMonto = 0;
    if (tipoDescuento === "global_pct") {
      const pct = Math.min(Math.max(roundFormPercentage(form.cabecera.descuento_global_pct), 0), 100);
      descuentoGlobalMonto = roundFormAmount((subtotalPostItemTotal * pct) / 100);
    } else if (tipoDescuento === "global_monto") {
      const monto = Math.max(parseNumber(form.cabecera.descuento_global_monto, 0), 0);
      descuentoGlobalMonto = Math.min(roundFormAmount(monto), roundFormAmount(subtotalPostItemTotal));
    }
    summary.descuento_global_monto = descuentoGlobalMonto;

    // Distribuir el descuento global proporcionalmente; último ítem absorbe redondeo.
    const cuotasDesc = new Array(itemsPostItem.length).fill(0);
    if (descuentoGlobalMonto > 0 && subtotalPostItemTotal > 0) {
      let acumulado = 0;
      for (let i = 0; i < itemsPostItem.length; i++) {
        if (i === itemsPostItem.length - 1) {
          cuotasDesc[i] = roundFormAmount(descuentoGlobalMonto - acumulado);
        } else {
          const cuota = roundFormAmount((itemsPostItem[i].subtotalPostItem / subtotalPostItemTotal) * descuentoGlobalMonto);
          cuotasDesc[i] = cuota;
          acumulado += cuota;
        }
        if (cuotasDesc[i] > itemsPostItem[i].subtotalPostItem) {
          cuotasDesc[i] = roundFormAmount(itemsPostItem[i].subtotalPostItem);
        }
      }
    }

    // Paso 3 — Con subtotal final por ítem, calcular IVA/base/exento.
    for (let i = 0; i < itemsPostItem.length; i++) {
      const subtotalFinal = roundFormAmount(itemsPostItem[i].subtotalPostItem - cuotasDesc[i]);
      const { porcentajeIva } = itemsPostItem[i];

      if (porcentajeIva === 0) {
        summary.total_exentas += subtotalFinal;
        continue;
      }
      const montoIva = computeIvaFromSubtotal(subtotalFinal, porcentajeIva, roundingConfig);
      summary.base_gravada += subtotalFinal - montoIva;
      if (porcentajeIva === 5) summary.total_iva_5 += montoIva;
      else summary.total_iva_10 += montoIva;
    }

    summary.total_iva = summary.total_iva_5 + summary.total_iva_10;
    summary.total_descuento = summary.total_descuento_items + descuentoGlobalMonto;
    summary.total_operacion = summary.total_bruto - summary.total_descuento;

    return {
      total_bruto: roundFormAmount(summary.total_bruto),
      total_descuento_items: roundFormAmount(summary.total_descuento_items),
      descuento_global_monto: roundFormAmount(summary.descuento_global_monto),
      total_descuento: roundFormAmount(summary.total_descuento),
      total_iva_10: roundFormAmount(summary.total_iva_10),
      total_iva_5: roundFormAmount(summary.total_iva_5),
      total_iva: roundFormAmount(summary.total_iva),
      base_gravada: roundFormAmount(summary.base_gravada),
      total_exentas: roundFormAmount(summary.total_exentas),
      total_operacion: roundFormAmount(summary.total_operacion),
    };
  }, [form.items, form.cabecera.descuento_tipo, form.cabecera.descuento_global_pct, form.cabecera.descuento_global_monto, roundFormAmount, roundFormPercentage, roundingConfig]);
  const totalPagosForm = useMemo(
    () =>
      form.pagos.reduce((acc, pago) => {
        const monto = parseNumber(pago?.monto, 0);
        return acc + (monto > 0 ? monto : 0);
      }, 0),
    [form.pagos],
  );

  // FASE 3 — cotización vigente para el asiento contable (cascada: contabilidad → facturación → fallback).
  const [cotizacionTocada, setCotizacionTocada] = useState(false);
  const { data: cotizacionVigente } = useCotizacionVigente(monedaActiva?.codigo, form.cabecera.fecha_emision);
  useEffect(() => {
    if (cotizacionTocada) return;
    if (!cotizacionVigente?.tasa) return;
    const actual = Number(form.cabecera.cotizacion) || 0;
    if (Math.abs(actual - cotizacionVigente.tasa) < 1e-6) return;
    setForm((prev) => ({
      ...prev,
      cabecera: { ...prev.cabecera, cotizacion: cotizacionVigente.tasa },
    }));
  }, [cotizacionVigente?.tasa, cotizacionTocada, form.cabecera.cotizacion]);
  useEffect(() => {
    setCotizacionTocada(false);
  }, [form.cabecera.moneda_id]);
  const monedaDetalle = useMemo(() => {
    const compra = detalleDialog.compra;
    if (!compra) return null;
    if (compra.moneda) return normalizeEmpresaMonedaOption(compra.moneda);
    return monedas.find((m) => m.id === compra.moneda_id) || null;
  }, [detalleDialog.compra, monedas]);
  const monedaCabeceraCodigo = useMemo(() => getMonedaCodigo(monedaActiva) || "PYG", [monedaActiva]);

  const isCredito = useMemo(() => {
    const selected = condicionesOperacion.find((c) => c.id === form.cabecera.condicion_operacion_id);
    return Number(selected?.codigo) === 2;
  }, [form.cabecera.condicion_operacion_id, condicionesOperacion]);

  const flowUiState = useMemo(() => resolveComprasFlowUiState(flowConfig, flowConfigReady), [flowConfig, flowConfigReady]);
  const requisicionHabilitada = flowUiState.requisicionHabilitada;
  const ordenCompraHabilitada = flowUiState.ordenCompraHabilitada;
  const recepcionHabilitada = flowUiState.recepcionHabilitada;
  const requiereRequisicionObligatoria = flowUiState.requiereRequisicionObligatoria;
  const afectaStockEnCompraDirecta = flowUiState.afectaStockEnCompraDirecta;
  const matching3ViasHabilitado = flowUiState.matching3ViasHabilitado;
  const generaCxpAutomatico = flowUiState.generaCxpAutomatico;
  const ordenPagoHabilitada = flowUiState.ordenPagoHabilitada;
  const pagoParcialHabilitado = flowUiState.pagoParcialHabilitado;
  const requiereAprobacion = flowUiState.requiereAprobacion;
  const workflowHabilitado = flowUiState.workflowHabilitado;
  const workflowObligatorio = flowUiState.workflowObligatorio;
  const comprasTabs = useMemo(() => {
    const tabs = [];
    if (requisicionHabilitada) tabs.push({ value: "requisiciones", label: "Requisiciones" });
    if (ordenCompraHabilitada) tabs.push({ value: "ordenes", label: "Órdenes de compra" });
    if (recepcionHabilitada) tabs.push({ value: "recepciones", label: "Recepciones" });
    tabs.push({ value: "facturas", label: "Facturas" });
    if (canViewNotasCredito && canVerNotasCredito) tabs.push({ value: "notas_credito", label: "Notas de crédito" });
    if (canViewGastos && canVerGastos) tabs.push({ value: "gastos", label: "Gastos" });
    if (canViewGastos && canVerGastos) tabs.push({ value: "tipos_gasto", label: "Tipos de gasto" });
    if (canVerCuentasPagar && !generaCxpAutomatico) tabs.push({ value: "cuentas_pagar", label: "Cuentas a Pagar" });
    if (canVerPlanPagos) tabs.push({ value: "plan_pagos", label: "Plan de pagos" });
    if (canVerOrdenPago && ordenPagoHabilitada) tabs.push({ value: "pagos_proveedor", label: "Orden de Pago" });
    if (tieneMarangatu) {
      tabs.push({
        value: "marangatu",
        label: (
          <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
            Marangatu
            {marangatuPendientes.length > 0 && <Chip size="small" color="warning" label={marangatuPendientes.length} sx={{ fontWeight: 700, minWidth: 24 }} />}
          </Box>
        ),
      });
    }
    return tabs;
  }, [
    canViewGastos,
    canViewNotasCredito,
    canVerNotasCredito,
    canVerGastos,
    canVerCuentasPagar,
    canVerOrdenPago,
    canVerPlanPagos,
    generaCxpAutomatico,
    ordenPagoHabilitada,
    ordenCompraHabilitada,
    recepcionHabilitada,
    requisicionHabilitada,
    tieneMarangatu,
    marangatuPendientes.length,
  ]);
  const activeTabIndex = useMemo(
    () =>
      Math.max(
        0,
        comprasTabs.findIndex((tab) => tab.value === activeTab),
      ),
    [comprasTabs, activeTab],
  );
  const tourFlowHash = useMemo(() => {
    const r = flowUiState.requisicionHabilitada ? 1 : 0;
    const o = flowUiState.ordenCompraHabilitada ? 1 : 0;
    const c = flowUiState.recepcionHabilitada ? 1 : 0;
    const m = flowUiState.requiereRequisicionObligatoria ? 1 : 0;
    const s = flowUiState.afectaStockEnCompraDirecta ? 1 : 0;
    const x = flowUiState.matching3ViasHabilitado ? 1 : 0;
    const a = flowUiState.requiereAprobacion ? 1 : 0;
    const w = flowUiState.workflowObligatorio ? 1 : 0;
    return `r${r}o${o}c${c}m${m}s${s}x${x}a${a}w${w}`;
  }, [flowUiState]);
  const tourSteps = useMemo(() => {
    if (showForm) return buildCompraFormSteps(flowUiState);
    if (activeTab === "requisiciones") return buildComprasRequisicionesSteps(flowUiState);
    if (activeTab === "ordenes") return buildComprasOrdenesSteps(flowUiState);
    if (activeTab === "recepciones") return buildComprasRecepcionesSteps(flowUiState);
    if (activeTab === "gastos" && canViewGastos) return buildComprasGastosSteps(flowUiState);
    if (activeTab === "tipos_gasto" && canViewGastos) return buildComprasGastosSteps(flowUiState);
    return [...buildComprasBienvenidaSteps(flowUiState), ...buildComprasFacturasSteps(flowUiState)];
  }, [activeTab, canViewGastos, showForm, flowUiState]);
  const tourKey = useMemo(() => {
    if (showForm) return `compra-form-v2-${tourFlowHash}`;
    if (activeTab === "requisiciones") return `compras-requisiciones-v2-${tourFlowHash}`;
    if (activeTab === "ordenes") return `compras-ordenes-v2-${tourFlowHash}`;
    if (activeTab === "recepciones") return `compras-recepciones-v2-${tourFlowHash}`;
    if (activeTab === "gastos" && canViewGastos) return `compras-gastos-v2-${tourFlowHash}`;
    if (activeTab === "tipos_gasto" && canViewGastos) return `compras-gastos-v2-${tourFlowHash}`;
    return `compras-facturas-v2-${tourFlowHash}`;
  }, [activeTab, canViewGastos, showForm, tourFlowHash]);
  const comprasGuia = useMemo(() => {
    if (showForm || activeTab === "marangatu") return null;

    const modoFlujoLabel = requiereRequisicionObligatoria
      ? "Requisición obligatoria"
      : flowUiState.modoFlujoCompra === "requisicion_opcional"
        ? "Requisición opcional"
        : requisicionHabilitada
          ? "Directo + requisición disponible"
          : "Compra directa";
    const recepcionLabel = !recepcionHabilitada ? "Recepción inactiva" : ordenCompraHabilitada ? "Recepción contra OC" : "Recepción libre";
    const stockLabel = afectaStockEnCompraDirecta ? "Stock al facturar" : "Stock al recepcionar";
    const aprobacionLabel = workflowObligatorio ? "Workflow obligatorio" : workflowHabilitado ? "Workflow activo" : requiereAprobacion ? "Aprobación requerida" : "Aprobación simple";
    const cxpLabel = generaCxpAutomatico ? "CxP automática" : "CxP manual";
    const ordenPagoLabel = ordenPagoHabilitada ? (pagoParcialHabilitado ? "OP con pagos parciales" : "Orden de pago activa") : "Orden de pago inactiva";

    if (activeTab === "requisiciones") {
      return {
        titulo: "¿Cómo funcionan las requisiciones?",
        intro: requiereRequisicionObligatoria
          ? "La empresa exige iniciar las compras y gastos desde una requisición aprobada; no se permite saltar directamente a la factura."
          : "Usá requisiciones para solicitar y aprobar necesidades internas. Como el flujo es opcional, también podés operar directamente cuando no requieras autorización previa.",
        configuracion: [
          { label: `Flujo: ${modoFlujoLabel}`, color: requiereRequisicionObligatoria ? "warning" : "info" },
          { label: ordenCompraHabilitada ? "OC habilitada" : "OC inactiva", color: ordenCompraHabilitada ? "success" : "default" },
          { label: "Aprobaciones según reglas", color: "info" },
        ],
        pasos: [
          {
            icon: "mdi:file-document-edit-outline",
            color: "#2563eb",
            titulo: "1. Crear solicitud",
            desc: "Hacé clic en «Nueva requisición», elegí origen (Compra o Gasto) y cargá ítems con cantidades estimadas.",
          },
          {
            icon: "mdi:content-save-check-outline",
            color: "#16a34a",
            titulo: "2. Guardar borrador",
            desc: "Guardá para revisión interna. En borrador aún podés ajustar cantidades, descripciones y prioridad.",
          },
          {
            icon: "mdi:account-check-outline",
            color: "#f59e0b",
            titulo: "3. Enviar y aprobar",
            desc: "Enviá a aprobación. Según montos y reglas de la empresa, puede requerir uno o más aprobadores.",
          },
          {
            icon: "mdi:file-export-outline",
            color: "#0ea5e9",
            titulo: "4. Ejecutar flujo",
            desc: ordenCompraHabilitada
              ? "Si el origen es Compra, generá la OC aprobada; si es Gasto, derivá al registro de gasto."
              : "Las requisiciones de Gasto pueden derivarse al gasto. Para convertir una requisición de Compra en OC, primero habilitá Órdenes de Compra.",
          },
          { icon: "mdi:timeline-check-outline", color: "#7c3aed", titulo: "5. Cerrar seguimiento", desc: "La requisición pasa a ejecutada parcial/total según lo que realmente se compró o gastó." },
        ],
        notas: [
          requiereRequisicionObligatoria
            ? { icon: "mdi:lock-outline", text: "Configuración actual: la factura y la OC deben originarse en una requisición aprobada." }
            : { icon: "mdi:source-branch", text: "Alternativa disponible: podés registrar una factura directa cuando no necesitás aprobación previa." },
          !ordenCompraHabilitada && {
            icon: "mdi:cog-outline",
            text: "Para usar requisiciones de origen Compra con trazabilidad completa, activá Órdenes de Compra; de lo contrario usá factura directa.",
          },
          { icon: "mdi:cog-outline", text: "Los niveles y montos de aprobación se configuran en Configuración > Compras > Aprobaciones de Requisiciones." },
          { icon: "mdi:shield-check-outline", text: "Si no podés aprobar/rechazar, revisá permisos del perfil en Configuración > Perfiles." },
        ].filter(Boolean),
      };
    }

    if (activeTab === "ordenes") {
      return {
        titulo: "¿Cómo funcionan las órdenes de compra?",
        intro: "La Orden de Compra formaliza el compromiso con el proveedor. Su recorrido cambia según estén habilitadas la requisición, la recepción y la validación de tres vías.",
        configuracion: [
          { label: `Flujo: ${modoFlujoLabel}`, color: requiereRequisicionObligatoria ? "warning" : "info" },
          { label: recepcionLabel, color: recepcionHabilitada ? "success" : "default" },
          { label: matching3ViasHabilitado ? "Matching 3 vías activo" : "Matching flexible", color: matching3ViasHabilitado ? "warning" : "default" },
          { label: aprobacionLabel, color: requiereAprobacion ? "warning" : "default" },
        ],
        pasos: [
          {
            icon: "mdi:file-sign",
            color: "#2563eb",
            titulo: "1. Crear OC",
            desc: requiereRequisicionObligatoria
              ? "Generala desde una requisición aprobada; la creación manual está bloqueada en este modo."
              : "Creala manualmente o desde una requisición aprobada para heredar sus ítems.",
          },
          {
            icon: "mdi:account-arrow-right-outline",
            color: "#0ea5e9",
            titulo: "2. Confirmar proveedor y condiciones",
            desc: "Validá proveedor, depósito, moneda, condición y plazos antes de confirmar.",
          },
          {
            icon: recepcionHabilitada ? "mdi:truck-delivery-outline" : "mdi:receipt-text-outline",
            color: "#f59e0b",
            titulo: recepcionHabilitada ? "3. Registrar recepción" : "3. Facturar directamente",
            desc: recepcionHabilitada
              ? afectaStockEnCompraDirecta
                ? "Confirmá lo recibido contra la OC. La recepción controla cantidades, pero el stock se actualizará al facturar."
                : "Confirmá lo recibido contra la OC; este paso actualiza stock y descuenta cantidades pendientes."
              : "Como Recepciones está inactiva, vinculá la OC directamente a la factura para completar el circuito.",
          },
          {
            icon: "mdi:receipt-text-outline",
            color: "#16a34a",
            titulo: "4. Vincular la factura",
            desc: matching3ViasHabilitado
              ? "La factura debe coincidir con OC y recepción por línea, cantidad y precio."
              : "Vinculá la factura para conservar trazabilidad y revisá manualmente cualquier diferencia.",
          },
        ],
        notas: [
          {
            icon: "mdi:warehouse",
            text: afectaStockEnCompraDirecta
              ? "Configuración actual: el stock se mueve al confirmar la factura, no al registrar la recepción."
              : "Configuración actual: el stock se mueve en Recepciones; la factura no vuelve a ingresarlo.",
          },
          recepcionHabilitada
            ? { icon: "mdi:clipboard-check-outline", text: "Las recepciones parciales mantienen la OC abierta por la cantidad pendiente." }
            : { icon: "mdi:cog-outline", text: "Alternativa: activá Recepciones si necesitás controlar entregas parciales antes de facturar." },
          !matching3ViasHabilitado && recepcionHabilitada && { icon: "mdi:shield-check-outline", text: "Para bloquear diferencias entre OC, recepción y factura, activá Matching 3 vías estricto." },
        ].filter(Boolean),
      };
    }

    if (activeTab === "recepciones") {
      const recepcionLibre = !ordenCompraHabilitada;
      return {
        titulo: "¿Cómo funcionan las recepciones?",
        intro: recepcionLibre
          ? "La empresa permite registrar ingresos físicos directamente por proveedor, depósito y producto, sin crear una Orden de Compra."
          : "La recepción registra lo que realmente entregó el proveedor y controla el saldo pendiente de cada Orden de Compra.",
        configuracion: [
          { label: recepcionLabel, color: recepcionLibre ? "info" : "success" },
          { label: stockLabel, color: afectaStockEnCompraDirecta ? "warning" : "success" },
          { label: matching3ViasHabilitado ? "Matching 3 vías activo" : "Matching flexible", color: matching3ViasHabilitado ? "warning" : "default" },
        ],
        pasos: [
          recepcionLibre
            ? { icon: "mdi:account-arrow-right-outline", color: "#2563eb", titulo: "1. Definir origen", desc: "Seleccioná proveedor y depósito; no necesitás crear una Orden de Compra." }
            : { icon: "mdi:clipboard-check-outline", color: "#2563eb", titulo: "1. Elegir OC pendiente", desc: "Seleccioná una orden con saldo pendiente de recepción." },
          { icon: "mdi:package-variant-closed-check", color: "#f59e0b", titulo: "2. Registrar recibido real", desc: "Confirmá cantidades entregadas por el proveedor (puede ser parcial)." },
          {
            icon: "mdi:barcode-scan",
            color: "#0ea5e9",
            titulo: "3. Cargar productos",
            desc: recepcionLibre ? "Buscá por código, código de barras o descripción y definí la cantidad antes de agregar." : "Revisá las líneas pendientes y ajustá lo que realmente llegó.",
          },
          {
            icon: afectaStockEnCompraDirecta ? "mdi:file-document-check-outline" : "mdi:warehouse",
            color: "#16a34a",
            titulo: afectaStockEnCompraDirecta ? "4. Confirmar control documental" : "4. Confirmar ingreso de stock",
            desc: afectaStockEnCompraDirecta
              ? "La recepción queda registrada sin duplicar inventario; el stock se actualizará cuando se confirme la factura."
              : "Al confirmar, las cantidades ingresan al stock del depósito; la factura posterior no las duplica.",
          },
        ],
        notas: [
          recepcionLibre
            ? { icon: "mdi:information-outline", text: "La recepción libre queda identificada como LIBRE. Activá Órdenes de Compra si necesitás autorización y control previo." }
            : { icon: "mdi:alert-outline", text: "No se permite recibir más de la cantidad pendiente; ajustá primero la OC si corresponde." },
          afectaStockEnCompraDirecta
            ? { icon: "mdi:swap-horizontal", text: "Alternativa: desactivá «Afectar stock en compra directa» para centralizar el ingreso físico en esta pestaña." }
            : { icon: "mdi:information-outline", text: "Con la configuración actual, los productos deben pasar por Recepciones para ingresar al stock." },
          matching3ViasHabilitado && { icon: "mdi:shield-check-outline", text: "Matching 3 vías exigirá coherencia entre OC, cantidades recibidas y factura." },
        ].filter(Boolean),
      };
    }

    if (activeTab === "gastos" && canViewGastos) {
      return {
        titulo: "¿Cómo funcionan los gastos?",
        intro: "Registrá servicios, viáticos y otros costos operativos que no representan ingreso de mercadería al inventario.",
        configuracion: [
          { label: `Flujo: ${modoFlujoLabel}`, color: requiereRequisicionObligatoria ? "warning" : "info" },
          { label: cxpLabel, color: generaCxpAutomatico ? "success" : "default" },
          { label: ordenPagoLabel, color: ordenPagoHabilitada ? "info" : "default" },
        ],
        pasos: [
          {
            icon: "mdi:plus-circle-outline",
            color: "#2563eb",
            titulo: "1. Iniciar el gasto",
            desc: requiereRequisicionObligatoria
              ? "Crealo desde una requisición de origen Gasto previamente aprobada."
              : "Registralo directamente o derivalo desde una requisición aprobada cuando necesites control previo.",
          },
          { icon: "mdi:tag-outline", color: "#f59e0b", titulo: "2. Clasificar correctamente", desc: "Asigná tipo de gasto para ordenar reportes y análisis contable." },
          {
            icon: "mdi:cash-check",
            color: "#0ea5e9",
            titulo: "3. Definir pago o cuenta",
            desc: generaCxpAutomatico
              ? "En crédito se genera automáticamente la cuenta por pagar; en contado registrá el pago."
              : "En crédito deberás gestionar la cuenta por pagar manualmente; en contado registrá el pago.",
          },
          { icon: "mdi:chart-line", color: "#16a34a", titulo: "4. Controlar evolución", desc: "Usá reportes para comparar gastos por período, proveedor y categoría." },
        ],
        notas: [
          { icon: "mdi:information-outline", text: "Los gastos nunca deben utilizarse para ingresar productos al stock." },
          {
            icon: "mdi:file-document-check-outline",
            text:
              "Dejá tildado «Comprobante preimpreso» si es una factura de talonario en papel: son las que hay " +
              "que declarar en el Libro IVA Compras. Destildalo solo si es electrónica, porque esas ya le llegan " +
              "solas a la DNIT y volver a subirlas las duplica.",
          },
          {
            icon: "mdi:shield-check-outline",
            text:
              ordenCompraHabilitada && recepcionHabilitada
                ? "Si corresponde a mercadería, usá OC + Recepción + Factura."
                : recepcionHabilitada
                  ? "Si corresponde a mercadería, usá Recepción libre + Factura."
                  : "Si corresponde a mercadería, usá Facturas o activá Recepciones para separar el ingreso físico.",
          },
          requiereRequisicionObligatoria && { icon: "mdi:lock-outline", text: "La carga directa de gastos está restringida por el modo Requisición obligatoria." },
        ].filter(Boolean),
      };
    }

    if (activeTab === "notas_credito") {
      return {
        titulo: "¿Cómo funcionan las notas de crédito de compra?",
        intro:
          "La NC de compra registra lo que el proveedor te devuelve o descuenta después de haber facturado: " +
          "una devolución de mercadería, un descuento posterior o una corrección de precio.",
        configuracion: [
          { label: cxpLabel, color: generaCxpAutomatico ? "success" : "default" },
          { label: stockLabel, color: "info" },
        ],
        pasos: [
          {
            icon: "mdi:file-search-outline",
            color: "#2563eb",
            titulo: "1. Buscar la compra de origen",
            desc: "Partí de la factura que se está corrigiendo: la NC hereda proveedor, moneda e ítems, y así queda trazable.",
          },
          {
            icon: "mdi:numeric",
            color: "#0ea5e9",
            titulo: "2. Cargar el comprobante del proveedor",
            desc: "Timbrado y número tal como vienen impresos en la nota que te entregó el proveedor.",
          },
          {
            icon: "mdi:package-variant-closed",
            color: "#f59e0b",
            titulo: "3. Elegir qué se devuelve",
            desc: "Dejá sólo los ítems y cantidades que efectivamente vuelven. Si es un descuento sin devolución, no toques el stock.",
          },
          {
            icon: "mdi:cash-refund",
            color: "#16a34a",
            titulo: "4. Confirmar el efecto",
            desc: generaCxpAutomatico
              ? "Al guardar se descuenta el saldo de la cuenta por pagar del proveedor."
              : "Al guardar queda el crédito registrado; aplicalo manualmente contra la cuenta por pagar.",
          },
        ],
        notas: [
          {
            icon: "mdi:warehouse",
            text: "Si la NC es por devolución de mercadería, el depósito de salida es el que descuenta el stock. En un descuento sin devolución no corresponde mover inventario.",
          },
          { icon: "mdi:book-open-variant", text: "La NC resta IVA crédito en el Libro IVA Compras del período en que se emite." },
        ],
      };
    }

    if (activeTab === "tipos_gasto" && canViewGastos) {
      return {
        titulo: "¿Cómo funcionan los tipos de gasto?",
        intro: "Este catálogo define cómo vas a clasificar gastos para mantener orden operativo y contable.",
        configuracion: [
          { label: "Catálogo para Gastos", color: "info" },
          { label: cxpLabel, color: generaCxpAutomatico ? "success" : "default" },
          { label: `Flujo: ${modoFlujoLabel}`, color: requiereRequisicionObligatoria ? "warning" : "default" },
        ],
        pasos: [
          { icon: "mdi:shape-plus-outline", color: "#2563eb", titulo: "1. Crear categorías útiles", desc: "Definí tipos concretos (combustible, alquiler, mantenimiento, etc.)." },
          { icon: "mdi:link-variant", color: "#0ea5e9", titulo: "2. Vincular criterio contable", desc: "Completá datos que ayuden a mapear correctamente reportes y cuentas." },
          { icon: "mdi:pencil-outline", color: "#f59e0b", titulo: "3. Mantener catálogo", desc: "Editá/inactivá categorías obsoletas para evitar errores de carga." },
          { icon: "mdi:chart-donut", color: "#16a34a", titulo: "4. Reusar en operación diaria", desc: "Cada gasto usa este catálogo para reportes comparables entre períodos." },
        ],
        notas: [
          { icon: "mdi:check-decagram-outline", text: "Un catálogo bien definido facilita cierres y auditoría." },
          { icon: "mdi:account-group-outline", text: "Acordá nombres de categorías con el equipo para evitar duplicados similares." },
          { icon: "mdi:information-outline", text: "Este catálogo no decide si se usa OC o Recepción; solamente clasifica los registros de Gastos." },
        ],
      };
    }

    return {
      titulo: "¿Cómo funciona Facturas de Compra?",
      intro: requiereRequisicionObligatoria
        ? "Registrá la factura final del proveedor vinculándola al circuito aprobado de requisición y Orden de Compra."
        : "Registrá facturas directas o vinculadas a una Orden de Compra, según el control que necesite cada operación.",
      configuracion: [
        { label: `Flujo: ${modoFlujoLabel}`, color: requiereRequisicionObligatoria ? "warning" : "info" },
        { label: stockLabel, color: afectaStockEnCompraDirecta ? "success" : "warning" },
        { label: matching3ViasHabilitado ? "Matching 3 vías activo" : "Matching flexible", color: matching3ViasHabilitado ? "warning" : "default" },
        { label: cxpLabel, color: generaCxpAutomatico ? "success" : "default" },
      ],
      pasos: [
        {
          icon: "mdi:file-plus-outline",
          color: "#2563eb",
          titulo: "1. Iniciar nueva compra",
          desc: requiereRequisicionObligatoria
            ? "Iniciá desde una requisición aprobada y su OC; la factura directa está bloqueada."
            : "Entrá a «Nueva compra» y completá sucursal, proveedor, fecha, moneda y comprobante.",
        },
        {
          icon: "mdi:cart-outline",
          color: "#f59e0b",
          titulo: "2. Cargar ítems",
          desc: ordenCompraHabilitada
            ? "Agregá productos manualmente o vinculá una OC para heredar líneas y mantener trazabilidad."
            : "Agregá productos por código, código de barras o descripción; la empresa no utiliza Órdenes de Compra.",
        },
        { icon: "mdi:calculator-variant-outline", color: "#0ea5e9", titulo: "3. Revisar resumen fiscal", desc: "Validá total bruto, IVA 10/5, base gravada y total operación antes de guardar." },
        { icon: "mdi:cash-check", color: "#16a34a", titulo: "4. Definir pago o cuotas", desc: "Contado requiere pagos; crédito permite cuotas según la configuración de compras." },
        {
          icon: "mdi:content-save-check-outline",
          color: "#7c3aed",
          titulo: "5. Guardar y seguir",
          desc: generaCxpAutomatico
            ? "Al guardar una compra a crédito se genera su CxP; controlá luego los estados Pendiente, Parcial, Pagada o Anulada."
            : "Al guardar, controlá el estado y gestioná manualmente la cuenta por pagar cuando corresponda.",
        },
      ],
      notas: [
        requiereRequisicionObligatoria
          ? { icon: "mdi:link-variant", text: "Configuración actual: toda factura debe provenir de una requisición aprobada mediante OC." }
          : {
              icon: "mdi:source-branch",
              text: ordenCompraHabilitada
                ? "Alternativas: factura directa para casos simples u OC para control previo y trazabilidad."
                : "Alternativa: activá Órdenes de Compra si necesitás autorización y control antes de facturar.",
            },
        {
          icon: "mdi:file-document-check-outline",
          text:
            "Dejá tildado «Preimpreso» si la factura es de talonario en papel: son las que hay que declarar " +
            "en el Libro IVA Compras. Destildalo solo si es electrónica, porque esas ya le llegan solas a la " +
            "SET y volver a subirlas las duplica.",
        },
        {
          icon: "mdi:warehouse",
          text: afectaStockEnCompraDirecta
            ? "El stock se actualiza al confirmar la factura; una recepción previa queda como control y no duplica inventario."
            : recepcionHabilitada
              ? ordenCompraHabilitada
                ? "La factura no mueve stock: primero registrá la recepción contra OC."
                : "La factura no mueve stock: registrá una recepción libre para ingresar la mercadería."
              : "La factura no mueve stock y Recepciones está inactiva; activá Recepciones para ingresar inventario.",
        },
        matching3ViasHabilitado && { icon: "mdi:shield-check-outline", text: "Matching 3 vías exige que cada línea facturada coincida con la OC y con lo recibido." },
        {
          icon: "mdi:airplane-takeoff",
          text:
            "Compra del exterior / documento aduanero: no hay un «tipo de comprobante: documento aduanero». " +
            "Para importar mercadería general y que entre al stock, cargala como compra directa normal con el " +
            "proveedor del exterior, elegí el Depósito Inventario y cargá los ítems del catálogo (el invoice extranjero " +
            "va en Número Factura). El despacho aduanero formal con prorrateo de costos solo existe en el módulo " +
            "Importaciones y hoy aplica únicamente a autos importados.",
        },
        { icon: "mdi:cog-outline", text: "Podés cambiar estas alternativas en Configuración > Compras; la guía se actualizará automáticamente por empresa." },
      ].filter(Boolean),
    };
  }, [
    activeTab,
    afectaStockEnCompraDirecta,
    canViewGastos,
    flowUiState.modoFlujoCompra,
    generaCxpAutomatico,
    matching3ViasHabilitado,
    ordenCompraHabilitada,
    ordenPagoHabilitada,
    pagoParcialHabilitado,
    recepcionHabilitada,
    requiereAprobacion,
    requiereRequisicionObligatoria,
    requisicionHabilitada,
    showForm,
    workflowHabilitado,
    workflowObligatorio,
  ]);

  // La búsqueda es server-side (getCompras manda `search`), así que el listado se
  // usa tal cual viene del backend. No filtrar client-side (perdía resultados de
  // otras páginas/períodos).
  const comprasFiltradas = compras;

  // KPIs: vienen del agregado del backend sobre TODO el filtro (no la página).
  // Se setean en loadCompras a partir de resp.stats / resp.montosPorMoneda.
  const totalPages = useMemo(() => Math.max(1, Math.ceil(parseNumber(total, 0) / parseNumber(limit, 20))), [total, limit]);

  const filtrosActivos = useMemo(() => [searchText, estadoFilter, fechaDesde, fechaHasta].filter(Boolean).length, [searchText, estadoFilter, fechaDesde, fechaHasta]);
  const actionIconSx = (colorKey = "text.secondary") => ({
    width: 30,
    height: 30,
    border: "1px solid",
    borderColor: "divider",
    borderRadius: 1,
    color: colorKey,
    "&:hover": {
      borderColor: colorKey,
      backgroundColor: "action.hover",
    },
    "&.Mui-disabled": {
      opacity: 0.45,
    },
  });

  const loadCompras = async () => {
    const requestId = ++comprasRequestIdRef.current;
    try {
      setLoading(true);
      // Al buscar por texto se busca en todo el historial → ignorar el rango de
      // fechas del filtro rápido (si no, "000021" en "Mes anterior" no aparece).
      const buscando = Boolean(searchDebounced.trim());
      const resp = await getCompras({
        page,
        limit,
        search: buscando ? searchDebounced.trim() : undefined,
        estado: estadoFilter || undefined,
        fecha_desde: buscando ? undefined : fechaDesde || undefined,
        fecha_hasta: buscando ? undefined : fechaHasta || undefined,
      });
      if (requestId !== comprasRequestIdRef.current) return;
      setCompras(resp.items || []);
      setTotal(resp.total || 0);
      setEstadoStats(resp.stats || { total: resp.total || 0, pendiente: 0, parcial: 0, pagada: 0, anulada: 0 });
      setTotalesPorMoneda(resp.montosPorMoneda || {});
    } catch (err) {
      if (requestId !== comprasRequestIdRef.current) return;
      console.error(err);
      toast.error("No se pudo cargar compras");
    } finally {
      if (requestId === comprasRequestIdRef.current) setLoading(false);
    }
  };

  const loadLookups = async () => {
    try {
      const [refs, mediosResp, sucursalesResp, empresaMonedasResp, comprasMonedaConfigResp] = await Promise.all([
        getReferenciales(),
        getMediosPagoActivos(),
        getSucursales(1, 200),
        getEmpresaMonedas(),
        getComprasConfigMoneda().catch(() => []),
      ]);

      setSucursales(toArray(sucursalesResp));

      const data = refs?.data ? refs.data : refs;
      setMonedas(normalizeEmpresaMonedas(toArray(empresaMonedasResp)));
      setComprasMonedaConfigs(toArray(comprasMonedaConfigResp));
      setCondicionesOperacion(Array.isArray(data?.condicion_operacion) ? data.condicion_operacion : []);
      setMediosPago(Array.isArray(mediosResp?.data) ? mediosResp.data : Array.isArray(mediosResp) ? mediosResp : []);
    } catch (err) {
      console.error(err);
      toast.error("No se pudieron cargar datos auxiliares");
    }
  };

  // Debounce del buscador (400 ms). Al cambiar el texto se vuelve a la página 1.
  useEffect(() => {
    const t = setTimeout(() => {
      setSearchDebounced(searchText);
      setPage(1);
    }, 400);
    return () => clearTimeout(t);
  }, [searchText]);

  useEffect(() => {
    loadCompras();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [page, limit, estadoFilter, fechaDesde, fechaHasta, searchDebounced]);

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

  useEffect(() => {
    let cancelled = false;
    setFlowConfigReady(false);

    const loadFlowConfig = async () => {
      try {
        const config = await getConfigComprasFlujo();
        if (cancelled) return;
        setFlowConfig(normalizeFlowConfig(config));
      } catch (error) {
        if (!cancelled) {
          setFlowConfig(DEFAULT_FLOW_CONFIG);
        }
      } finally {
        if (!cancelled) {
          setFlowConfigReady(true);
        }
      }
    };

    loadFlowConfig();
    return () => {
      cancelled = true;
    };
  }, [user?.empresa_id]);

  useEffect(() => {
    if (!showForm || sucursales.length === 0) return;

    setForm((prev) => {
      if (prev.cabecera.sucursal_id) return prev;
      const sucursalDefault = sucursales.find((s) => s.id === user?.sucursal_id) || sucursales[0];
      if (!sucursalDefault?.id) return prev;

      return {
        ...prev,
        cabecera: {
          ...prev.cabecera,
          sucursal_id: sucursalDefault.id,
        },
      };
    });
  }, [showForm, sucursales, user?.sucursal_id]);

  // Recién con la config cargada: antes, todo figura deshabilitado y un deep link
  // (?tab=pagos_proveedor desde el plan de pagos, por ejemplo) caía en Facturas.
  useEffect(() => {
    if (!flowConfigReady) return;
    setActiveTab((prev) => resolveComprasActiveTab(prev, flowUiState));
  }, [flowUiState, flowConfigReady]);

  useEffect(() => {
    if (!showForm) return;
    const sucursalId = form.cabecera.sucursal_id;
    if (!sucursalId) return;

    let cancelled = false;
    const loadConfigBySucursal = async () => {
      try {
        const [comprasConfigResp, lotesStatus] = await Promise.all([getComprasConfig(sucursalId).catch(() => ({})), getLotesStatus(sucursalId).catch(() => ({ enabled: false }))]);
        if (cancelled) return;
        setCreditoRequiereCuotas(Boolean(comprasConfigResp?.compras_credito_requiere_cuotas));
        setLotesEnabled(Boolean(lotesStatus?.enabled));
      } catch (error) {
        console.error(error);
        if (!cancelled) {
          setCreditoRequiereCuotas(false);
          setLotesEnabled(false);
        }
      }
    };

    loadConfigBySucursal();

    return () => {
      cancelled = true;
    };
  }, [showForm, form.cabecera.sucursal_id]);

  useEffect(() => {
    if (!showForm || monedas.length === 0) return;

    setForm((prev) => {
      if (prev.cabecera.moneda_id) return prev;
      const monedaDefault = monedas.find((m) => m.es_principal && m.activo !== false) || monedas[0];
      if (!monedaDefault?.id) return prev;

      return {
        ...prev,
        cabecera: {
          ...prev.cabecera,
          moneda_id: monedaDefault.id,
        },
      };
    });
  }, [showForm, monedas]);

  useEffect(() => {
    if (!showForm || condicionesOperacion.length === 0) return;
    if (editingId) return;

    setForm((prev) => {
      if (prev.cabecera.condicion_operacion_id) return prev;

      const condicionCredito = condicionesOperacion.find((condicion) => Number(condicion?.codigo) === 2) || null;
      if (!condicionCredito?.id) return prev;

      return {
        ...prev,
        cabecera: {
          ...prev.cabecera,
          condicion_operacion_id: condicionCredito.id,
        },
      };
    });
  }, [showForm, condicionesOperacion, editingId]);

  // Aviso de comprobante duplicado (compra o gasto). Debounced; no bloquea el guardado.
  const cabNumero = form.cabecera.numero_factura;
  const cabTimbrado = form.cabecera.timbrado_proveedor;
  const cabEst = form.cabecera.establecimiento;
  const cabPunto = form.cabecera.punto_expedicion;
  useEffect(() => {
    const numero = String(cabNumero || "").trim();
    const timbrado = String(cabTimbrado || "").trim();
    if (!showForm || !numero || !timbrado) {
      setDupComprobante(null);
      return;
    }
    let cancelled = false;
    const t = setTimeout(async () => {
      try {
        const res = await verificarComprobanteDuplicado({
          numero_factura: numero,
          timbrado,
          establecimiento: String(cabEst || "").trim(),
          punto_expedicion: String(cabPunto || "").trim(),
          excluir_id: editingId || undefined,
        });
        if (!cancelled) setDupComprobante(res?.existe ? res : null);
      } catch {
        if (!cancelled) setDupComprobante(null);
      }
    }, 500);
    return () => {
      cancelled = true;
      clearTimeout(t);
    };
  }, [showForm, editingId, cabNumero, cabTimbrado, cabEst, cabPunto]);

  // Se cargan sólo con el formulario abierto: es una lista chica y no hace falta
  // pedirla mientras se mira el listado de compras.
  useEffect(() => {
    if (!showForm) return;
    let cancelado = false;
    (async () => {
      try {
        const data = await getImportaciones({ limit: 100 });
        const lista = data?.data ?? data ?? [];
        if (cancelado) return;
        setEmbarquesImputables(
          (Array.isArray(lista) ? lista : []).filter(
            (e) => !["CERRADO", "SELLADO"].includes(String(e?.estado)),
          ),
        );
      } catch {
        // El módulo de importaciones puede no estar activo en esta empresa: el
        // selector simplemente no se muestra.
        if (!cancelado) setEmbarquesImputables([]);
      }
    })();
    return () => {
      cancelado = true;
    };
  }, [showForm]);

  useEffect(() => {
    if (!showForm) return;

    let isCancelled = false;

    const loadDepositosBySucursal = async () => {
      try {
        const sucursalId = form.cabecera.sucursal_id || null;

        const depositosSucursal = sucursalId ? toArray(await getDepositos(sucursalId)) : [];
        const depositosEmpresa = !sucursalId || depositosSucursal.length === 0 ? toArray(await getDepositos()) : [];
        const depositosResolved = depositosSucursal.length > 0 ? depositosSucursal : depositosEmpresa;

        let depositoPrincipal = null;
        if (sucursalId) {
          try {
            depositoPrincipal = await getDepositoPrincipal(sucursalId);
          } catch {
            depositoPrincipal = null;
          }
        }

        if (isCancelled) return;
        setDepositos(depositosResolved);

        setForm((prev) => {
          const currentDepositoId = prev.cabecera.deposito_id;
          const selectedStillExists = !!currentDepositoId && depositosResolved.some((deposito) => deposito.id === currentDepositoId);
          if (selectedStillExists) return prev;

          const depositoPrincipalEnLista = depositoPrincipal ? depositosResolved.find((deposito) => deposito.id === depositoPrincipal.id) : null;
          const depositoDefault = depositoPrincipalEnLista || depositosResolved.find((deposito) => Boolean(deposito.es_principal)) || depositosResolved[0];

          return {
            ...prev,
            cabecera: {
              ...prev.cabecera,
              deposito_id: depositoDefault?.id || "",
            },
          };
        });
      } catch (error) {
        console.error(error);
        if (!isCancelled) {
          toast.error("No se pudieron cargar depósitos");
        }
      }
    };

    loadDepositosBySucursal();

    return () => {
      isCancelled = true;
    };
  }, [showForm, form.cabecera.sucursal_id]);

  useEffect(() => {
    if (!isCredito) return;
    if (form.pagos.length === 0) return;

    setForm((prev) => ({ ...prev, pagos: [] }));
  }, [isCredito, form.pagos.length]);

  useEffect(() => {
    if (!showForm || !ordenCompraHabilitada) return;

    let cancelled = false;
    const loadOrdenesFacturables = async () => {
      try {
        setLoadingOrdenesFacturables(true);
        const resp = await getOrdenesCompra({ page: 1, limit: 100, estado: "parcial" });
        if (cancelled) return;
        const data = Array.isArray(resp?.data) ? resp.data : [];
        setOrdenesFacturables(data.filter((orden) => hasPendienteFacturaEnOrden(orden)));
      } catch (error) {
        console.error(error);
        toast.error("No se pudieron cargar órdenes de compra para vincular", {
          id: "compras-ordenes-vinculo-load-error",
        });
      } finally {
        if (!cancelled) setLoadingOrdenesFacturables(false);
      }
    };

    loadOrdenesFacturables();
    return () => {
      cancelled = true;
    };
  }, [showForm, ordenCompraHabilitada]);

  useEffect(() => {
    if (!isCredito) {
      if (usarCuotasManuales) setUsarCuotasManuales(false);
      return;
    }

    if (creditoRequiereCuotas && !usarCuotasManuales) {
      setUsarCuotasManuales(true);
    }

    setForm((prev) => {
      const plazoActual = Math.trunc(parseNumber(prev.cabecera.plazo_credito, 0));
      if (plazoActual > 0) return prev;

      return {
        ...prev,
        cabecera: {
          ...prev.cabecera,
          plazo_credito: 30,
        },
      };
    });
  }, [isCredito, usarCuotasManuales, creditoRequiereCuotas]);

  useEffect(() => {
    if (isCredito) return;
    if (!form.cabecera.moneda_id) return;
    if (form.pagos.length === 0) return;

    setForm((prev) => {
      const shouldSync = prev.pagos.some((pago) => pago.moneda_id !== prev.cabecera.moneda_id);
      if (!shouldSync) return prev;

      return {
        ...prev,
        pagos: prev.pagos.map((pago) => ({
          ...pago,
          moneda_id: prev.cabecera.moneda_id,
        })),
      };
    });
  }, [isCredito, form.cabecera.moneda_id, form.pagos.length]);

  const obligacionesFiscales = useObligacionesFiscalesQuery().data;
  // Sin timbrado ni número de la SET: proveedor del exterior o comprobante del exterior.
  const esProveedorExterior =
    selectedProveedor?.tipo_entidad === "PROVEEDOR_EXTERIOR" ||
    selectedProveedor?.es_extranjero === true ||
    form.cabecera.tipo_comprobante === TIPOS_COMPROBANTE_COMPRA.COMPROBANTE_EXTERIOR.value;

  const updateCabecera = (field, value) => {
    // FASE 3 — Proveedor exterior: los campos fiscales aceptan invoice text libre.
    const bypassFiscalNormalize = esProveedorExterior && CABECERA_FIELD_RULES[field] && field !== "observaciones";
    // El zero-pad NO va acá: rellenar mientras se tipea deja el campo en su
    // largo exacto con la primera tecla, y a partir de ahí `maxLength` descarta
    // todo lo que siga (tipear "1234" en Nº de factura quedaba en "0000001", y
    // un backspace lo dejaba en "0000000", imposible de corregir).
    // El relleno ocurre al salir del campo —`ComprobanteProveedorFields` llama a
    // `onCampoChange` con el valor ya rellenado en su `onBlur`— y de nuevo en
    // `handleSubmit` vía `getCabeceraZeroPaddedValues` como red de seguridad.
    const normalizedValue = bypassFiscalNormalize ? String(value ?? "").slice(0, 30) : normalizeCabeceraInput(field, value);
    setForm((prev) => ({
      ...prev,
      cabecera: {
        ...prev.cabecera,
        [field]: normalizedValue,
      },
    }));
  };


  const handleSelectProveedor = (proveedor) => {
    setSelectedProveedor(proveedor || null);
    updateCabecera("proveedor_id", proveedor?.id || "");
    // Precargar el timbrado del proveedor (rara vez cambia). No pisa lo ya tipeado.
    if (proveedor?.timbrado && !form.cabecera.timbrado_proveedor) {
      updateCabecera("timbrado_proveedor", proveedor.timbrado);
      if (proveedor.fecha_vencimiento_timbrado && !form.cabecera.fecha_vencimiento_timbrado) {
        updateCabecera("fecha_vencimiento_timbrado", String(proveedor.fecha_vencimiento_timbrado).split("T")[0]);
      }
    }
  };

  const clearOrdenCompraVinculo = () => {
    setLinkedOrdenNumero("");
    setForm((prev) => ({
      ...prev,
      cabecera: { ...prev.cabecera, orden_compra_id: "" },
      items: prev.items.map((item) => ({ ...item, orden_compra_det_id: "" })),
    }));
  };

  const updateItem = (index, field, value) => {
    setForm((prev) => {
      const items = [...prev.items];
      items[index] = enrichCompraItem({ ...items[index], [field]: value }, roundingConfig);
      return { ...prev, items };
    });
  };

  const handleAddItemFromSearch = (item) => {
    setForm((prev) => ({
      ...prev,
      items: [...prev.items, enrichCompraItem(item, roundingConfig)],
    }));
  };

  useEffect(() => {
    setForm((prev) => {
      if (prev.items.length === 0) return prev;
      return {
        ...prev,
        items: prev.items.map((item) => enrichCompraItem(item, roundingConfig)),
      };
    });
  }, [roundingConfig]);

  const ordenesFacturablesConSeleccion = useMemo(() => {
    if (!String(form.cabecera.orden_compra_id || "").trim()) return ordenesFacturables;
    if (ordenesFacturables.some((oc) => oc.id === form.cabecera.orden_compra_id)) return ordenesFacturables;
    return [
      {
        id: form.cabecera.orden_compra_id,
        numero_orden: linkedOrdenNumero || "OC vinculada",
        proveedores: selectedProveedor
          ? {
              personas: {
                razon_social: selectedProveedor?.personas?.razon_social || selectedProveedor?.razon_social || "Proveedor",
              },
            }
          : null,
        fecha_emision: null,
      },
      ...ordenesFacturables,
    ];
  }, [form.cabecera.orden_compra_id, linkedOrdenNumero, ordenesFacturables, selectedProveedor]);

  const selectedOrdenFacturable = useMemo(
    () => ordenesFacturablesConSeleccion.find((orden) => orden.id === form.cabecera.orden_compra_id) || null,
    [ordenesFacturablesConSeleccion, form.cabecera.orden_compra_id],
  );

  const removeItem = (index) => {
    setForm((prev) => ({ ...prev, items: prev.items.filter((_, i) => i !== index) }));
  };

  const addPago = () => {
    setForm((prev) => ({
      ...prev,
      pagos: [
        ...prev.pagos,
        {
          medio_pago_id: "",
          moneda_id: prev.cabecera.moneda_id || "",
          monto: 0,
          referencia: "",
        },
      ],
    }));
  };

  const updatePago = (index, field, value) => {
    setForm((prev) => {
      const pagos = [...prev.pagos];
      pagos[index] = { ...pagos[index], [field]: value };
      return { ...prev, pagos };
    });
  };

  const removePago = (index) => {
    setForm((prev) => ({
      ...prev,
      pagos: prev.pagos.filter((_, i) => i !== index),
    }));
  };

  const addCuota = () => {
    setForm((prev) => ({
      ...prev,
      cuotas: [
        ...prev.cuotas,
        {
          numero_cuota: prev.cuotas.length + 1,
          fecha_vencimiento: hoyISO(),
          monto: 0,
          observaciones: "",
        },
      ],
    }));
  };

  const updateCuota = (index, field, value) => {
    setForm((prev) => {
      const cuotas = [...prev.cuotas];
      cuotas[index] = { ...cuotas[index], [field]: value };
      return { ...prev, cuotas };
    });
  };

  const removeCuota = (index) => {
    setForm((prev) => ({
      ...prev,
      cuotas: prev.cuotas.filter((_, i) => i !== index),
    }));
  };

  const buildPayload = (cabeceraData = form.cabecera) => ({
    // Cabecera construida por whitelist explícita (sin spread) para que ningún
    // campo ajeno al DTO del backend (que valida con forbidNonWhitelisted) se
    // cuele y dispare un 400 "property X should not exist".
    cabecera: {
      proveedor_id: cabeceraData.proveedor_id || undefined,
      deposito_id: cabeceraData.deposito_id || undefined,
      sucursal_id: cabeceraData.sucursal_id || undefined,
      requisicion_id: cabeceraData.requisicion_id || undefined,
      orden_compra_id: cabeceraData.orden_compra_id || undefined,
      embarque_id: cabeceraData.embarque_id || undefined,
      // Sin embarque estos dos no significan nada y el DTO los rechaza.
      concepto_costo_id: cabeceraData.embarque_id
        ? cabeceraData.concepto_costo_id || undefined
        : undefined,
      convertir_componente_id: cabeceraData.embarque_id
        ? cabeceraData.convertir_componente_id || undefined
        : undefined,
      condicion_operacion_id: cabeceraData.condicion_operacion_id || undefined,
      plazo_credito: isCredito ? Math.max(1, Math.trunc(parseNumber(cabeceraData.plazo_credito, 30))) : undefined,
      moneda_id: cabeceraData.moneda_id || undefined,
      cotizacion: parseNumber(cabeceraData.cotizacion, 1) || 1,
      fecha_emision: cabeceraData.fecha_emision || undefined,
      fecha_vencimiento_timbrado: cabeceraData.fecha_vencimiento_timbrado || undefined,
      timbrado_proveedor: cabeceraData.timbrado_proveedor || undefined,
      establecimiento: cabeceraData.establecimiento || undefined,
      punto_expedicion: cabeceraData.punto_expedicion || undefined,
      numero_factura: cabeceraData.numero_factura || undefined,
      es_preimpreso: TIPOS_TIMBRADO[cabeceraData.tipo_timbrado]?.declara !== false,
      tipo_timbrado: cabeceraData.tipo_timbrado || TIPOS_TIMBRADO.PREIMPRESO.value,
      cdc: String(cabeceraData.cdc || "").trim() || null,
      tipo_comprobante: cabeceraData.tipo_comprobante || TIPOS_COMPROBANTE_COMPRA.FACTURA.value,
      imputa_iva: cabeceraData.imputa_iva ?? null,
      imputa_ire: cabeceraData.imputa_ire ?? null,
      imputa_irp_rsp: cabeceraData.imputa_irp_rsp ?? null,
      no_imputa: Boolean(cabeceraData.no_imputa),
      observaciones: cabeceraData.observaciones || undefined,
      descuento_tipo: cabeceraData.descuento_tipo || "por_item",
      descuento_global_pct: cabeceraData.descuento_tipo === "global_pct" ? parseNumber(cabeceraData.descuento_global_pct, 0) : 0,
      descuento_global_monto: cabeceraData.descuento_tipo === "global_monto" ? parseNumber(cabeceraData.descuento_global_monto, 0) : 0,
    },
    items: form.items.map((item) => {
      const requiresLote = itemRequiresLote(item, lotesEnabled);
      return {
        requisicion_det_id: item.requisicion_det_id || undefined,
        orden_compra_det_id: item.orden_compra_det_id || undefined,
        producto_id: item.producto_id || undefined,
        descripcion: item.descripcion,
        cantidad: parseNumber(item.cantidad, 0),
        precio_unitario: parseNumber(item.precio_unitario, 0),
        descuento_tipo: item.descuento_tipo === "monto" ? "monto" : "porcentaje",
        descuento_porcentaje: parseNumber(item.descuento_pct, 0),
        descuento_monto: parseNumber(item.descuento_monto, 0),
        porcentaje_iva: parseNumber(item.porcentaje_iva, 10),
        afecta_stock: Boolean(item.afecta_stock),
        numero_lote: requiresLote ? String(item.numero_lote || item.lote || "").trim() || undefined : undefined,
        fecha_fabricacion: requiresLote && item.fecha_fabricacion ? String(item.fecha_fabricacion).slice(0, 10) : undefined,
        fecha_vencimiento: requiresLote && item.fecha_vencimiento ? String(item.fecha_vencimiento).slice(0, 10) : undefined,
      };
    }),
    pagos: !isCredito
      ? form.pagos
          .filter((p) => p.medio_pago_id && parseNumber(p.monto, 0) > 0)
          .map((p) => ({
            medio_pago_id: p.medio_pago_id || undefined,
            moneda_id: cabeceraData.moneda_id || p.moneda_id || undefined,
            monto: parseNumber(p.monto, 0),
            referencia: p.referencia || undefined,
          }))
      : [],
    cuotas:
      isCredito && (usarCuotasManuales || creditoRequiereCuotas)
        ? form.cuotas
            .filter((c) => c.fecha_vencimiento && parseNumber(c.monto, 0) > 0)
            .map((c, idx) => ({
              numero_cuota: parseNumber(c.numero_cuota, idx + 1),
              fecha_vencimiento: c.fecha_vencimiento,
              monto: parseNumber(c.monto, 0),
              observaciones: c.observaciones || undefined,
            }))
        : [],
  });

  const clearForm = () => {
    setEditingId(null);
    setForm(emptyForm);
    setSelectedProveedor(null);
    setUsarCuotasManuales(false);
    setLinkedOrdenNumero("");
    setLinkedRequisicionNumero("");
  };

  const cargarOrdenEnFactura = async (ordenInput) => {
    try {
      const orden = ordenInput?.orden_compra_det ? ordenInput : await getOrdenCompra(ordenInput?.id || ordenInput);
      if (!orden?.id) {
        toast.error("No se pudo cargar la orden seleccionada");
        return;
      }

      const detallesPendientes = (orden.orden_compra_det || []).filter((det) => parseNumber(det.cantidad_pendiente_factura, 0) > 0.0001);

      if (detallesPendientes.length === 0) {
        toast.warning("La orden no tiene líneas pendientes para facturación");
        return;
      }

      const itemsFromOrden = detallesPendientes.map((det) =>
        enrichCompraItem(
          {
            requisicion_det_id: det.requisicion_det_id || "",
            orden_compra_det_id: det.id,
            producto_id: det.producto_id || "",
            cod_producto: det?.productos?.cod_producto || det?.productos?.codigo_barra || "S/C",
            descripcion: det?.productos?.descripcion || det.descripcion || "",
            cantidad: parseNumber(det.cantidad_pendiente_factura, 0),
            precio_unitario: parseNumber(det.precio_unitario, 0),
            descuento_tipo: "porcentaje",
            descuento_pct: 0,
            porcentaje_iva: parseNumber(det.porcentaje_iva, 10),
            afecta_stock: true,
            maneja_lote: Boolean(det?.productos?.maneja_lote),
          },
          roundingConfig,
        ),
      );

      setActiveTab("facturas");
      setEditingId(null);
      setCreateValidationMessages([]);
      setSelectedProveedor(orden.proveedores || null);
      setUsarCuotasManuales(false);
      setForm((prev) => ({
        ...prev,
        cabecera: {
          ...prev.cabecera,
          proveedor_id: orden.proveedor_id || "",
          sucursal_id: orden.sucursal_id || prev.cabecera.sucursal_id,
          moneda_id: orden.moneda_id || prev.cabecera.moneda_id,
          requisicion_id: orden.requisicion_id || prev.cabecera.requisicion_id || "",
          orden_compra_id: orden.id,
          fecha_emision: hoyISO(),
        },
        items: itemsFromOrden,
        pagos: [],
        cuotas: [],
      }));
      const ordenNumero = String(orden.numero_orden || "").trim();
      let requisicionNumero = String(orden?.requisicion_compra_cab?.numero_requisicion || "").trim();
      if (!requisicionNumero && orden?.requisicion_id) {
        try {
          const req = await getRequisicion(orden.requisicion_id);
          requisicionNumero = String(req?.numero_requisicion || "").trim();
        } catch (_) {
          // no-op: mantenemos fallback visual amigable
        }
      }
      setLinkedOrdenNumero(ordenNumero);
      setLinkedRequisicionNumero(requisicionNumero);
      setShowForm(true);
      window.scrollTo({ top: 0, behavior: "smooth" });
    } catch (error) {
      console.error(error);
      toast.error("No se pudo cargar la orden para facturar");
    }
  };

  /**
   * Deep-link desde Importaciones: /compras?convertir=<componente>&embarque=<id>
   *
   * Es la regularización del FOB cargado a mano: abre el formulario con lo que el
   * componente sí guarda (proveedor, nº de factura, importe, moneda) y deja para
   * completar lo que no guarda: timbrado, fecha y los ítems. Al guardar, la
   * compra toma ese componente en vez de crear otro, así el costeo no se infla.
   */
  useEffect(() => {
    const convertir = searchParams.get("convertir");
    const embarque = searchParams.get("embarque");
    if (!convertir || !embarque || convertirRef.current) return;
    convertirRef.current = true;

    setActiveTab("facturas");
    setForm((prev) => ({
      ...prev,
      cabecera: {
        ...prev.cabecera,
        embarque_id: embarque,
        convertir_componente_id: convertir,
        proveedor_id: searchParams.get("proveedor") || prev.cabecera.proveedor_id,
        numero_factura: searchParams.get("factura") || prev.cabecera.numero_factura,
        moneda_id: searchParams.get("moneda") || prev.cabecera.moneda_id,
      },
    }));
    setShowForm(true);
    toast.info("Completá el timbrado, la fecha y los ítems de la factura del exterior.");

    const limpio = new URLSearchParams(searchParams);
    ["convertir", "embarque", "proveedor", "factura", "importe", "moneda"].forEach((k) =>
      limpio.delete(k),
    );
    setSearchParams(limpio, { replace: true });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [searchParams]);

  const handleNuevaCompra = () => {
    if (requiereRequisicionObligatoria) {
      toast.warning("La empresa opera con requisición obligatoria. Facturá desde una OC originada en requisición aprobada.");
      return;
    }
    setActiveTab("facturas");
    clearForm();
    setCreateValidationMessages([]);
    setShowForm(true);
  };

  const closeForm = () => {
    clearForm();
    setShowForm(false);
    setCreateConfirmOpen(false);
    setPendingCreatePayload(null);
    setCreateValidationMessages([]);
  };

  const closeDetalleDialog = () => {
    setDetalleDialog({
      open: false,
      loading: false,
      compra: null,
    });
  };

  const handleVerDetalle = async (id) => {
    if (!id) return;
    setDetalleDialog({
      open: true,
      loading: true,
      compra: null,
    });
    try {
      const compra = await getCompra(id);
      setDetalleDialog({
        open: true,
        loading: false,
        compra: compra || null,
      });
    } catch (err) {
      console.error(err);
      toast.error("No se pudo cargar el detalle de la compra");
      setDetalleDialog({
        open: true,
        loading: false,
        compra: null,
      });
    }
  };

  const handleFooterSecondaryAction = () => {
    if (editingId) {
      closeForm();
      return;
    }

    const sucursalDefault = sucursales.find((s) => s.id === user?.sucursal_id) || sucursales[0];
    const monedaDefault = monedas.find((m) => m.es_principal && m.activo !== false) || monedas[0];
    const condicionCredito = condicionesOperacion.find((c) => Number(c?.codigo) === 2) || null;
    const depositoDefault = depositos.find((d) => Boolean(d.es_principal)) || depositos[0];

    setEditingId(null);
    setSelectedProveedor(null);
    setUsarCuotasManuales(false);
    setLinkedOrdenNumero("");
    setLinkedRequisicionNumero("");
    setCreateConfirmOpen(false);
    setPendingCreatePayload(null);
    setCreateValidationMessages([]);

    setForm({
      ...emptyForm,
      cabecera: {
        ...emptyForm.cabecera,
        fecha_emision: hoyISO(),
        sucursal_id: sucursalDefault?.id || "",
        moneda_id: monedaDefault?.id || "",
        condicion_operacion_id: condicionCredito?.id || "",
        deposito_id: depositoDefault?.id || "",
      },
    });
  };

  const closeCreateConfirm = () => {
    if (saving) return;
    setCreateConfirmOpen(false);
    setPendingCreatePayload(null);
    setCreateValidationMessages([]);
  };

  const persistCompra = async (payload, isEdit) => {
    const loadingToastId = !isEdit ? toast.loading("Guardando compra...") : null;
    try {
      setSaving(true);
      let response = null;
      if (isEdit) {
        response = await updateCompra(editingId, payload);
      } else {
        response = await createCompra(payload);
      }
      if (loadingToastId) toast.dismiss(loadingToastId);
      toast.success(response?.message || (isEdit ? "Compra actualizada" : "Compra registrada"));
      await loadCompras();
      closeForm();
    } catch (err) {
      if (loadingToastId) toast.dismiss(loadingToastId);
      console.error(err);
      const isTimeout = err?.code === "ECONNABORTED";
      const fallbackMessage = isTimeout ? "La operación tardó demasiado en responder. Verificá conexión/servidor y volvé a intentar." : "No se pudo guardar la compra";
      const messages = isTimeout ? [fallbackMessage] : extractApiErrorMessages(err, fallbackMessage);
      const message = messages[0];

      setCreateValidationMessages(messages);

      if (err?.response?.status === 400 && !isTimeout) toast.warning(message);
      else toast.error(message);
    } finally {
      setSaving(false);
    }
  };

  const handleConfirmCreate = async () => {
    if (!pendingCreatePayload) return;
    await persistCompra(pendingCreatePayload, false);
  };

  const aplicarFiltroRapido = (tipo) => {
    const today = new Date();
    let from = null;
    let to = null;

    if (tipo === "hoy") {
      from = new Date(today);
      to = new Date(today);
    } else if (tipo === "ayer") {
      from = new Date(today);
      from.setDate(from.getDate() - 1);
      to = new Date(from);
    } else if (tipo === "semana") {
      from = getStartOfWeek(today);
      to = new Date(today);
    } else if (tipo === "mes") {
      from = new Date(today.getFullYear(), today.getMonth(), 1);
      to = new Date(today);
    } else if (tipo === "mes_anterior") {
      from = new Date(today.getFullYear(), today.getMonth() - 1, 1);
      to = new Date(today.getFullYear(), today.getMonth(), 0);
    }

    setFiltroRapido(tipo);
    setPage(1);
    setFechaDesde(from ? toIsoDate(from) : "");
    setFechaHasta(to ? toIsoDate(to) : "");
  };

  const limpiarFiltros = () => {
    setSearchText("");
    setSearchDebounced("");
    setEstadoFilter("");
    setFechaDesde("");
    setFechaHasta("");
    setFiltroRapido("");
    setPage(1);
  };

  const handleSubmit = async (event) => {
    event?.preventDefault?.();
    event?.stopPropagation?.();
    try {
      setCreateValidationMessages([]);
      // Para exterior no aplica el zero-pad (invoice text puede contener letras/guiones).
      const paddedCabeceraValues = esProveedorExterior ? {} : getCabeceraZeroPaddedValues(form.cabecera);
      const cabeceraForSubmit = {
        ...form.cabecera,
        ...paddedCabeceraValues,
      };

      setForm((prev) => {
        const nextCabecera = {
          ...prev.cabecera,
          ...paddedCabeceraValues,
        };
        const changed = Object.keys(paddedCabeceraValues).some((field) => nextCabecera[field] !== prev.cabecera[field]);
        if (!changed) return prev;
        return { ...prev, cabecera: nextCabecera };
      });

      const validationErrors = [];
      const addValidationError = (message) => {
        if (!validationErrors.includes(message)) validationErrors.push(message);
      };

      // FASE 3 — Proveedor exterior: timbrado/establecimiento/pto exp/nro factura
      // no son campos SET (Paraguay). Se aceptan libres — sólo se pide numero_factura
      // no vacío para trazabilidad interna.
      if (!esProveedorExterior) {
        CABECERA_REQUIRED_FIELDS.forEach((field) => {
          const value = String(cabeceraForSubmit[field] || "").trim();
          if (!value) {
            const label = CABECERA_FIELD_RULES[field]?.label || field;
            addValidationError(`${label} es obligatorio`);
          }
        });

        Object.entries(CABECERA_FIELD_RULES)
          .filter(([, rules]) => rules.exactLength)
          .forEach(([field, rules]) => {
            const value = String(cabeceraForSubmit[field] || "").trim();
            if (value.length > 0 && value.length !== rules.exactLength) {
              addValidationError(`${rules.label} debe tener ${rules.exactLength} dígitos`);
            }
          });
      } else {
        const nro = String(cabeceraForSubmit.numero_factura || "").trim();
        if (!nro) {
          addValidationError("Número/invoice del proveedor exterior es obligatorio");
        }
      }

      if (!String(cabeceraForSubmit.proveedor_id || "").trim()) {
        addValidationError("Seleccioná un proveedor");
      }
      if (!cabeceraForSubmit.deposito_id) {
        addValidationError("Seleccioná un depósito");
      }
      // No permitir guardar un comprobante ya registrado (compra o gasto).
      if (dupComprobante?.existe) {
        const c = dupComprobante.coincidencias?.[0];
        addValidationError(
          `Ya existe ${c?.origen === "COMPRA" ? "otra factura de compra" : "un gasto"} con el comprobante ${c?.numero || cabeceraForSubmit.numero_factura}. No se puede registrar duplicado.`,
        );
      }
      if (requiereRequisicionObligatoria && !String(cabeceraForSubmit.requisicion_id || "").trim()) {
        addValidationError("La configuración exige requisición obligatoria. Facturá desde una OC vinculada a requisición aprobada.");
      }
      if (form.items.length === 0) {
        addValidationError("Agregá al menos un ítem a la compra");
      }
      if (form.items.some((item) => !item.descripcion || parseNumber(item.cantidad, 0) <= 0)) {
        addValidationError("Completá descripción y cantidad de todos los ítems");
      }
      if (String(cabeceraForSubmit.orden_compra_id || "").trim() && form.items.some((item) => !String(item.orden_compra_det_id || "").trim())) {
        addValidationError("Todos los ítems deben estar vinculados a una línea de orden de compra");
      }
      if (
        !afectaStockEnCompraDirecta &&
        !String(cabeceraForSubmit.orden_compra_id || "").trim() &&
        !(recepcionHabilitada && !ordenCompraHabilitada) &&
        form.items.some((item) => Boolean(item.producto_id) && Boolean(item.afecta_stock))
      ) {
        addValidationError(
          recepcionHabilitada && ordenCompraHabilitada
            ? "Esta empresa ingresa el stock mediante Recepción contra Orden de Compra. Creá o seleccioná una OC, registrá la recepción y luego facturala."
            : "La factura no puede ingresar stock con la configuración actual. Activá Recepciones o habilitá el impacto de stock en compra directa.",
        );
      }
      if (isCredito && form.pagos.some((p) => parseNumber(p?.monto, 0) > 0)) {
        addValidationError("Las compras a crédito no permiten pagos en alta/edición");
      }
      if (isCredito) {
        const cuotasObligatorias = creditoRequiereCuotas || usarCuotasManuales;

        if (cuotasObligatorias) {
          const cuotasValidas = form.cuotas.filter((c) => c.fecha_vencimiento && parseNumber(c.monto, 0) > 0);
          if (cuotasValidas.length === 0) {
            addValidationError("Agregá al menos una cuota válida o desactivá cuotas manuales");
          }

          const totalCuotas = cuotasValidas.reduce((acc, cuota) => acc + parseNumber(cuota.monto, 0), 0);
          if (cuotasValidas.length > 0 && Math.abs(totalCuotas - totalItems) > 0.01) {
            addValidationError("La suma de cuotas debe coincidir exactamente con el total de la compra");
          }
        } else {
          const plazoDias = Math.trunc(parseNumber(cabeceraForSubmit.plazo_credito, 0));
          if (plazoDias <= 0) {
            addValidationError("Ingresá un plazo de crédito válido (días)");
          }
        }
      }
      // Con "incluir contado en orden de pago" activo, el contado no se paga al
      // alta: queda como CxP pendiente para pagarse vía orden de pago.
      const contadoDifierePago = !isCredito && Boolean(flowConfig?.incluir_contado_en_orden_pago);
      if (!isCredito && !contadoDifierePago) {
        const pagosConMonto = form.pagos.filter((p) => parseNumber(p?.monto, 0) > 0);
        if (pagosConMonto.length === 0) {
          addValidationError("En compras contado debés cargar al menos un pago");
        }
        const pagosSinMedio = form.pagos.filter((p) => parseNumber(p?.monto, 0) > 0 && !String(p?.medio_pago_id || "").trim());
        if (pagosSinMedio.length > 0) {
          addValidationError("Seleccioná medio de pago en cada pago con monto mayor a 0");
        }
      }
      if (!isCredito && !contadoDifierePago && form.pagos.some((p) => parseNumber(p?.monto, 0) > 0)) {
        if (Math.abs(totalPagosForm - totalItems) > 0.01) {
          const diferencia = roundAmount(Math.abs(totalItems - totalPagosForm), 2);
          const totalTexto = formatMontoCompra(totalItems);
          const pagosTexto = formatMontoCompra(totalPagosForm);
          const diferenciaTexto = formatMontoCompra(diferencia);
          const message =
            totalPagosForm < totalItems
              ? `En compras contado, los pagos deben cubrir el total exacto. Total: ${totalTexto}, pagos: ${pagosTexto}. Faltan ${diferenciaTexto}.`
              : `En compras contado, los pagos deben coincidir con el total exacto. Total: ${totalTexto}, pagos: ${pagosTexto}. Excede por ${diferenciaTexto}.`;
          addValidationError(message);
        }
      }

      if (validationErrors.length > 0) {
        setCreateValidationMessages(validationErrors);
        toast.warning("Revisá los campos obligatorios y validaciones del formulario.");
        return;
      }

      const payload = buildPayload(cabeceraForSubmit);
      if (editingId) {
        await persistCompra(payload, true);
      } else {
        setCreateValidationMessages([]);
        setPendingCreatePayload(payload);
        setCreateConfirmOpen(true);
      }
    } catch (err) {
      console.error(err);
      const messages = extractApiErrorMessages(err, "No se pudo procesar la compra");
      setCreateValidationMessages(messages);
      toast.error(messages[0]);
    }
  };

  const handleEdit = async (id) => {
    try {
      const compra = await getCompra(id);
      if (compra?.moneda?.id) {
        setMonedas((prev) => {
          if (prev.some((m) => m.id === compra.moneda.id)) return prev;
          return [...prev, normalizeEmpresaMonedaOption(compra.moneda)];
        });
      }

      setEditingId(compra.id);
      const cuentas = Array.isArray(compra.cuentas_pagar) ? compra.cuentas_pagar : [];
      const cuotasManuales = creditoRequiereCuotas ? true : cuentas.length > 1;
      const fechaEmisionCompra = compra.fecha_emision ? new Date(compra.fecha_emision) : null;
      const fechaVencimientoPrimera = cuentas[0]?.fecha_vencimiento ? new Date(cuentas[0].fecha_vencimiento) : null;
      const fechaEmisionValida = fechaEmisionCompra instanceof Date && !Number.isNaN(fechaEmisionCompra.getTime());
      const fechaVencimientoValida = fechaVencimientoPrimera instanceof Date && !Number.isNaN(fechaVencimientoPrimera.getTime());
      const plazoPorDiff = fechaEmisionValida && fechaVencimientoValida ? Math.max(1, Math.round((fechaVencimientoPrimera.getTime() - fechaEmisionCompra.getTime()) / (1000 * 60 * 60 * 24))) : 30;
      const plazoCreditoEdit = Math.trunc(parseNumber(compra.plazo_credito, 0)) > 0 ? Math.trunc(parseNumber(compra.plazo_credito, 30)) : plazoPorDiff;
      const plazoCreditoSeguro = Number.isFinite(plazoCreditoEdit) && plazoCreditoEdit > 0 ? plazoCreditoEdit : 30;

      setUsarCuotasManuales(cuotasManuales);
      setForm({
        cabecera: {
          proveedor_id: compra.proveedor_id || "",
          deposito_id: compra.deposito_id || "",
          sucursal_id: compra.sucursal_id || "",
          requisicion_id: compra.requisicion_id || "",
          orden_compra_id: compra.orden_compra_id || "",
          condicion_operacion_id: compra.condicion_operacion_id || "",
          plazo_credito: plazoCreditoSeguro,
          moneda_id: compra.moneda_id || "",
          fecha_emision: compra.fecha_emision ? String(compra.fecha_emision).slice(0, 10) : "",
          timbrado_proveedor: compra.timbrado_proveedor || "",
          establecimiento: compra.establecimiento || "",
          punto_expedicion: compra.punto_expedicion || "",
          numero_factura: compra.numero_factura || "",
          es_preimpreso: compra.es_preimpreso !== false,
          ...datosFiscalesDe(compra),
          observaciones: compra.observaciones || "",
          descuento_tipo: compra.descuento_tipo || "por_item",
          descuento_global_pct: parseNumber(compra.descuento_global_pct, 0),
          descuento_global_monto: parseNumber(compra.descuento_global_monto, 0),
        },
        items:
          compra.compra_det
            ?.map((d) => ({
              requisicion_det_id: d.requisicion_det_id || "",
              orden_compra_det_id: d.orden_compra_det_id || "",
              producto_id: d.producto_id || "",
              cod_producto: d.productos?.cod_producto || d.productos?.codigo_barra || "S/C",
              descripcion: d.descripcion || "",
              cantidad: parseNumber(d.cantidad, 0),
              precio_unitario: parseNumber(d.precio_unitario, 0),
              descuento_tipo: d.descuento_tipo === "monto" ? "monto" : "porcentaje",
              descuento_pct: parseNumber(d.descuento_pct ?? d.descuento_porcentaje, 0),
              descuento_monto: parseNumber(d.descuento_monto, 0),
              porcentaje_iva: parseNumber(d.porcentaje_iva, 10),
              afecta_stock: Boolean(d.afecta_stock ?? true),
              maneja_lote: Boolean(d.productos?.maneja_lote ?? d.lotes_producto?.length),
              numero_lote: d.lotes_producto?.[0]?.numero_lote || d.lote || "",
              fecha_fabricacion: d.lotes_producto?.[0]?.fecha_fabricacion ? String(d.lotes_producto[0].fecha_fabricacion).slice(0, 10) : "",
              fecha_vencimiento: d.lotes_producto?.[0]?.fecha_vencimiento
                ? String(d.lotes_producto[0].fecha_vencimiento).slice(0, 10)
                : d.fecha_vencimiento
                  ? String(d.fecha_vencimiento).slice(0, 10)
                  : "",
            }))
            .map((item) => enrichCompraItem(item, roundingConfig)) || [],
        pagos:
          compra.compra_forma_pagos?.map((p) => ({
            medio_pago_id: p.medio_pago_id || "",
            moneda_id: p.moneda_id || "",
            monto: parseNumber(p.monto, 0),
            referencia: p.referencia || "",
          })) || [],
        cuotas:
          compra.cuentas_pagar?.map((c) => ({
            numero_cuota: parseNumber(c.numero_cuota, 1),
            fecha_vencimiento: c.fecha_vencimiento ? String(c.fecha_vencimiento).slice(0, 10) : "",
            monto: parseNumber(c.monto_original, 0),
            observaciones: c.observaciones || "",
          })) || [],
      });
      let ordenNumero = String(compra?.orden_compra_cab?.numero_orden || "").trim();
      let requisicionNumero = String(compra?.requisicion_compra_cab?.numero_requisicion || "").trim();

      if (compra?.orden_compra_id && (!ordenNumero || !requisicionNumero)) {
        try {
          const orden = await getOrdenCompra(compra.orden_compra_id);
          if (!ordenNumero) ordenNumero = String(orden?.numero_orden || "").trim();
          if (!requisicionNumero) {
            requisicionNumero = String(orden?.requisicion_compra_cab?.numero_requisicion || "").trim();
          }
        } catch (_) {
          // no-op: mantenemos fallback visual amigable
        }
      }
      if (compra?.requisicion_id && !requisicionNumero) {
        try {
          const req = await getRequisicion(compra.requisicion_id);
          requisicionNumero = String(req?.numero_requisicion || "").trim();
        } catch (_) {
          // no-op: mantenemos fallback visual amigable
        }
      }
      setLinkedOrdenNumero(ordenNumero);
      setLinkedRequisicionNumero(requisicionNumero);
      setSelectedProveedor(compra.proveedores || null);
      setShowForm(true);
      window.scrollTo({ top: 0, behavior: "smooth" });
    } catch (err) {
      console.error(err);
      toast.error("No se pudo cargar la compra para editar");
    }
  };

  const requestAnularCompra = (compra) => {
    if (!compra?.id) return;
    setPendingAnularCompra(compra);
    setAnularConfirmOpen(true);
  };

  const closeAnularConfirm = () => {
    if (anulandoCompra) return;
    setAnularConfirmOpen(false);
    setPendingAnularCompra(null);
  };

  const handleConfirmAnularCompra = async () => {
    if (!pendingAnularCompra?.id) return;
    const loadingToastId = toast.loading("Anulando compra...");
    try {
      setAnulandoCompra(true);
      const response = await anularCompra(pendingAnularCompra.id);
      toast.dismiss(loadingToastId);
      toast.success(response?.message || "Compra anulada exitosamente");
      await loadCompras();
      if (editingId === pendingAnularCompra.id) closeForm();
      setAnularConfirmOpen(false);
      setPendingAnularCompra(null);
    } catch (err) {
      toast.dismiss(loadingToastId);
      console.error(err);
      const message = err?.response?.data?.message || "No se pudo anular la compra";
      if (err?.response?.status === 400) toast.warning(message);
      else toast.error(message);
    } finally {
      setAnulandoCompra(false);
    }
  };

  return (
    <Container>
      <TopCard id="compras-header">
        <TitleWrap>
          <Icon icon="lucide:shopping-cart" />
          <div>
            <h1>Compras</h1>
            <p>Gestión de compras: registrá facturas, controlá pagos y seguí el flujo operativo</p>
          </div>
        </TitleWrap>
        <TourTrigger key={tourKey} steps={tourSteps} tourKey={tourKey} autoStart={true} />
      </TopCard>

      {showForm && (
        <FormPanel>
          {/* Volver va primero y a la izquierda: es la salida del formulario,
              y en esa esquina se lee antes que el título. Como "Cerrar" a la
              derecha competía visualmente con las acciones de guardado del pie
              y casi no se veía. */}
          <FormHead>
            <GhostButton onClick={closeForm}>
              <Icon icon="lucide:arrow-left" /> Volver
            </GhostButton>
            <SectionTitle>
              <Icon icon="mdi:file-document-outline" />
              {editingId ? "Editar Compra" : "Nueva Compra"}
            </SectionTitle>
          </FormHead>

          <FormLayout>
            <FormMain>
              {requiereRequisicionObligatoria && !String(form.cabecera.requisicion_id || "").trim() && (
                <Alert severity="warning" sx={{ mb: 1.5 }}>
                  Esta empresa opera con requisición obligatoria. Para facturar, seleccioná una OC creada desde requisición aprobada.
                </Alert>
              )}
              {!afectaStockEnCompraDirecta && (
                <Alert severity="info" sx={{ mb: 1.5 }}>
                  {recepcionHabilitada && ordenCompraHabilitada
                    ? "Esta factura no ingresará stock. Para productos de inventario, primero registrá la Recepción contra una Orden de Compra; luego vinculá esa OC a la factura."
                    : recepcionHabilitada
                      ? "Esta factura no ingresará stock. Registrá por separado una Recepción libre con el proveedor, depósito, productos y cantidades efectivamente recibidas."
                      : "Esta factura no ingresará stock y Recepciones está inactiva. Activá Recepciones o habilitá «Afectar stock en compra directa» antes de operar productos de inventario."}
                </Alert>
              )}
              {createValidationMessages.length > 0 && (
                <Alert severity="error" sx={{ mb: 1.5 }}>
                  <Typography variant="body2" sx={{ fontWeight: 600, mb: 0.5 }}>
                    No se pudo {editingId ? "actualizar" : "crear"} la compra:
                  </Typography>
                  {createValidationMessages.map((msg, idx) => (
                    <Typography key={`inline-${msg}-${idx}`} variant="body2">
                      • {msg}
                    </Typography>
                  ))}
                </Alert>
              )}
              <FieldsGrid id="compra-form-cabecera">
                <Field>
                  <TextField
                    select
                    fullWidth
                    size="small"
                    label="Sucursal"
                    value={form.cabecera.sucursal_id}
                    onChange={(e) => updateCabecera("sucursal_id", e.target.value)}
                  >
                    <MenuItem value="">Seleccionar</MenuItem>
                    {sucursales.map((s) => (
                      <MenuItem key={s.id} value={s.id}>
                        {s.descripcion || s.punto_establecimiento || s.codigo || "Sucursal"}
                      </MenuItem>
                    ))}
                  </TextField>
                </Field>

                <Field id="compra-form-proveedor">
                  <ProveedorSelector
                    value={selectedProveedor}
                    onChange={handleSelectProveedor}
                    label="Proveedor"
                    required
                    size="small"
                    minWidth={0}
                  />
                </Field>

                {embarquesImputables.length > 0 && (
                  <Field id="compra-form-embarque" $span={2}>
                    <TextField
                      select
                      fullWidth
                      size="small"
                      label="Factura de importación (opcional)"
                      value={form.cabecera.embarque_id}
                      onChange={(e) => updateCabecera("embarque_id", e.target.value)}
                      helperText={
                        form.cabecera.embarque_id
                          ? "Mercadería del exterior: no va al Libro IVA y el stock entra al liberar el embarque, no ahora."
                          : "Vinculá el embarque si esta es la factura del proveedor del exterior."
                      }
                    >
                      <MenuItem value="">
                        <em>Compra normal (sin importación)</em>
                      </MenuItem>
                      {embarquesImputables.map((e) => (
                        <MenuItem key={e.id} value={e.id}>
                          {`${e.numero || "Sin número"} — ${e.estado}`}
                        </MenuItem>
                      ))}
                    </TextField>
                  </Field>
                )}

                {(ordenCompraHabilitada || String(form.cabecera.orden_compra_id || "").trim()) && (
                  <Field id="compra-form-orden-compra" $span={2}>
                    <Autocomplete
                        options={ordenesFacturablesConSeleccion}
                        loading={loadingOrdenesFacturables}
                        value={selectedOrdenFacturable}
                        isOptionEqualToValue={(option, value) => option?.id === value?.id}
                        getOptionLabel={getOrdenCompraVinculoLabel}
                        noOptionsText="No hay órdenes con líneas pendientes de facturación"
                        onChange={(_, option) => {
                          if (!option) {
                            clearOrdenCompraVinculo();
                            return;
                          }
                          cargarOrdenEnFactura(option);
                        }}
                      renderInput={(params) => (
                        <TextField
                          {...params}
                          size="small"
                          label="Vincular orden de compra (OC)"
                          placeholder="Buscar por número, proveedor o fecha"
                        />
                      )}
                    />
                  </Field>
                )}

                {(requisicionHabilitada || requiereRequisicionObligatoria || String(form.cabecera.requisicion_id || "").trim()) && (
                  <Field>
                    <TextField
                      fullWidth
                      size="small"
                      label="Requisición (flujo)"
                      value={linkedRequisicionNumero || (form.cabecera.requisicion_id ? "Requisición vinculada" : "")}
                      placeholder="Sin requisición vinculada"
                      InputProps={{ readOnly: true }}
                    />
                  </Field>
                )}

                <Field>
                  <TextField
                    type="date"
                    fullWidth
                    size="small"
                    label="Fecha"
                    InputLabelProps={{ shrink: true }}
                    value={form.cabecera.fecha_emision}
                    onChange={(e) => updateCabecera("fecha_emision", e.target.value)}
                  />
                </Field>

                <Field>
                  <TextField
                    select
                    fullWidth
                    size="small"
                    label="Moneda"
                    value={form.cabecera.moneda_id}
                    onChange={(e) => updateCabecera("moneda_id", e.target.value)}
                  >
                    <MenuItem value="">Seleccionar</MenuItem>
                    {monedas.map((m) => (
                      <MenuItem key={m.id} value={m.id}>
                        {m.codigo} - {m.descripcion}
                        {m.es_principal ? " ★" : ""}
                      </MenuItem>
                    ))}
                  </TextField>
                </Field>

                {monedaActiva && monedaActiva.codigo !== "PYG" && (
                  <Field>
                    <TextField
                      type="number"
                      fullWidth
                      size="small"
                      label="Cotización"
                      inputProps={{ min: 0, step: "0.000001" }}
                      value={form.cabecera.cotizacion}
                      onChange={(e) => {
                        setCotizacionTocada(true);
                        updateCabecera("cotizacion", e.target.value);
                      }}
                    />
                    <CotizacionHint cotizacion={cotizacionVigente} tocado={cotizacionTocada} />
                  </Field>
                )}

                <Field>
                  <TextField
                    select
                    fullWidth
                    size="small"
                    label="Condición"
                    value={form.cabecera.condicion_operacion_id}
                    onChange={(e) => updateCabecera("condicion_operacion_id", e.target.value)}
                  >
                    <MenuItem value="">Seleccionar</MenuItem>
                    {condicionesOperacion.map((c) => (
                      <MenuItem key={c.id} value={c.id}>
                        {c.descripcion}
                      </MenuItem>
                    ))}
                  </TextField>
                </Field>

                {isCredito && (
                  <Field>
                    <TextField
                      type="number"
                      fullWidth
                      size="small"
                      label="Plazo de crédito (días)"
                      inputProps={{ min: 1, step: 1 }}
                      value={form.cabecera.plazo_credito}
                      onChange={(e) => updateCabecera("plazo_credito", e.target.value)}
                    />
                  </Field>
                )}

                <Field>
                  <TextField
                    select
                    fullWidth
                    required
                    size="small"
                    label={afectaStockEnCompraDirecta ? "Depósito de inventario" : "Depósito de referencia"}
                    value={form.cabecera.deposito_id}
                    onChange={(e) => updateCabecera("deposito_id", e.target.value)}
                  >
                    <MenuItem value="">Seleccionar</MenuItem>
                    {depositos.map((d) => (
                      <MenuItem key={d.id} value={d.id}>
                        {d.descripcion}
                      </MenuItem>
                    ))}
                  </TextField>
                </Field>

                <FieldFullRow>
                  {/* Timbrado + número del comprobante: el mismo bloque que usan
                      Notas de crédito y Gastos, desde _standards. */}
                  <ComprobanteProveedorFields
                    requerido={!esProveedorExterior}
                    exterior={esProveedorExterior}
                    valores={{
                      timbrado: form.cabecera.timbrado_proveedor,
                      establecimiento: form.cabecera.establecimiento,
                      punto_expedicion: form.cabecera.punto_expedicion,
                      numero_factura: form.cabecera.numero_factura,
                      es_preimpreso: form.cabecera.es_preimpreso,
                    }}
                    onCampoChange={(campo, valor) =>
                      updateCabecera(campo === "timbrado" ? "timbrado_proveedor" : campo, valor)
                    }
                    mostrarPreimpreso={false}
                    onPreimpresoChange={(checked) => updateCabecera("es_preimpreso", checked)}
                  />
                </FieldFullRow>
                <FieldFullRow>
                  <DatosFiscalesCompraFields
                    valores={form.cabecera}
                    tiposPermitidos={TIPOS_COMPROBANTE_PARA_COMPRAS}
                    obligaciones={obligacionesFiscales}
                    mostrarExtracto={false}
                    onChange={(patch) => setForm((prev) => ({ ...prev, cabecera: { ...prev.cabecera, ...patch } }))}
                    onCdcLeido={(datos) => {
                      // El CDC trae el número y la fecha de la factura.
                      setForm((prev) => ({
                        ...prev,
                        cabecera: {
                          ...prev.cabecera,
                          establecimiento: datos.establecimiento,
                          punto_expedicion: datos.punto,
                          numero_factura: datos.numero,
                          fecha_emision: datos.fecha,
                        },
                      }));
                      const rucProveedor = String(selectedProveedor?.personas?.ruc || selectedProveedor?.ruc || "").split("-")[0];
                      if (rucProveedor && rucProveedor !== datos.ruc) {
                        toast.warning(`El CDC es de una factura del RUC ${datos.rucConDv}, no del proveedor elegido.`);
                      }
                    }}
                  />
                </FieldFullRow>
              </FieldsGrid>

              {dupComprobante?.existe && (
                <Alert severity="error" icon={<Icon icon="mdi:content-duplicate" />} sx={{ mt: 1, mb: 1.5 }}>
                  Este comprobante ya está registrado y no se puede duplicar:{" "}
                  {dupComprobante.coincidencias
                    .slice(0, 3)
                    .map((c) => `${c.origen === "COMPRA" ? "Compra" : "Gasto"} ${c.numero}${c.nombre ? ` · ${c.nombre}` : ""}`)
                    .join(" — ")}
                  .
                </Alert>
              )}

              <Field>
                <TextField
                  fullWidth
                  multiline
                  minRows={2}
                  size="small"
                  label="Observaciones"
                  value={form.cabecera.observaciones}
                  onChange={(e) => updateCabecera("observaciones", e.target.value)}
                  placeholder={CABECERA_PLACEHOLDERS.observaciones}
                  inputProps={{ maxLength: CABECERA_FIELD_RULES.observaciones.maxLength }}
                />
              </Field>
            </FormMain>

            <FormFullWidth>
              <Divider />

              <Box id="compra-form-items">
                <SectionTitle>
                  <Icon icon="mdi:cart-outline" />
                  Ítems de Compra
                </SectionTitle>

                <ComprasProductSearch
                  depositoId={form.cabecera.deposito_id || null}
                  proveedorId={form.cabecera.proveedor_id || null}
                  moneda={monedaActiva}
                  onAddItem={handleAddItemFromSearch}
                  lotesEnabled={lotesEnabled}
                  disabled={Boolean(form.cabecera.orden_compra_id)}
                  stockFieldLabel={afectaStockEnCompraDirecta ? "Stock" : "Inventario"}
                  roundingConfig={comprasMonedaConfigActiva}
                />

                <InlineActions>
                  <span>
                    {form.cabecera.orden_compra_id
                      ? "Compra vinculada a OC: la selección manual de productos se bloquea para mantener consistencia entre OC, recepción y factura."
                      : !afectaStockEnCompraDirecta && recepcionHabilitada && !ordenCompraHabilitada
                        ? "Agregá los productos/servicios para registrar la factura. Los ítems marcados como Inventario no ingresarán stock aquí: cargá sus cantidades físicas mediante una Recepción libre."
                        : !afectaStockEnCompraDirecta && recepcionHabilitada
                          ? "Los ítems de inventario deben provenir de una OC previamente recepcionada; la factura no volverá a modificar stock. Los servicios pueden cargarse directamente."
                          : "Agregá los productos/servicios de esta compra desde la búsqueda rápida."}
                  </span>
                  {form.cabecera.orden_compra_id && (
                    <GhostButton onClick={clearOrdenCompraVinculo}>
                      <Icon icon="lucide:unlink-2" /> Quitar vínculo OC
                    </GhostButton>
                  )}
                </InlineActions>

                <ItemsTable
                  items={form.items}
                  onUpdateItem={updateItem}
                  onRemoveItem={removeItem}
                  moneda={monedaActiva}
                  priceLabel="Costo"
                  enableDiscounts
                  editableDiscountPct
                  editableDiscountAmount
                  showLotes={lotesEnabled && form.items.some((item) => Boolean(item.maneja_lote))}
                  amountDecimals={roundingConfig.decimalesMonto}
                  percentageDecimals={roundingConfig.decimalesPorcentaje}
                  showDiscountEquivalent
                />
              </Box>

              <Box id="compra-form-pagos">
                {!isCredito && flowConfig?.incluir_contado_en_orden_pago && (
                  <>
                    <Divider />
                    <SectionTitle>
                      <Icon icon="mdi:cash-multiple" />
                      Pagos
                    </SectionTitle>
                    <InlineActions>
                      <span>
                        Esta compra de contado quedará como <strong>cuenta por pagar pendiente</strong> y se abonará desde una <strong>orden de pago</strong> (configuración &quot;Incluir contado en orden
                        de pago&quot;).
                      </span>
                    </InlineActions>
                  </>
                )}
                {!isCredito && !flowConfig?.incluir_contado_en_orden_pago && (
                  <>
                    <Divider />

                    <SectionTitle>
                      <Icon icon="mdi:cash-multiple" />
                      Pagos
                    </SectionTitle>

                    <InlineActions>
                      <span>Obligatorio para compras al contado. Debés cargar pagos que cubran el total exacto. La moneda de los pagos se toma de la cabecera.</span>
                      <GhostButton onClick={addPago}>
                        <Icon icon="lucide:plus" /> Agregar pago
                      </GhostButton>
                    </InlineActions>

                    {form.pagos.map((pago, idx) => (
                      <SubCard key={`pago-${idx}`}>
                        <SubGrid>
                          <Field>
                            <TextField
                              select
                              fullWidth
                              size="small"
                              label="Medio de pago"
                              value={pago.medio_pago_id}
                              onChange={(e) => updatePago(idx, "medio_pago_id", e.target.value)}
                            >
                              <MenuItem value="">Seleccionar</MenuItem>
                              {mediosPago.map((m) => (
                                <MenuItem key={m.id} value={m.id}>
                                  {m.descripcion || m.nombre}
                                </MenuItem>
                              ))}
                            </TextField>
                          </Field>
                          <Field>
                            <MonedaInput
                              fullWidth
                              size="small"
                              label={`Monto (${monedaCabeceraCodigo})`}
                              value={parseNumber(pago.monto, 0)}
                              onChange={(value) => updatePago(idx, "monto", value)}
                              moneda={monedaCabeceraCodigo}
                              decimales={roundingConfig.decimalesMonto}
                              allowPygDecimals
                            />
                          </Field>
                          <Field>
                            <TextField
                              fullWidth
                              size="small"
                              label="Referencia"
                              value={pago.referencia}
                              onChange={(e) => updatePago(idx, "referencia", e.target.value)}
                            />
                          </Field>
                        </SubGrid>
                        <InlineActions>
                          <GhostButton onClick={() => removePago(idx)}>
                            <Icon icon="lucide:trash-2" /> Quitar
                          </GhostButton>
                        </InlineActions>
                      </SubCard>
                    ))}
                  </>
                )}

                {isCredito && (
                  <Box id="compra-form-cuotas">
                    <Divider />
                    <SectionTitle>
                      <Icon icon="mdi:credit-card-clock-outline" />
                      Cuotas de Crédito
                    </SectionTitle>

                    <InlineActions>
                      <span>
                        {creditoRequiereCuotas
                          ? "Esta empresa requiere cuotas manuales en compras a crédito."
                          : "Por defecto se genera una cuota automática por plazo."}
                      </span>
                      {!creditoRequiereCuotas && (
                        <SwitchConAyuda
                          checked={usarCuotasManuales}
                          onChange={setUsarCuotasManuales}
                          label="Definir cuotas manuales"
                          ayuda="Activalo solo si querés fijar vos el número, el vencimiento y el monto de cada cuota. Apagado, se crea una sola cuenta por pagar con vencimiento al plazo de crédito."
                        />
                      )}
                    </InlineActions>

                    {usarCuotasManuales ? (
                      <>
                        <InlineActions>
                          <span>La suma de cuotas debe coincidir con el total de la compra ({formatMontoCompra(totalItems)}).</span>
                          <GhostButton onClick={addCuota}>
                            <Icon icon="lucide:plus" /> Agregar cuota
                          </GhostButton>
                        </InlineActions>

                        {form.cuotas.map((cuota, idx) => (
                          <SubCard key={`cuota-${idx}`}>
                            <SubGrid>
                              <Field>
                                <TextField
                                  type="number"
                                  fullWidth
                                  size="small"
                                  label="Nro. de cuota"
                                  value={cuota.numero_cuota}
                                  onChange={(e) => updateCuota(idx, "numero_cuota", e.target.value)}
                                />
                              </Field>
                              <Field>
                                <TextField
                                  type="date"
                                  fullWidth
                                  size="small"
                                  label="Vencimiento"
                                  InputLabelProps={{ shrink: true }}
                                  value={cuota.fecha_vencimiento}
                                  onChange={(e) => updateCuota(idx, "fecha_vencimiento", e.target.value)}
                                />
                              </Field>
                              <Field>
                                <TextField
                                  type="number"
                                  fullWidth
                                  size="small"
                                  label="Monto"
                                  inputProps={{ step: "0.01" }}
                                  value={cuota.monto}
                                  onChange={(e) => updateCuota(idx, "monto", e.target.value)}
                                />
                              </Field>
                              <Field>
                                <TextField
                                  fullWidth
                                  size="small"
                                  label="Observaciones"
                                  value={cuota.observaciones}
                                  onChange={(e) => updateCuota(idx, "observaciones", e.target.value)}
                                />
                              </Field>
                            </SubGrid>
                            <InlineActions>
                              <GhostButton onClick={() => removeCuota(idx)}>
                                <Icon icon="lucide:trash-2" /> Quitar
                              </GhostButton>
                            </InlineActions>
                          </SubCard>
                        ))}
                      </>
                    ) : (
                      <CreditHint>
                        Se creará automáticamente 1 cuenta por pagar con vencimiento a <strong>{Math.max(1, Math.trunc(parseNumber(form.cabecera.plazo_credito, 30)))} días</strong>.
                      </CreditHint>
                    )}
                  </Box>
                )}
              </Box>
            </FormFullWidth>

            <SummaryAside>
              <SummaryCard id="compra-form-resumen">
                <SummaryHeader>
                  <Icon icon="mdi:calculator" />
                  <span>Resumen</span>
                </SummaryHeader>

                <SummaryBody>
                  <SummaryField>
                    <TextField
                      select
                      fullWidth
                      size="small"
                      label="Tipo de descuento"
                      value={form.cabecera.descuento_tipo || "por_item"}
                      onChange={(e) => {
                        const nuevoTipo = e.target.value;
                        setForm((prev) => ({
                          ...prev,
                          cabecera: {
                            ...prev.cabecera,
                            descuento_tipo: nuevoTipo,
                            descuento_global_pct: nuevoTipo === "global_pct" ? prev.cabecera.descuento_global_pct : 0,
                            descuento_global_monto: nuevoTipo === "global_monto" ? prev.cabecera.descuento_global_monto : 0,
                          },
                        }));
                      }}
                    >
                      <MenuItem value="por_item">Por ítem (% o monto)</MenuItem>
                      <MenuItem value="global_pct">Global %</MenuItem>
                      <MenuItem value="global_monto">Global monto</MenuItem>
                    </TextField>
                  </SummaryField>

                  {form.cabecera.descuento_tipo === "global_pct" && (
                    <SummaryField>
                      <TextField
                        fullWidth
                        size="small"
                        label="Descuento global (%)"
                        inputRef={descuentoGlobalPctMaskRef}
                        inputProps={{ inputMode: roundingConfig.decimalesPorcentaje > 0 ? "decimal" : "numeric" }}
                      />
                    </SummaryField>
                  )}

                  {form.cabecera.descuento_tipo === "global_monto" && (
                    <SummaryField>
                      <MonedaInput
                        label="Descuento global (monto)"
                        fullWidth
                        moneda={monedaActiva?.moneda?.codigo || "PYG"}
                        decimales={roundingConfig.decimalesMonto}
                        allowPygDecimals
                        value={form.cabecera.descuento_global_monto ?? 0}
                        onChange={(nuevoValor) =>
                          setForm((prev) => ({
                            ...prev,
                            cabecera: {
                              ...prev.cabecera,
                              descuento_global_monto: Math.max(parseNumber(nuevoValor, 0), 0),
                            },
                          }))
                        }
                      />
                    </SummaryField>
                  )}

                  <SummaryRows>
                    <SummaryRow>
                      <span className="label">Total Bruto</span>
                      <span className="value">{formatMontoCompra(resumenCompra.total_bruto)}</span>
                    </SummaryRow>
                    <SummaryRow>
                      <span className="label">Descuento ítems</span>
                      <span className="value">-{formatMontoCompra(resumenCompra.total_descuento_items)}</span>
                    </SummaryRow>
                    {resumenCompra.descuento_global_monto > 0 && (
                      <SummaryRow>
                        <span className="label">Descuento global</span>
                        <span className="value">-{formatMontoCompra(resumenCompra.descuento_global_monto)}</span>
                      </SummaryRow>
                    )}

                    <SummaryDivider />

                    <SummaryRow>
                      <span className="label">Liq. IVA 10%</span>
                      <span className="value">{formatMontoCompra(resumenCompra.total_iva_10)}</span>
                    </SummaryRow>
                    <SummaryRow>
                      <span className="label">Liq. IVA 5%</span>
                      <span className="value">{formatMontoCompra(resumenCompra.total_iva_5)}</span>
                    </SummaryRow>
                    <SummaryRow>
                      <span className="label">Total IVA</span>
                      <span className="value">{formatMontoCompra(resumenCompra.total_iva)}</span>
                    </SummaryRow>

                    <SummaryDivider />

                    <SummaryRow>
                      <span className="label">Base Gravada</span>
                      <span className="value">{formatMontoCompra(resumenCompra.base_gravada)}</span>
                    </SummaryRow>
                    <SummaryRow>
                      <span className="label">Total Exentas</span>
                      <span className="value">{formatMontoCompra(resumenCompra.total_exentas)}</span>
                    </SummaryRow>

                    <SummaryDivider />

                    <SummaryRow $main>
                      <span className="label">TOTAL OPERACIÓN</span>
                      <span className="value">{formatMontoCompra(resumenCompra.total_operacion)}</span>
                    </SummaryRow>
                  </SummaryRows>
                </SummaryBody>
              </SummaryCard>
            </SummaryAside>
          </FormLayout>

          <Footer id="compra-form-footer">
            <BarActions>
              <FooterActionButton type="button" $variant="danger" disabled={saving} onClick={handleFooterSecondaryAction}>
                <Icon icon={editingId ? "mdi:close-circle" : "mdi:trash-can"} />
                <span>{editingId ? "Cancelar edición" : "Limpiar"}</span>
              </FooterActionButton>

              <FooterActionButton type="button" $variant="primary" disabled={saving || dupComprobante?.existe} onClick={handleSubmit}>
                <Icon icon="mdi:check-circle" />
                <span>{saving ? "Guardando..." : editingId ? "Actualizar compra" : "Crear compra"}</span>
              </FooterActionButton>
            </BarActions>
          </Footer>
        </FormPanel>
      )}

      {!showForm && (
        <>
          <ListPanel>
            <Box
              id="compras-tabs"
              sx={{
                borderBottom: 1,
                borderColor: "divider",
                bgcolor: "action.hover",
                px: { xs: 0.5, sm: 1 },
              }}
            >
              <Tabs value={activeTabIndex} onChange={(_, idx) => setActiveTab(comprasTabs[idx]?.value ?? "facturas")} variant="scrollable" scrollButtons="auto" allowScrollButtonsMobile>
                {comprasTabs.map((tab) => (
                  <Tab key={tab.value} label={tab.label} />
                ))}
              </Tabs>
            </Box>
            <ListBody>
              {activeTab === "requisiciones" ? (
                <Box id="compras-requisiciones-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  {comprasGuia && (
                    <Box id="compras-guia">
                      <ScreenGuia titulo={comprasGuia.titulo} intro={comprasGuia.intro} configuracion={comprasGuia.configuracion} pasos={comprasGuia.pasos} notas={comprasGuia.notas} />
                    </Box>
                  )}
                  <RequisicionesTab />
                </Box>
              ) : activeTab === "ordenes" ? (
                <Box id="compras-ordenes-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  {comprasGuia && (
                    <Box id="compras-guia">
                      <ScreenGuia titulo={comprasGuia.titulo} intro={comprasGuia.intro} configuracion={comprasGuia.configuracion} pasos={comprasGuia.pasos} notas={comprasGuia.notas} />
                    </Box>
                  )}
                  <OrdenesCompraTab onUsarEnFactura={cargarOrdenEnFactura} requiereRequisicionObligatoria={requiereRequisicionObligatoria} />
                </Box>
              ) : activeTab === "recepciones" ? (
                <Box id="compras-recepciones-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  {comprasGuia && (
                    <Box id="compras-guia">
                      <ScreenGuia titulo={comprasGuia.titulo} intro={comprasGuia.intro} configuracion={comprasGuia.configuracion} pasos={comprasGuia.pasos} notas={comprasGuia.notas} />
                    </Box>
                  )}
                  <RecepcionesCompraTab ordenCompraHabilitada={ordenCompraHabilitada} afectaStockEnCompraDirecta={afectaStockEnCompraDirecta} />
                </Box>
              ) : activeTab === "notas_credito" ? (
                <Box id="compras-nc-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  {comprasGuia && (
                    <Box id="compras-guia">
                      <ScreenGuia titulo={comprasGuia.titulo} intro={comprasGuia.intro} configuracion={comprasGuia.configuracion} pasos={comprasGuia.pasos} notas={comprasGuia.notas} />
                    </Box>
                  )}
                  <NotasCreditoCompraTab />
                </Box>
              ) : activeTab === "gastos" && canViewGastos ? (
                <Box id="compras-gastos-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  {comprasGuia && (
                    <Box id="compras-guia">
                      <ScreenGuia titulo={comprasGuia.titulo} intro={comprasGuia.intro} configuracion={comprasGuia.configuracion} pasos={comprasGuia.pasos} notas={comprasGuia.notas} />
                    </Box>
                  )}
                  <GastosTemplate embedded />
                </Box>
              ) : activeTab === "tipos_gasto" && canViewGastos ? (
                <Box id="compras-tipos-gasto-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  {comprasGuia && (
                    <Box id="compras-guia">
                      <ScreenGuia titulo={comprasGuia.titulo} intro={comprasGuia.intro} configuracion={comprasGuia.configuracion} pasos={comprasGuia.pasos} notas={comprasGuia.notas} />
                    </Box>
                  )}
                  <TiposGastoTab />
                </Box>
              ) : activeTab === "cuentas_pagar" && canVerCuentasPagar ? (
                <Box id="compras-cuentas-pagar-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  <CuentasPagarTab />
                </Box>
              ) : activeTab === "plan_pagos" && canVerPlanPagos ? (
                <Box id="compras-plan-pagos-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  <PlanPagosProveedores
                    onAbrirOrden={
                      canVerOrdenPago && ordenPagoHabilitada
                        ? (proveedorId, cuotaIds, fechaPago) => {
                            // La orden de pago lee ?proveedor=&cuotas=&fecha= al montarse y abre el formulario precargado.
                            setSearchParams({ tab: "pagos_proveedor", proveedor: proveedorId, cuotas: cuotaIds.join(","), ...(fechaPago ? { fecha: fechaPago } : {}) });
                            setActiveTab("pagos_proveedor");
                          }
                        : null
                    }
                  />
                </Box>
              ) : activeTab === "pagos_proveedor" && canVerOrdenPago ? (
                <Box id="compras-orden-pago-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  <ComprasPagosTab />
                </Box>
              ) : activeTab === "marangatu" && tieneMarangatu ? (
                <Box id="compras-marangatu-tab" sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
                  <MarangatuGuia flowUiState={flowUiState} />
                  <MarangatuSyncPanel tipo="COMPRAS" />
                  <MarangatuPendientesPanel />
                </Box>
              ) : (
                <>
                  {comprasGuia && (
                    <Box id="compras-guia" sx={{ mb: 2 }}>
                      <ScreenGuia titulo={comprasGuia.titulo} intro={comprasGuia.intro} configuracion={comprasGuia.configuracion} pasos={comprasGuia.pasos} notas={comprasGuia.notas} />
                    </Box>
                  )}
                  <Box sx={{ display: "flex", justifyContent: "flex-end", alignItems: "center", mb: 2 }}>
                    <Tooltip
                      title={
                        requiereRequisicionObligatoria
                          ? "Bloqueado porque la empresa está en «Requisición obligatoria»: una compra/factura solo se permite cuando nace de una requisición aprobada (normalmente vía OC)."
                          : ""
                      }
                    >
                      <span>
                        <Button id="compras-nuevo-btn" variant="contained" onClick={handleNuevaCompra} startIcon={<Icon icon="mdi:plus" />} disabled={requiereRequisicionObligatoria}>
                          Nueva compra
                        </Button>
                      </span>
                    </Tooltip>
                  </Box>
                  <Box id="compras-filtros">
                    <ListToolbar
                      searchText={searchText}
                      onSearchChange={setSearchText}
                      searchPlaceholder="Buscar por número, proveedor, RUC..."
                      showFilters={showFilters}
                      onToggleFilters={() => setShowFilters((prev) => !prev)}
                      filtrosActivos={filtrosActivos}
                      onRefresh={() => loadCompras()}
                      searchHelper={searchDebounced.trim() ? "Buscando en todo el historial" : undefined}
                      quickFilters={[
                        { label: "Hoy", value: "hoy" },
                        { label: "Ayer", value: "ayer" },
                        { label: "Esta semana", value: "semana" },
                        { label: "Este mes", value: "mes" },
                        { label: "Mes anterior", value: "mes_anterior" },
                      ]}
                      quickFilterActive={filtroRapido}
                      onQuickFilter={aplicarFiltroRapido}
                      onClearFilters={limpiarFiltros}
                      quickFiltersDisabled={Boolean(searchDebounced.trim())}
                      quickFiltersDisabledHint="Ignorado al buscar en todo el historial"
                    />
                  </Box>

                  <Collapse in={showFilters}>
                    <Paper sx={{ p: { xs: 1.5, sm: 2 }, mb: 2, overflow: "hidden" }} variant="outlined">
                      <Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
                        <Box sx={{ display: "flex", gap: 1.5, flexWrap: "wrap" }}>
                          <Box sx={{ flex: 1, minWidth: { xs: "100%", sm: 160 } }}>
                            <TextField
                              label="Fecha desde"
                              type="date"
                              value={fechaDesde}
                              onChange={(e) => {
                                setFechaDesde(e.target.value);
                                setFiltroRapido("");
                                setPage(1);
                              }}
                              size="small"
                              fullWidth
                              InputLabelProps={{ shrink: true }}
                            />
                          </Box>
                          <Box sx={{ flex: 1, minWidth: { xs: "100%", sm: 160 } }}>
                            <TextField
                              label="Fecha hasta"
                              type="date"
                              value={fechaHasta}
                              onChange={(e) => {
                                setFechaHasta(e.target.value);
                                setFiltroRapido("");
                                setPage(1);
                              }}
                              size="small"
                              fullWidth
                              InputLabelProps={{ shrink: true }}
                            />
                          </Box>
                        </Box>
                        <Box sx={{ display: "flex", gap: 1.5, flexWrap: "wrap" }}>
                          <FormControl sx={{ flex: 1, minWidth: { xs: "100%", sm: 160 } }} size="small">
                            <InputLabel>Estado</InputLabel>
                            <Select
                              value={estadoFilter}
                              label="Estado"
                              onChange={(e) => {
                                setEstadoFilter(e.target.value);
                                setPage(1);
                              }}
                            >
                              <MenuItem value="">Todos los estados</MenuItem>
                              <MenuItem value="pendiente">Pendiente</MenuItem>
                              <MenuItem value="parcial">Parcial</MenuItem>
                              <MenuItem value="pagada">Pagada</MenuItem>
                              <MenuItem value="anulada">Anulada</MenuItem>
                            </Select>
                          </FormControl>
                          <FormControl sx={{ flex: 1, minWidth: { xs: "100%", sm: 140 } }} size="small">
                            <InputLabel>Límite</InputLabel>
                            <Select
                              value={String(limit)}
                              label="Límite"
                              onChange={(e) => {
                                setLimit(parseNumber(e.target.value, 20));
                                setPage(1);
                              }}
                            >
                              <MenuItem value="10">10</MenuItem>
                              <MenuItem value="20">20</MenuItem>
                              <MenuItem value="50">50</MenuItem>
                            </Select>
                          </FormControl>
                          <Button
                            variant="outlined"
                            onClick={limpiarFiltros}
                            startIcon={<Icon icon="mdi:filter-off" />}
                            sx={{
                              flex: { xs: 1, sm: "none" },
                              minWidth: { xs: 0, sm: 120 },
                            }}
                          >
                            Limpiar
                          </Button>
                        </Box>
                      </Box>
                    </Paper>
                  </Collapse>

                  <Box id="compras-dashboard" sx={KPI_GRID_CONTAINER_SX}>
                    <Box sx={KPI_GRID_ITEM_SX}>
                      <Card variant="outlined" sx={KPI_CARD_SX}>
                        <CardContent sx={KPI_CARD_CONTENT_SX}>
                          <Typography color="text.secondary" variant="caption" sx={KPI_CAPTION_SX}>
                            <Icon icon="mdi:file-document" width={isMobile ? 14 : 16} />
                            {isMobile ? "Comp." : "Compras"}
                          </Typography>
                          <Typography variant={isMobile ? "h6" : "h5"} fontWeight={600}>
                            {loading ? <Skeleton width={50} /> : estadoStats.total}
                          </Typography>
                        </CardContent>
                      </Card>
                    </Box>
                    <Box sx={KPI_GRID_ITEM_SX}>
                      <Card variant="outlined" sx={KPI_CARD_SX}>
                        <CardContent sx={KPI_CARD_CONTENT_SX}>
                          <Typography color="text.secondary" variant="caption" sx={KPI_CAPTION_SX}>
                            <Icon icon="mdi:cash" width={isMobile ? 14 : 16} />
                            Monto
                          </Typography>
                          <Box sx={{ display: "flex", flexDirection: "column", gap: 0.25 }}>
                            {loading ? (
                              <Skeleton width={100} />
                            ) : Object.keys(totalesPorMoneda).length === 0 ? (
                              <Typography variant={isMobile ? "h6" : "h5"} fontWeight={600} color="success.main">
                                {fmtMoneda(0, "PYG")}
                              </Typography>
                            ) : (
                              Object.entries(totalesPorMoneda).map(([code, total]) => (
                                <Typography key={code} variant={isMobile ? "body2" : "h6"} fontWeight={700} color="success.main" sx={{ lineHeight: 1.15 }}>
                                  {fmtMoneda(total, code)}
                                </Typography>
                              ))
                            )}
                          </Box>
                        </CardContent>
                      </Card>
                    </Box>
                    <Box sx={KPI_GRID_ITEM_SX}>
                      <Card variant="outlined" sx={KPI_CARD_SX}>
                        <CardContent sx={KPI_CARD_CONTENT_SX}>
                          <Typography color="text.secondary" variant="caption" sx={KPI_CAPTION_SX}>
                            <Icon icon="mdi:clock-outline" width={isMobile ? 14 : 16} />
                            {isMobile ? "Pend." : "Pendientes"}
                          </Typography>
                          <Typography variant={isMobile ? "h6" : "h5"} fontWeight={600} color="warning.main">
                            {loading ? <Skeleton width={40} /> : estadoStats.pendiente}
                          </Typography>
                        </CardContent>
                      </Card>
                    </Box>
                    <Box sx={KPI_GRID_ITEM_SX}>
                      <Card variant="outlined" sx={KPI_CARD_SX}>
                        <CardContent sx={KPI_CARD_CONTENT_SX}>
                          <Typography color="text.secondary" variant="caption" sx={KPI_CAPTION_SX}>
                            <Icon icon="mdi:progress-check" width={isMobile ? 14 : 16} />
                            Parciales
                          </Typography>
                          <Typography variant={isMobile ? "h6" : "h5"} fontWeight={600} color="info.main">
                            {loading ? <Skeleton width={40} /> : estadoStats.parcial}
                          </Typography>
                        </CardContent>
                      </Card>
                    </Box>
                    <Box sx={KPI_GRID_ITEM_SX}>
                      <Card variant="outlined" sx={KPI_CARD_SX}>
                        <CardContent sx={KPI_CARD_CONTENT_SX}>
                          <Typography color="text.secondary" variant="caption" sx={KPI_CAPTION_SX}>
                            <Icon icon="mdi:check-circle" width={isMobile ? 14 : 16} />
                            Pagadas
                          </Typography>
                          <Typography variant={isMobile ? "h6" : "h5"} fontWeight={600} color="success.main">
                            {loading ? <Skeleton width={40} /> : estadoStats.pagada}
                          </Typography>
                        </CardContent>
                      </Card>
                    </Box>
                    <Box sx={KPI_GRID_ITEM_SX}>
                      <Card variant="outlined" sx={{ ...KPI_CARD_SX, borderColor: "warning.light" }}>
                        <CardContent sx={KPI_CARD_CONTENT_SX}>
                          <Typography color="text.secondary" variant="caption" sx={KPI_CAPTION_SX}>
                            <Icon icon="mdi:file-cancel-outline" width={isMobile ? 14 : 16} />
                            Anuladas
                          </Typography>
                          <Typography variant={isMobile ? "h6" : "h5"} fontWeight={600} color="warning.main">
                            {loading ? <Skeleton width={40} /> : estadoStats.anulada}
                          </Typography>
                        </CardContent>
                      </Card>
                    </Box>
                  </Box>

                  <Box id="compras-lista">
                    {loading ? (
                      <EmptyState>Cargando compras...</EmptyState>
                    ) : comprasFiltradas.length === 0 ? (
                      <EmptyState>Sin compras registradas</EmptyState>
                    ) : isMobile ? (
                      <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
                        {comprasFiltradas.map((compra) => {
                          const proveedor = compra?.proveedores?.personas;
                          const estado = String(compra?.estado || "pendiente").toLowerCase();

                          return (
                            <Paper key={compra.id} variant="outlined" sx={{ p: 1.25, borderRadius: 2 }}>
                              <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 1 }}>
                                <Box sx={{ minWidth: 0 }}>
                                  <DetailNumberButton onClick={() => handleVerDetalle(compra.id)} sx={{ fontSize: "1.05rem" }}>
                                    {buildFacturaCompraNumero(compra)}
                                  </DetailNumberButton>
                                  <Typography
                                    variant="body2"
                                    sx={{
                                      fontWeight: 600,
                                      overflow: "hidden",
                                      textOverflow: "ellipsis",
                                      whiteSpace: "nowrap",
                                    }}
                                  >
                                    {proveedor?.razon_social || "-"}
                                  </Typography>
                                  <Typography variant="caption" color="text.secondary">
                                    {proveedor?.ruc ? `${proveedor.ruc}-${proveedor.dv || 0}` : proveedor?.nro_documento || "-"}
                                  </Typography>
                                </Box>
                                <Badge $state={estado}>{compra.estado || "-"}</Badge>
                              </Box>

                              <Box sx={{ mt: 1, display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: 1 }}>
                                <Box>
                                  <Typography sx={{ fontSize: "1.35rem", lineHeight: 1.1, fontWeight: 700, color: "primary.main" }}>
                                    {fmtMoneda(parseNumber(compra.total, 0), compra.moneda?.codigo || "PYG")}
                                  </Typography>
                                  <Typography variant="caption" color="text.secondary">
                                    {formatCompraDate(compra.fecha_emision)}
                                  </Typography>
                                </Box>
                                <RowActions>
                                  <Tooltip title="Ver">
                                    <IconButton size="small" onClick={() => handleVerDetalle(compra.id)} sx={actionIconSx("info.main")}>
                                      <Icon icon="lucide:eye" width={16} />
                                    </IconButton>
                                  </Tooltip>
                                  <Tooltip title="Editar">
                                    <IconButton size="small" onClick={() => handleEdit(compra.id)} sx={actionIconSx("primary.main")}>
                                      <Icon icon="lucide:pencil" width={16} />
                                    </IconButton>
                                  </Tooltip>
                                  {compra.estado !== "anulada" && (
                                    <Tooltip title="Anular">
                                      <IconButton size="small" onClick={() => requestAnularCompra(compra)} sx={actionIconSx("error.main")}>
                                        <Icon icon="lucide:ban" width={16} />
                                      </IconButton>
                                    </Tooltip>
                                  )}
                                </RowActions>
                              </Box>
                            </Paper>
                          );
                        })}
                      </Box>
                    ) : (
                      <TableWrap $listView>
                        <table>
                          <thead>
                            <tr>
                              <th>Número</th>
                              <th>Fecha</th>
                              <th>Proveedor</th>
                              <th>Total</th>
                              <th>Estado</th>
                              <th id="compras-acciones-col">Acciones</th>
                            </tr>
                          </thead>
                          <tbody>
                            {comprasFiltradas.map((compra) => (
                              <tr key={compra.id}>
                                <td>
                                  <DetailNumberButton onClick={() => handleVerDetalle(compra.id)}>{buildFacturaCompraNumero(compra)}</DetailNumberButton>
                                </td>
                                <td>{formatCompraDate(compra.fecha_emision)}</td>
                                <td>
                                  <ProviderCol>
                                    <strong>{compra.proveedores?.personas?.razon_social || "-"}</strong>
                                    <small>
                                      {compra.proveedores?.personas?.ruc
                                        ? `${compra.proveedores.personas.ruc}-${compra.proveedores.personas.dv || 0}`
                                        : compra.proveedores?.personas?.nro_documento || ""}
                                    </small>
                                  </ProviderCol>
                                </td>
                                <td>{fmtMoneda(parseNumber(compra.total, 0), compra.moneda?.codigo || "PYG")}</td>
                                <td>
                                  <Badge $state={compra.estado || "pendiente"}>{compra.estado || "-"}</Badge>
                                </td>
                                <td>
                                  <RowActions>
                                    <Tooltip title="Ver">
                                      <IconButton size="small" onClick={() => handleVerDetalle(compra.id)} sx={actionIconSx("info.main")}>
                                        <Icon icon="lucide:eye" width={16} />
                                      </IconButton>
                                    </Tooltip>
                                    <Tooltip title="Editar">
                                      <IconButton size="small" onClick={() => handleEdit(compra.id)} sx={actionIconSx("primary.main")}>
                                        <Icon icon="lucide:pencil" width={16} />
                                      </IconButton>
                                    </Tooltip>
                                    {compra.estado !== "anulada" && (
                                      <Tooltip title="Anular">
                                        <IconButton size="small" onClick={() => requestAnularCompra(compra)} sx={actionIconSx("error.main")}>
                                          <Icon icon="lucide:ban" width={16} />
                                        </IconButton>
                                      </Tooltip>
                                    )}
                                  </RowActions>
                                </td>
                              </tr>
                            ))}
                          </tbody>
                        </table>
                      </TableWrap>
                    )}
                  </Box>

                  <TablePaginationBar
                    page={page}
                    totalPages={totalPages}
                    onPageChange={setPage}
                    total={total}
                    shown={comprasFiltradas.length}
                    noun="compras"
                    rowsPerPage={limit}
                    onRowsPerPageChange={(n) => {
                      setLimit(n);
                      setPage(1);
                    }}
                  />
                </>
              )}
            </ListBody>
          </ListPanel>
        </>
      )}

      <Dialog open={detalleDialog.open} onClose={closeDetalleDialog} fullWidth maxWidth="lg">
        <DialogTitle sx={{ display: "flex", alignItems: "center", gap: 1, flexWrap: "wrap" }}>
          <Icon icon="mdi:file-document-outline" width={22} />
          <Box sx={{ display: "flex", flexDirection: "column" }}>
            <Typography variant="subtitle2" color="text.secondary" sx={{ lineHeight: 1 }}>
              Factura de compra
            </Typography>
            <Typography variant="h6" sx={{ fontWeight: 700, lineHeight: 1.2, fontFamily: "monospace" }}>
              {buildFacturaCompraNumero(detalleDialog.compra) || "sin número"}
            </Typography>
          </Box>
          {detalleDialog.compra?.estado && (
            <Chip
              size="small"
              label={String(detalleDialog.compra.estado).toUpperCase()}
              color={detalleDialog.compra.estado === "anulada" ? "error" : detalleDialog.compra.estado === "registrada" ? "success" : "default"}
              sx={{ ml: "auto", fontWeight: 700 }}
            />
          )}
        </DialogTitle>
        <DialogContent dividers>
          {detalleDialog.loading ? (
            <Typography variant="body2">Cargando detalle...</Typography>
          ) : !detalleDialog.compra ? (
            <Typography variant="body2" color="text.secondary">
              No se pudo obtener el detalle de la compra.
            </Typography>
          ) : (
            (() => {
              const compra = detalleDialog.compra;
              const monedaCodigo = monedaDetalle?.moneda?.codigo || compra?.moneda?.codigo || "PYG";
              const descTipo = String(compra?.descuento_tipo || "por_item");
              const totalDescuento = parseNumber(compra?.total_descuento, 0);
              const descuentoGlobalMonto = parseNumber(compra?.descuento_global_monto, 0);
              const descuentoGlobalPct = parseNumber(compra?.descuento_global_pct, 0);
              const descuentoItems = Math.max(totalDescuento - descuentoGlobalMonto, 0);
              const subtotalFinal = parseNumber(compra?.subtotal, 0);
              const totalIva10 = parseNumber(compra?.total_iva_10, 0);
              const totalIva5 = parseNumber(compra?.total_iva_5, 0);
              const totalExentas = parseNumber(compra?.total_exentas, 0);
              const totalFinal = parseNumber(compra?.total, 0);
              const totalBruto = subtotalFinal + totalDescuento;
              const baseGravada = subtotalFinal - totalIva10 - totalIva5 - totalExentas;
              const items = compra?.compra_det || [];
              const pagos = compra?.compra_forma_pagos || [];
              const cuotas = compra?.cuentas_pagar || [];
              const ordenCompra = compra?.orden_compra_cab || null;
              const recepciones = ordenCompra?.recepcion_compra_cab || [];
              const ordenesPago = compra?.orden_pago_proveedor_cab || [];
              const notasCreditoCompra = compra?.nota_credito_compra_cab || [];
              const proveedor = compra?.proveedores?.personas || {};
              const rucFull = proveedor?.ruc ? `${proveedor.ruc}-${proveedor.dv || 0}` : "-";
              const condicion = compra?.condicion_operacion?.descripcion || condicionesOperacion.find((c) => c.id === compra?.condicion_operacion_id)?.descripcion || "-";
              const sucursal = sucursales.find((s) => s.id === compra?.sucursal_id)?.descripcion || "-";
              const cotizacion = parseNumber(compra?.cotizacion, 1);
              const descGlobalLabel = descTipo === "global_pct" ? `Global ${descuentoGlobalPct}%` : descTipo === "global_monto" ? "Global (monto fijo)" : null;

              return (
                <Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
                  {/* Proveedor */}
                  <Paper variant="outlined" sx={{ p: { xs: 1.25, sm: 1.5 } }}>
                    <Typography variant="overline" color="primary" sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 0.5, letterSpacing: 0.5 }}>
                      <Icon icon="mdi:store-outline" width={16} /> Proveedor
                    </Typography>
                    <Grid container spacing={1.25}>
                      <Grid item xs={12} sm={7}>
                        <Typography variant="caption" color="text.secondary">
                          Razón social
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 700 }}>
                          {proveedor?.razon_social || "-"}
                        </Typography>
                      </Grid>
                      <Grid item xs={12} sm={5}>
                        <Typography variant="caption" color="text.secondary">
                          RUC
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 700, fontFamily: "monospace" }}>
                          {rucFull}
                        </Typography>
                      </Grid>
                    </Grid>
                  </Paper>

                  {/* Documento + Operación */}
                  <Paper variant="outlined" sx={{ p: { xs: 1.25, sm: 1.5 } }}>
                    <Typography variant="overline" color="primary" sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 0.5, letterSpacing: 0.5 }}>
                      <Icon icon="mdi:receipt-text-outline" width={16} /> Documento y operación
                    </Typography>
                    <Grid container spacing={1.25}>
                      <Grid item xs={6} sm={3}>
                        <Typography variant="caption" color="text.secondary">
                          Fecha emisión
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 700 }}>
                          {formatCompraDate(compra?.fecha_emision)}
                        </Typography>
                      </Grid>
                      <Grid item xs={6} sm={3}>
                        <Typography variant="caption" color="text.secondary">
                          Timbrado
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 700, fontFamily: "monospace" }}>
                          {compra?.timbrado_proveedor || "-"}
                        </Typography>
                      </Grid>
                      <Grid item xs={6} sm={3}>
                        <Typography variant="caption" color="text.secondary">
                          Condición
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 700 }}>
                          {condicion}
                        </Typography>
                      </Grid>
                      <Grid item xs={6} sm={3}>
                        <Typography variant="caption" color="text.secondary">
                          Moneda
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 700 }}>
                          {monedaCodigo}
                          {cotizacion && cotizacion !== 1 ? (
                            <Typography component="span" variant="caption" color="text.secondary" sx={{ ml: 0.5 }}>
                              @ {cotizacion.toLocaleString("es-PY")}
                            </Typography>
                          ) : null}
                        </Typography>
                      </Grid>
                      <Grid item xs={6} sm={3}>
                        <Typography variant="caption" color="text.secondary">
                          Sucursal
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 700 }}>
                          {sucursal}
                        </Typography>
                      </Grid>
                      <Grid item xs={6} sm={3}>
                        <Typography variant="caption" color="text.secondary">
                          Depósito
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 700 }}>
                          {compra?.deposito?.descripcion || "-"}
                        </Typography>
                      </Grid>
                      {compra?.fecha_vencimiento_timbrado && (
                        <Grid item xs={6} sm={3}>
                          <Typography variant="caption" color="text.secondary">
                            Vto. timbrado
                          </Typography>
                          <Typography variant="body2" sx={{ fontWeight: 700 }}>
                            {formatCompraDate(compra?.fecha_vencimiento_timbrado)}
                          </Typography>
                        </Grid>
                      )}
                      {parseNumber(compra?.plazo_credito, 0) > 0 && (
                        <Grid item xs={6} sm={3}>
                          <Typography variant="caption" color="text.secondary">
                            Plazo crédito
                          </Typography>
                          <Typography variant="body2" sx={{ fontWeight: 700 }}>
                            {parseNumber(compra?.plazo_credito, 0)} días
                          </Typography>
                        </Grid>
                      )}
                    </Grid>
                  </Paper>

                  {/* Documentos vinculados */}
                  <Paper variant="outlined" sx={{ p: { xs: 1.25, sm: 1.5 } }}>
                    <Typography variant="overline" color="primary" sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 1, letterSpacing: 0.5 }}>
                      <Icon icon="mdi:link-variant" width={16} /> Documentos vinculados
                    </Typography>
                    <Grid container spacing={1.25}>
                      <Grid item xs={12} sm={6} lg={3}>
                        <Box sx={{ height: "100%", p: 1.25, border: 1, borderColor: "divider", borderRadius: 1.5 }}>
                          <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, mb: 0.75 }}>
                            <Typography variant="caption" color="text.secondary" sx={{ display: "flex", alignItems: "center", gap: 0.5, fontWeight: 700 }}>
                              <Icon icon="mdi:clipboard-text-outline" width={16} /> ORDEN DE COMPRA (OC)
                            </Typography>
                            <Chip size="small" variant="outlined" color={ordenCompra ? "primary" : "default"} label={ordenCompra ? "VINCULADA" : "SIN VÍNCULO"} />
                          </Box>
                          {ordenCompra ? (
                            <>
                              <Typography variant="body2" color="primary" sx={{ fontWeight: 700, fontFamily: "monospace" }}>
                                {ordenCompra.numero_orden || "Sin número"}
                              </Typography>
                              <Box sx={{ mt: 0.75, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1 }}>
                                <Typography variant="caption" color="text.secondary">
                                  {formatCompraDate(ordenCompra.fecha_emision)}
                                </Typography>
                                <Chip
                                  size="small"
                                  variant="outlined"
                                  color={ordenCompra.estado === "completada" ? "success" : ordenCompra.estado === "cancelada" ? "error" : "warning"}
                                  label={String(ordenCompra.estado || "-").toUpperCase()}
                                />
                              </Box>
                            </>
                          ) : (
                            <Typography variant="body2" color="text.secondary">
                              La factura no está vinculada a una OC.
                            </Typography>
                          )}
                        </Box>
                      </Grid>

                      <Grid item xs={12} sm={6} lg={3}>
                        <Box sx={{ height: "100%", p: 1.25, border: 1, borderColor: "divider", borderRadius: 1.5 }}>
                          <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, mb: 0.75 }}>
                            <Typography variant="caption" color="text.secondary" sx={{ display: "flex", alignItems: "center", gap: 0.5, fontWeight: 700 }}>
                              <Icon icon="mdi:truck-check-outline" width={16} /> RECEPCIONES (REP)
                            </Typography>
                            <Chip size="small" variant="outlined" color={recepciones.length ? "primary" : "default"} label={recepciones.length} />
                          </Box>
                          {recepciones.length ? (
                            <Box sx={{ display: "flex", flexDirection: "column", gap: 0.75 }}>
                              {recepciones.map((recepcion) => (
                                <Box key={recepcion.id} sx={{ pb: 0.75, borderBottom: "1px dashed", borderColor: "divider", "&:last-child": { pb: 0, borderBottom: 0 } }}>
                                  <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1 }}>
                                    <Typography variant="body2" color="primary" sx={{ fontWeight: 700, fontFamily: "monospace" }}>
                                      Recepción del {formatCompraDate(recepcion.fecha_recepcion)}
                                    </Typography>
                                    <Chip
                                      size="small"
                                      variant="outlined"
                                      color={recepcion.anulado || recepcion.estado === "anulada" ? "error" : "success"}
                                      label={String(recepcion.estado || "-").toUpperCase()}
                                    />
                                  </Box>
                                </Box>
                              ))}
                            </Box>
                          ) : (
                            <Typography variant="body2" color="text.secondary">
                              {ordenCompra ? "La OC no tiene recepciones registradas." : "Sin OC vinculada para consultar recepciones."}
                            </Typography>
                          )}
                        </Box>
                      </Grid>

                      <Grid item xs={12} sm={6} lg={3}>
                        <Box sx={{ height: "100%", p: 1.25, border: 1, borderColor: "divider", borderRadius: 1.5 }}>
                          <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, mb: 0.75 }}>
                            <Typography variant="caption" color="text.secondary" sx={{ display: "flex", alignItems: "center", gap: 0.5, fontWeight: 700 }}>
                              <Icon icon="mdi:cash-check" width={16} /> ÓRDENES DE PAGO (OP)
                            </Typography>
                            <Chip size="small" variant="outlined" color={ordenesPago.length ? "primary" : "default"} label={ordenesPago.length} />
                          </Box>
                          {ordenesPago.length ? (
                            <Box sx={{ display: "flex", flexDirection: "column", gap: 0.75 }}>
                              {ordenesPago.map((ordenPago) => (
                                <Box key={ordenPago.id} sx={{ pb: 0.75, borderBottom: "1px dashed", borderColor: "divider", "&:last-child": { pb: 0, borderBottom: 0 } }}>
                                  <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1 }}>
                                    <Typography variant="body2" color="primary" sx={{ fontWeight: 700, fontFamily: "monospace" }}>
                                      {ordenPago.numero_orden_pago || "Sin número"}
                                    </Typography>
                                    <Chip
                                      size="small"
                                      variant="outlined"
                                      color={
                                        ordenPago.anulado || String(ordenPago.estado || "").toUpperCase() === "ANULADA"
                                          ? "error"
                                          : String(ordenPago.estado || "").toUpperCase() === "PAGADO"
                                            ? "success"
                                            : "warning"
                                      }
                                      label={String(ordenPago.estado || "-").toUpperCase()}
                                    />
                                  </Box>
                                  <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1 }}>
                                    <Typography variant="caption" color="text.secondary">
                                      {formatCompraDate(ordenPago.fecha_pago || ordenPago.created_at)} · {ordenPago.origen === "compra_auto" ? "Automática" : "Manual"}
                                    </Typography>
                                    <Typography variant="caption" sx={{ fontWeight: 700 }}>
                                      {formatMontoByMoneda(parseNumber(ordenPago.monto_total, 0), monedaDetalle)}
                                    </Typography>
                                  </Box>
                                </Box>
                              ))}
                            </Box>
                          ) : (
                            <Typography variant="body2" color="text.secondary">
                              La factura no tiene órdenes de pago vinculadas.
                            </Typography>
                          )}
                        </Box>
                      </Grid>

                      <Grid item xs={12} sm={6} lg={3}>
                        <Box sx={{ height: "100%", p: 1.25, border: 1, borderColor: "divider", borderRadius: 1.5 }}>
                          <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, mb: 0.75 }}>
                            <Typography variant="caption" color="text.secondary" sx={{ display: "flex", alignItems: "center", gap: 0.5, fontWeight: 700 }}>
                              <Icon icon="mdi:file-document-minus-outline" width={16} /> NOTAS DE CRÉDITO (NC)
                            </Typography>
                            <Chip size="small" variant="outlined" color={notasCreditoCompra.length ? "primary" : "default"} label={notasCreditoCompra.length} />
                          </Box>
                          {notasCreditoCompra.length ? (
                            <Box sx={{ display: "flex", flexDirection: "column", gap: 0.75 }}>
                              {notasCreditoCompra.map((nc) => {
                                const nroNc = [nc.establecimiento, nc.punto_expedicion, nc.numero].filter(Boolean).join("-") || "Sin número";
                                const anulada = String(nc.estado || "").toLowerCase() === "anulada";
                                return (
                                  <Box key={nc.id} sx={{ pb: 0.75, borderBottom: "1px dashed", borderColor: "divider", "&:last-child": { pb: 0, borderBottom: 0 } }}>
                                    <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1 }}>
                                      <Typography variant="body2" color="primary" sx={{ fontWeight: 700, fontFamily: "monospace", textDecoration: anulada ? "line-through" : "none" }}>
                                        {nroNc}
                                      </Typography>
                                      <Chip
                                        size="small"
                                        variant="outlined"
                                        color={anulada ? "error" : "success"}
                                        label={anulada ? "ANULADA" : "REGISTRADA"}
                                      />
                                    </Box>
                                    <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1 }}>
                                      <Typography variant="caption" color="text.secondary">
                                        {formatCompraDate(nc.fecha_emision)}
                                      </Typography>
                                      <Typography variant="caption" sx={{ fontWeight: 700 }}>
                                        {formatMontoByMoneda(parseNumber(nc.total, 0), monedaDetalle)}
                                      </Typography>
                                    </Box>
                                  </Box>
                                );
                              })}
                            </Box>
                          ) : (
                            <Typography variant="body2" color="text.secondary">
                              La factura no tiene notas de crédito vinculadas.
                            </Typography>
                          )}
                        </Box>
                      </Grid>
                    </Grid>
                  </Paper>

                  {/* Resumen totales */}
                  <Paper variant="outlined" sx={{ p: { xs: 1.25, sm: 1.5 }, bgcolor: "action.hover" }}>
                    <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", mb: 1, flexWrap: "wrap", gap: 0.5 }}>
                      <Typography variant="overline" color="primary" sx={{ display: "flex", alignItems: "center", gap: 0.5, letterSpacing: 0.5 }}>
                        <Icon icon="mdi:calculator-variant-outline" width={16} /> Resumen económico
                      </Typography>
                      {descGlobalLabel && <Chip size="small" color="warning" variant="outlined" icon={<Icon icon="mdi:sale" width={14} />} label={`Descuento ${descGlobalLabel}`} />}
                    </Box>
                    <Box sx={{ display: "flex", flexDirection: "column", gap: 0.35 }}>
                      <Box sx={{ display: "flex", justifyContent: "space-between" }}>
                        <Typography variant="body2" color="text.secondary">
                          Total bruto (sin descuentos)
                        </Typography>
                        <Typography variant="body2" sx={{ fontWeight: 600 }}>
                          {formatMontoByMoneda(totalBruto, monedaDetalle)}
                        </Typography>
                      </Box>
                      {descuentoItems > 0 && (
                        <Box sx={{ display: "flex", justifyContent: "space-between", color: "warning.main" }}>
                          <Typography variant="body2">Descuento por ítems</Typography>
                          <Typography variant="body2" sx={{ fontWeight: 600 }}>
                            -{formatMontoByMoneda(descuentoItems, monedaDetalle)}
                          </Typography>
                        </Box>
                      )}
                      {descuentoGlobalMonto > 0 && (
                        <Box sx={{ display: "flex", justifyContent: "space-between", color: "warning.main" }}>
                          <Typography variant="body2">Descuento global{descuentoGlobalPct > 0 ? ` (${descuentoGlobalPct}%)` : ""}</Typography>
                          <Typography variant="body2" sx={{ fontWeight: 600 }}>
                            -{formatMontoByMoneda(descuentoGlobalMonto, monedaDetalle)}
                          </Typography>
                        </Box>
                      )}
                      <MuiDivider sx={{ my: 0.5 }} />
                      <Box sx={{ display: "flex", justifyContent: "space-between" }}>
                        <Typography variant="body2" color="text.secondary">
                          Base gravada
                        </Typography>
                        <Typography variant="body2">{formatMontoByMoneda(baseGravada, monedaDetalle)}</Typography>
                      </Box>
                      {totalIva10 > 0 && (
                        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
                          <Typography variant="body2" color="text.secondary">
                            IVA 10%
                          </Typography>
                          <Typography variant="body2">{formatMontoByMoneda(totalIva10, monedaDetalle)}</Typography>
                        </Box>
                      )}
                      {totalIva5 > 0 && (
                        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
                          <Typography variant="body2" color="text.secondary">
                            IVA 5%
                          </Typography>
                          <Typography variant="body2">{formatMontoByMoneda(totalIva5, monedaDetalle)}</Typography>
                        </Box>
                      )}
                      {totalExentas > 0 && (
                        <Box sx={{ display: "flex", justifyContent: "space-between" }}>
                          <Typography variant="body2" color="text.secondary">
                            Exentas
                          </Typography>
                          <Typography variant="body2">{formatMontoByMoneda(totalExentas, monedaDetalle)}</Typography>
                        </Box>
                      )}
                      <MuiDivider sx={{ my: 0.5 }} />
                      <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                        <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
                          Total factura
                        </Typography>
                        <Typography variant="h6" color="primary" sx={{ fontWeight: 700 }}>
                          {formatMontoByMoneda(totalFinal, monedaDetalle)}
                        </Typography>
                      </Box>
                    </Box>
                  </Paper>

                  {String(compra?.observaciones || "").trim() && (
                    <Paper variant="outlined" sx={{ p: { xs: 1.25, sm: 1.5 } }}>
                      <Typography variant="overline" color="primary" sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 0.5, letterSpacing: 0.5 }}>
                        <Icon icon="mdi:note-text-outline" width={16} /> Observaciones
                      </Typography>
                      <Typography variant="body2">{String(compra?.observaciones || "")}</Typography>
                    </Paper>
                  )}

                  <Paper variant="outlined" sx={{ p: { xs: 1.25, sm: 1.5 } }}>
                    <Typography variant="overline" color="primary" sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 0.5, letterSpacing: 0.5 }}>
                      <Icon icon="mdi:format-list-bulleted" width={16} /> Ítems ({items.length})
                    </Typography>
                    <TableContainer>
                      <Table size={isMobile ? "small" : "medium"}>
                        <TableHead>
                          <TableRow>
                            <TableCell sx={{ fontWeight: 600 }}>#</TableCell>
                            {!isMobile && <TableCell sx={{ fontWeight: 600 }}>Código</TableCell>}
                            <TableCell sx={{ fontWeight: 600 }}>Descripción</TableCell>
                            <TableCell sx={{ fontWeight: 600 }} align="right">
                              Cant.
                            </TableCell>
                            <TableCell sx={{ fontWeight: 600 }} align="right">
                              P. Unit.
                            </TableCell>
                            <TableCell sx={{ fontWeight: 600 }} align="right">
                              Desc.
                            </TableCell>
                            <TableCell sx={{ fontWeight: 600 }} align="right">
                              IVA
                            </TableCell>
                            <TableCell sx={{ fontWeight: 600 }} align="right">
                              Subtotal
                            </TableCell>
                          </TableRow>
                        </TableHead>
                        <TableBody>
                          {items.map((item, index) => {
                            const descPct = parseNumber(item?.descuento_pct ?? item?.descuento_porcentaje, 0);
                            const descMonto = parseNumber(item?.descuento_monto, 0);
                            const itemTipo = item?.descuento_tipo || (descMonto > 0 && descPct === 0 ? "monto" : "porcentaje");
                            const hasDesc = descPct > 0 || descMonto > 0;
                            return (
                              <TableRow key={item.id || index} hover>
                                <TableCell>{index + 1}</TableCell>
                                {!isMobile && <TableCell sx={{ fontFamily: "monospace", fontSize: "0.8rem" }}>{item?.productos?.cod_producto || item?.cod_producto || "S/C"}</TableCell>}
                                <TableCell>{item?.productos?.descripcion || item?.descripcion || "-"}</TableCell>
                                <TableCell align="right">{parseNumber(item?.cantidad, 0).toLocaleString("es-PY")}</TableCell>
                                <TableCell align="right">{formatMontoByMoneda(parseNumber(item?.precio_unitario, 0), monedaDetalle)}</TableCell>
                                <TableCell align="right" sx={{ color: hasDesc ? "warning.main" : "text.disabled" }}>
                                  {hasDesc ? (
                                    itemTipo === "monto" ? (
                                      formatMontoByMoneda(descMonto, monedaDetalle)
                                    ) : (
                                      <>
                                        {descPct}%
                                        {descMonto > 0 && (
                                          <Typography component="span" variant="caption" sx={{ display: "block", color: "warning.main" }}>
                                            -{formatMontoByMoneda(descMonto, monedaDetalle)}
                                          </Typography>
                                        )}
                                      </>
                                    )
                                  ) : (
                                    "—"
                                  )}
                                </TableCell>
                                <TableCell align="right">{parseNumber(item?.porcentaje_iva, 0)}%</TableCell>
                                <TableCell align="right" sx={{ fontWeight: 600 }}>
                                  {formatMontoByMoneda(parseNumber(item?.subtotal, 0), monedaDetalle)}
                                </TableCell>
                              </TableRow>
                            );
                          })}
                          {items.length === 0 && (
                            <TableRow>
                              <TableCell colSpan={isMobile ? 7 : 8} align="center">
                                <Typography variant="body2" color="text.secondary">
                                  Sin ítems
                                </Typography>
                              </TableCell>
                            </TableRow>
                          )}
                        </TableBody>
                      </Table>
                    </TableContainer>
                  </Paper>

                  {Array.isArray(pagos) && pagos.length > 0 && (
                    <Paper variant="outlined" sx={{ p: { xs: 1.25, sm: 1.5 } }}>
                      <Typography variant="overline" color="primary" sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 0.5, letterSpacing: 0.5 }}>
                        <Icon icon="mdi:credit-card-outline" width={16} /> Pagos registrados
                      </Typography>
                      <TableContainer>
                        <Table size="small">
                          <TableHead>
                            <TableRow>
                              <TableCell sx={{ fontWeight: 600 }}>Medio de pago</TableCell>
                              <TableCell sx={{ fontWeight: 600 }}>Referencia</TableCell>
                              <TableCell sx={{ fontWeight: 600 }} align="right">
                                Monto
                              </TableCell>
                            </TableRow>
                          </TableHead>
                          <TableBody>
                            {pagos.map((pago, idx) => (
                              <TableRow key={pago.id || idx} hover>
                                <TableCell>{pago?.medio_pago?.descripcion || "-"}</TableCell>
                                <TableCell>{pago?.referencia || "—"}</TableCell>
                                <TableCell align="right" sx={{ fontWeight: 600 }}>
                                  {formatMontoByMoneda(parseNumber(pago?.monto, 0), monedaDetalle)}
                                </TableCell>
                              </TableRow>
                            ))}
                          </TableBody>
                        </Table>
                      </TableContainer>
                    </Paper>
                  )}

                  {Array.isArray(cuotas) && cuotas.length > 0 && (
                    <Paper variant="outlined" sx={{ p: { xs: 1.25, sm: 1.5 } }}>
                      <Typography variant="overline" color="primary" sx={{ display: "flex", alignItems: "center", gap: 0.5, mb: 0.5, letterSpacing: 0.5 }}>
                        <Icon icon="mdi:calendar-clock-outline" width={16} /> Cuotas / cuentas por pagar
                      </Typography>
                      <TableContainer>
                        <Table size="small">
                          <TableHead>
                            <TableRow>
                              <TableCell sx={{ fontWeight: 600 }}>Cuota</TableCell>
                              <TableCell sx={{ fontWeight: 600 }}>Vencimiento</TableCell>
                              <TableCell sx={{ fontWeight: 600 }}>Estado</TableCell>
                              <TableCell sx={{ fontWeight: 600 }} align="right">
                                Saldo pendiente
                              </TableCell>
                            </TableRow>
                          </TableHead>
                          <TableBody>
                            {cuotas.map((cuota, idx) => (
                              <TableRow key={cuota.id || idx} hover>
                                <TableCell>{parseNumber(cuota?.numero_cuota, idx + 1)}</TableCell>
                                <TableCell>{formatCompraDate(cuota?.fecha_vencimiento)}</TableCell>
                                <TableCell>
                                  <Chip
                                    size="small"
                                    label={String(cuota?.estado || "-").toUpperCase()}
                                    color={cuota?.estado === "pagada" ? "success" : cuota?.estado === "anulada" ? "default" : "warning"}
                                    variant="outlined"
                                  />
                                </TableCell>
                                <TableCell align="right" sx={{ fontWeight: 600 }}>
                                  {formatMontoByMoneda(parseNumber(cuota?.saldo_pendiente, cuota?.monto_original || 0), monedaDetalle)}
                                </TableCell>
                              </TableRow>
                            ))}
                          </TableBody>
                        </Table>
                      </TableContainer>
                    </Paper>
                  )}
                </Box>
              );
            })()
          )}
        </DialogContent>
        <DialogActions>
          <Button onClick={closeDetalleDialog}>Cerrar</Button>
        </DialogActions>
      </Dialog>

      <ConfirmDialog
        open={createConfirmOpen}
        onClose={closeCreateConfirm}
        onConfirm={handleConfirmCreate}
        loading={saving}
        title="Confirmar creación de compra"
        message={
          <Box sx={{ display: "flex", flexDirection: "column", gap: 1.25 }}>
            <Typography variant="body2">
              Se creará una compra por {formatMontoCompra(totalItems)} con {form.items.length} ítem(s).
              {generaCxpAutomatico
                ? isCredito
                  ? " Al ser una compra a crédito, se generarán las cuentas por pagar correspondientes."
                  : " Al ser una compra al contado, se aplicarán los pagos que cargaste en el formulario."
                : " La configuración actual no genera cuentas por pagar automáticamente."}
              {" ¿Deseas continuar?"}
            </Typography>
            {createValidationMessages.length > 0 && (
              <Alert severity="error" sx={{ mt: 0.5 }}>
                <Typography variant="body2" sx={{ fontWeight: 600, mb: 0.5 }}>
                  No se pudo crear la compra:
                </Typography>
                {createValidationMessages.map((msg, idx) => (
                  <Typography key={`${msg}-${idx}`} variant="body2">
                    • {msg}
                  </Typography>
                ))}
              </Alert>
            )}
          </Box>
        }
        confirmText="Crear compra"
        cancelText="Cancelar"
        confirmColor="primary"
      />

      <ConfirmDialog
        open={anularConfirmOpen}
        onClose={closeAnularConfirm}
        onConfirm={handleConfirmAnularCompra}
        loading={anulandoCompra}
        title="Confirmar anulación de compra"
        message={
          <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
            <Typography variant="body2">
              Vas a anular la compra <strong>{buildFacturaCompraNumero(pendingAnularCompra)}</strong>.
            </Typography>
            <Typography variant="body2" color="text.secondary">
              Se revertirán únicamente los movimientos de stock generados por esta factura, se recalcularán costos y se anularán sus estados financieros relacionados.
            </Typography>
            {!afectaStockEnCompraDirecta && recepcionHabilitada && (
              <Typography variant="body2" color="warning.main">
                Las recepciones son documentos independientes y no se anulan junto con la factura. Si también necesitás revertir la mercadería, anulá la recepción correspondiente.
              </Typography>
            )}
            <Typography variant="body2" color="text.secondary">
              Proveedor: <strong>{pendingAnularCompra?.proveedores?.personas?.razon_social || "-"}</strong> | Total:{" "}
              <strong>{fmtMoneda(parseNumber(pendingAnularCompra?.total, 0), pendingAnularCompra?.moneda?.codigo || "PYG")}</strong>
            </Typography>
            <Typography variant="body2" color="warning.main" sx={{ fontWeight: 600 }}>
              No podrás seguir editándola después de anularla.
            </Typography>
          </Box>
        }
        confirmText="Sí, anular compra"
        cancelText="Volver"
        confirmColor="error"
      />
    </Container>
  );
}

const Container = styled.div`
  margin-top: 50px;
  padding: 16px;
`;

const TopCard = styled.div`
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: center;
  margin-bottom: 14px;
  flex-wrap: wrap;
  background: ${({ theme }) => theme.bg2};
  border: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
  border-radius: 12px;
  padding: 14px;
`;

const TitleWrap = styled.div`
  display: flex;
  gap: 10px;
  align-items: flex-start;

  svg {
    font-size: 24px;
    margin-top: 4px;
  }

  h1 {
    margin: 0;
    font-size: 1.2rem;
  }

  p {
    margin: 4px 0 0;
    opacity: 0.8;
    font-size: 0.85rem;
  }
`;

const PanelBase = styled.section`
  background: ${({ theme }) => theme.bg2};
  border: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
  border-radius: 12px;
  padding: 14px;
`;

const FormPanel = styled(PanelBase)``;
const ListPanel = styled(PanelBase)`
  padding: 0;
  overflow: hidden;
`;

const ListBody = styled.div`
  padding: 14px;
`;

const FormHead = styled.div`
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
`;

const SectionTitle = styled.h3`
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
  font-size: 0.9rem;
  font-weight: 600;
  color: ${({ theme }) => theme.primary || "#7dd3a8"};
`;

const FieldsGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 10px;

  @media (max-width: 1700px) {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  @media (max-width: 1360px) {
    grid-template-columns: repeat(3, 1fr);
  }

  @media (max-width: 980px) {
    grid-template-columns: repeat(2, 1fr);
  }

  @media (max-width: 640px) {
    grid-template-columns: 1fr;
  }
`;

/** Celda que ocupa la fila completa, sin importar cuántas columnas haya. */
const FieldFullRow = styled.div`
  min-width: 0;
  grid-column: 1 / -1;
`;

const Field = styled.div`
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  grid-column: span ${({ $span }) => $span || 1};

  @media (max-width: 768px) {
    grid-column: span 1;
  }

  /* Ya no hay controles nativos acá dentro: los campos son de MUI y traen su
     propio label flotante, así que Field quedó como pura celda de grilla. */
`;


const Divider = styled.hr`
  border: 0;
  height: 1px;
  background: ${({ theme }) => theme.color2 || theme.bg4};
  margin: 14px 0;
`;

const InlineActions = styled.div`
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 10px;

  span {
    opacity: 0.8;
    font-size: 0.84rem;
  }
`;

const CreditHint = styled.div`
  margin-top: 6px;
  padding: 10px 12px;
  border: 1px dashed ${({ theme }) => theme.color2 || theme.bg4};
  border-radius: 8px;
  font-size: 0.86rem;
  color: ${({ theme }) => theme.textSecondary || theme.text};
`;

const TableWrap = styled.div`
  overflow: auto;
  margin-bottom: 8px;
  border: ${({ $listView, theme }) => ($listView ? `1px solid ${theme.color2 || theme.bg4}` : "none")};
  border-radius: 10px;

  table {
    width: 100%;
    border-collapse: collapse;
  }

  th,
  td {
    border-bottom: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
    padding: 10px 12px;
    text-align: left;
    white-space: nowrap;
    vertical-align: middle;
  }

  th {
    font-size: ${({ $listView }) => ($listView ? "0.95rem" : "0.75rem")};
    font-weight: ${({ $listView }) => ($listView ? 700 : 600)};
    text-transform: ${({ $listView }) => ($listView ? "none" : "uppercase")};
    letter-spacing: ${({ $listView }) => ($listView ? "0" : "0.4px")};
    background: ${({ theme, $listView }) => ($listView ? theme.bg3 : "transparent")};
  }

  tbody tr:hover {
    background: ${({ theme, $listView }) => ($listView ? `${theme.primary || "#5b7fff"}10` : "transparent")};
  }

  td input[type="checkbox"] {
    width: 16px;
    height: 16px;
  }
`;

const RowActions = styled.div`
  display: inline-flex;
  align-items: center;
  gap: 6px;
`;

const ProviderCol = styled.div`
  display: flex;
  flex-direction: column;
  gap: 2px;

  strong {
    font-size: 0.92rem;
  }

  small {
    opacity: 0.72;
  }
`;

const SubCard = styled.div`
  border: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
  border-radius: 10px;
  padding: 10px;
  margin-bottom: 10px;
  background: ${({ theme }) => theme.bg3};
`;

const SubGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 8px;

  @media (max-width: 900px) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  @media (max-width: 540px) {
    grid-template-columns: 1fr;
  }
`;

/**
 * La cabecera ocupa el ancho completo y el resumen baja a la fila de los ítems.
 *
 * Antes el resumen compartía fila con la cabecera y eso causaba cuatro
 * problemas de una sola vez: le robaba 380px al formulario (la grilla de campos
 * quedaba comprimida y despareja), era más alto que la cabecera (dejaba un
 * hueco vertical grande), mostraba 0,00 en todas las filas porque todavía no
 * hay ítems cargados, y repetía el total que ya vive en la barra fija del pie.
 */
const FormLayout = styled.div`
  margin-top: 8px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 340px);
  gap: 14px;
  align-items: start;

  @media (max-width: 1240px) {
    grid-template-columns: 1fr;
  }

  @media (max-width: 900px) {
    gap: 10px;
  }
`;

const FormMain = styled.div`
  min-width: 0;
  grid-column: 1 / -1;
  grid-row: 1;
`;

const FormFullWidth = styled.div`
  min-width: 0;
  grid-column: 1;
  grid-row: 2;
`;

const SummaryAside = styled.aside`
  min-width: 0;
  grid-column: 2;
  grid-row: 2;
  position: sticky;
  top: 12px;
  align-self: start;
  justify-self: stretch;

  @media (max-width: 1240px) {
    grid-column: 1;
    grid-row: 3;
    position: static;
  }
`;

const SummaryCard = styled.div`
  width: 100%;
  border: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
  border-radius: 12px;
  background: ${({ theme }) => theme.bg2};
  overflow: hidden;
`;

const SummaryHeader = styled.div`
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 16px;
  background: ${({ theme }) => theme.bg3};
  border-bottom: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
  font-weight: 700;
  font-size: 1.02rem;

  svg {
    color: ${({ theme }) => theme.primary || "#5b7fff"};
  }
`;

const SummaryBody = styled.div`
  padding: 12px 16px 14px;
`;

const SummaryField = styled.div`
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 14px;

  label {
    font-size: 0.76rem;
    font-weight: 600;
    color: ${({ theme }) => theme.textSecondary || theme.text};
    text-transform: uppercase;
    letter-spacing: 0.55px;
  }

  select,
  input {
    min-height: 42px;
    padding: 10px 12px;
    border: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
    border-radius: 8px;
    background: ${({ theme }) => theme.bg3};
    color: ${({ theme }) => theme.text};
    font-size: 0.96rem;
  }
`;

const SummaryRows = styled.div`
  display: flex;
  flex-direction: column;
  gap: 10px;
`;

const SummaryDivider = styled.hr`
  border: 0;
  border-top: 1px dashed ${({ theme }) => theme.color2 || theme.bg4};
  margin: 2px 0;
`;

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

  .label {
    font-size: ${({ $main }) => ($main ? "1.12rem" : "0.95rem")};
    font-weight: ${({ $main }) => ($main ? 800 : 500)};
    letter-spacing: ${({ $main }) => ($main ? "0.35px" : "0.1px")};
  }

  .value {
    font-size: ${({ $main }) => ($main ? "2rem" : "1.02rem")};
    font-weight: ${({ $main }) => ($main ? 800 : 700)};
    color: ${({ theme, $main }) => ($main ? theme.primary || "#5b7fff" : theme.text)};
    line-height: 1;
  }

  @media (max-width: 640px) {
    .label {
      font-size: ${({ $main }) => ($main ? "1rem" : "0.9rem")};
    }

    .value {
      font-size: ${({ $main }) => ($main ? "1.6rem" : "0.98rem")};
    }
  }
`;

/**
 * Barra de acción pegada al pie de la ventana.
 *
 * El formulario de compra mide alrededor de dos pantallas, así que el botón de
 * guardar quedaba fuera de vista casi siempre y había que scrollear entre el
 * total y el botón. Con `sticky` la barra acompaña el scroll.
 */
const Footer = styled.div`
  position: sticky;
  bottom: 0;
  z-index: 5;
  margin: 14px -14px -14px;
  padding: 12px 14px;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  background: ${({ theme }) => theme.bg2};
  border-top: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
  /* Sin esto la barra se confunde con el contenido que pasa por debajo. */
  box-shadow: 0 -6px 16px -12px rgba(0, 0, 0, 0.55);
`;

const BarActions = styled.div`
  width: min(460px, 100%);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
`;

const FooterActionButton = styled.button`
  border: none;
  border-radius: 10px;
  padding: 12px 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;

  ${({ $variant, theme }) =>
    $variant === "primary"
      ? `
        background: ${theme.primary || "#5b7fff"};
        color: #fff;
        &:hover:not(:disabled) {
          filter: brightness(1.1);
          transform: translateY(-1px);
        }
      `
      : `
        background: rgba(220, 53, 69, 0.1);
        color: #dc3545;
        &:hover:not(:disabled) {
          background: rgba(220, 53, 69, 0.2);
        }
      `}

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  svg {
    font-size: 1.1rem;
  }
`;

const GhostButton = styled.button.attrs({ type: "button" })`
  border: 1px solid ${({ theme }) => theme.color2 || theme.bg4};
  background: transparent;
  color: ${({ theme }) => theme.text};
  padding: 8px 10px;
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
`;

const badgeByState = {
  pendiente: "#f59e0b",
  parcial: "#3b82f6",
  pagada: "#16a34a",
  anulada: "#dc2626",
};

const Badge = styled.span`
  border-radius: 999px;
  padding: 4px 9px;
  font-size: 0.72rem;
  font-weight: 700;
  background: ${({ $state }) => `${badgeByState[$state] || "#6b7280"}20`};
  color: ${({ $state }) => badgeByState[$state] || "#6b7280"};
  border: 1px solid ${({ $state }) => `${badgeByState[$state] || "#6b7280"}40`};
  text-transform: uppercase;
  letter-spacing: 0.4px;
`;

const EmptyState = styled.div`
  border: 1px dashed ${({ theme }) => theme.color2 || theme.bg4};
  border-radius: 10px;
  padding: 16px;
  text-align: center;
  opacity: 0.8;
`;
