import { Icon } from "@iconify/react";
import {
  Autocomplete,
  Box,
  Button,
  Card,
  CardContent,
  Chip,
  FormControl,
  IconButton,
  InputAdornment,
  InputLabel,
  MenuItem,
  Select,
  Skeleton,
  TextField,
  Tooltip,
  Typography,
  useMediaQuery,
  useTheme,
} from "@mui/material";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect, useMemo, useState } from "react";
import styled from "styled-components";
import { toast } from "sonner";
import * as XLSX from "xlsx";
import * as pdfMake from "pdfmake/build/pdfmake";
import "pdfmake/build/vfs_fonts";
import { getDepositos, getStockByDeposito } from "../../api";
import { updateStockLimites } from "../../api/stock.service";
import { getCategorias } from "../../api/categorias.service";
import { getMarcas } from "../../api/marcas.service";
import { usePermission } from "../../hooks/usePermission";
import { useSucursalesStore } from "../../store/SucursalesStore";
import { useMostrarSucursalesQuery } from "../../tanstack/SucursalesStack";
import { getErrorApi } from "../../utils/errorUtils";
import { AccesoRestringido } from "../common/AccesoRestringido";
import { PdfPreviewModal } from "../common/PdfPreviewModal";
import { RequiredDataGuard } from "../common/RequiredDataGuard";
import { EmptyState, ScreenGuia, StandardTable } from "../_standards";
import { sanitizeRows } from "../_standards";
import { fmtMoneda } from "../_standards/enums/monedas";
import { hoyISO } from "../../utils/fecha";

// De dónde sale cada precio (backend: stock.service preciosPorProducto).
const FUENTE_COSTO = Object.freeze({
  lote: "Costo promedio ponderado de los lotes en este depósito",
  producto: "Precio de costo del producto",
});
const fuenteVenta = (venta) =>
  venta?.fuente === "lista" ? `Lista de precios: ${venta.lista || "general"}` : "Precio base del producto";

// cantidad × precio unitario, en la moneda del precio (null si no hay precio).
const totalPrecio = (precio, cantidad) =>
  precio && precio.valor != null ? { valor: Number(precio.valor) * Number(cantidad || 0), moneda: precio.moneda } : null;

// Suma por moneda: no se mezclan monedas distintas en un solo número.
const sumarPorMoneda = (totales) => {
  const acc = new Map();
  for (const t of totales) {
    if (!t) continue;
    acc.set(t.moneda, (acc.get(t.moneda) || 0) + t.valor);
  }
  return [...acc.entries()].map(([moneda, valor]) => ({ moneda, valor }));
};
const fmtSumas = (sumas) => (sumas.length ? sumas.map((s) => fmtMoneda(s.valor, s.moneda)).join(" + ") : "—");

const PrecioCell = ({ precio, titulo }) => {
  if (!precio || precio.valor == null) return "—";
  return (
    <Tooltip title={titulo} placement="top" arrow>
      <span>{fmtMoneda(precio.valor, precio.moneda)}</span>
    </Tooltip>
  );
};

const GUIA_PASOS = [
  {
    icon: "lucide:store",
    color: "#3b82f6",
    titulo: "1. Elegí sucursal y depósito",
    desc: "El stock se controla por depósito. Seleccioná dónde querés ver o ajustar existencias.",
  },
  {
    icon: "lucide:edit-3",
    color: "#f59e0b",
    titulo: "2. Editá límites in-line",
    desc: "Hacé clic en Mín., Máx. o Ubicación para modificar sin abrir formulario.",
  },
  {
    icon: "lucide:alert-triangle",
    color: "#ef4444",
    titulo: "3. Monitoreá bajos",
    desc: "El semáforo destaca productos por debajo del mínimo o sin stock disponible.",
  },
];

// ── Styled ────────────────────────────────────────────────────────
const Wrapper = styled.div`
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  height: 100%;
`;
const SectionBar = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem;
  .title-block {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    .title { font-size: 1.05rem; font-weight: 700; color: ${(p) => p.theme.text}; line-height: 1.2; }
    .subtitle { font-size: 0.78rem; color: ${(p) => p.theme.textSecondary}; }
  }
`;
// Tarjetas de indicadores: se acomodan por ancho del contenedor (sin @media).
const KpiGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 0.75rem;
  margin-bottom: 1rem;
`;

const KpiCard = ({ icon, label, value, sub, color, hint, onClick, active }) => {
  const card = (
    <Card
      variant="outlined"
      onClick={onClick}
      sx={{
        borderRadius: 2,
        cursor: onClick ? "pointer" : "default",
        borderColor: active ? "warning.main" : undefined,
        bgcolor: active ? "action.selected" : undefined,
        "&:hover": onClick ? { borderColor: "primary.main" } : undefined,
      }}
    >
      <CardContent sx={{ p: "12px 14px !important", display: "flex", alignItems: "center", gap: 1.25 }}>
        <Icon icon={icon} width={22} style={{ opacity: 0.6, flexShrink: 0 }} />
        <Box sx={{ minWidth: 0 }}>
          <Typography variant="caption" color="text.secondary" noWrap display="block">{label}</Typography>
          <Typography variant="subtitle1" fontWeight={700} color={color} noWrap lineHeight={1.25}>{value}</Typography>
          {sub && <Typography variant="caption" color="text.secondary" noWrap display="block">{sub}</Typography>}
        </Box>
      </CardContent>
    </Card>
  );
  return hint ? <Tooltip title={hint} arrow placement="top">{card}</Tooltip> : card;
};

const FiltersBox = styled.div`
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  align-items: center;
  padding: 0.85rem 1rem;
  background: ${(p) => p.theme.bg};
  border: 1px solid ${(p) => p.theme.borderColor};
  border-radius: 12px;
`;
const HintText = styled.div`
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: ${(p) => p.theme.textSecondary};
  font-size: 0.78rem;
  margin-left: auto;
`;
function EditableCell({ row, field, type = "number", onSave }) {
  const [editing, setEditing] = useState(false);
  const [value, setValue] = useState(row[field] ?? "");

  const handleSave = () => {
    const numValue = type === "number" ? Number(value) || 0 : value;
    onSave(row.id, { [field]: numValue });
    setEditing(false);
  };

  const handleCancel = () => {
    setValue(row[field] ?? "");
    setEditing(false);
  };

  if (editing) {
    return (
      <Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
        <TextField
          size="small"
          type={type}
          value={value}
          onChange={(e) => setValue(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === "Enter") handleSave();
            if (e.key === "Escape") handleCancel();
          }}
          autoFocus
          sx={{ width: 90, "& input": { py: 0.5, px: 1, fontSize: "0.85rem" } }}
          inputProps={type === "number" ? { min: 0, step: 1 } : {}}
        />
        <IconButton size="small" onClick={handleSave} color="success" sx={{ p: 0.25 }}>
          <Icon icon="lucide:check" width={16} />
        </IconButton>
        <IconButton size="small" onClick={handleCancel} sx={{ p: 0.25 }}>
          <Icon icon="lucide:x" width={16} />
        </IconButton>
      </Box>
    );
  }

  const displayValue =
    type === "number"
      ? Number(row[field] || 0).toLocaleString("es-PY")
      : row[field] || "-";

  return (
    <Box
      sx={{
        display: "flex",
        alignItems: "center",
        gap: 0.5,
        cursor: "pointer",
        "&:hover .edit-icon": { opacity: 1 },
      }}
      onClick={() => setEditing(true)}
    >
      <Typography variant="body2">{displayValue}</Typography>
      <Box className="edit-icon" sx={{ opacity: 0, transition: "0.2s", color: "text.secondary", display: "inline-flex" }}>
        <Icon icon="lucide:edit-3" width={14} />
      </Box>
    </Box>
  );
}

export function StockTab() {
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down("md"));
  const { can } = usePermission("INVENTARIO");
  const canRead = can("INV_STK_STOCK_VER");
  const canEdit = can("INV_STK_STOCK_AJUSTAR");
  const canVerCosto = can("INV_STK_COSTO_VER");
  const [depositoId, setDepositoId] = useState("");
  const [busqueda, setBusqueda] = useState("");
  const [categoriasSel, setCategoriasSel] = useState([]);
  const [marcasSel, setMarcasSel] = useState([]);
  const [soloBajos, setSoloBajos] = useState(false);
  const [pdfModalOpen, setPdfModalOpen] = useState(false);
  const [pdfDocDef, setPdfDocDef] = useState(null);
  const [pageSize, setPageSize] = useState(25);
  const [pdfFilename, setPdfFilename] = useState("stock.pdf");
  const { sucursalesItemSelect, selectSucursal } = useSucursalesStore();
  const queryClient = useQueryClient();

  const { data: sucursalesData, isLoading: loadingSucursales } = useMostrarSucursalesQuery();
  const sucursales = Array.isArray(sucursalesData) ? sucursalesData : [];

  const { data: depositosData = [], isSuccess: depositosCargados } = useQuery({
    queryKey: ["depositos", sucursalesItemSelect?.id],
    queryFn: () => getDepositos(sucursalesItemSelect?.id),
    enabled: !!sucursalesItemSelect?.id,
  });
  const depositos = useMemo(() => (Array.isArray(depositosData) ? depositosData : []), [depositosData]);

  // La sucursal puede cambiar sin tocar el select (al elegir otro rubro en el
  // sidebar): si el depósito elegido no es de la sucursal actual, se suelta,
  // si no la tabla queda mostrando el stock del depósito anterior.
  useEffect(() => {
    if (depositoId && depositosCargados && !depositos.some((d) => d.id === depositoId)) {
      setDepositoId("");
    }
  }, [depositoId, depositos, depositosCargados]);

  const {
    data: stock = [],
    isLoading,
    error: stockError,
  } = useQuery({
    queryKey: ["stock-deposito", depositoId],
    queryFn: () => getStockByDeposito(depositoId),
    enabled: !!depositoId,
  });

  // Catálogos para los filtros (cargados una vez).
  const { data: categoriasResp } = useQuery({
    queryKey: ["categorias-all-stock"],
    queryFn: () => getCategorias(1, 500),
    staleTime: 5 * 60_000,
  });
  const categorias = categoriasResp?.data ?? [];

  const { data: marcasResp = [] } = useQuery({
    queryKey: ["marcas-all-stock"],
    queryFn: () => getMarcas(),
    select: (d) => (d?.data ?? d ?? []).filter((m) => m.active !== false),
    staleTime: 5 * 60_000,
  });
  const marcas = Array.isArray(marcasResp) ? marcasResp : [];

  // Filtrado en cliente sobre el stock del depósito.
  const stockFiltrado = useMemo(() => {
    const q = busqueda.trim().toLowerCase();
    const catIds = new Set(categoriasSel.map((c) => c.id));
    const marIds = new Set(marcasSel.map((m) => m.id));
    return stock.filter((s) => {
      const p = s.producto || {};
      if (q) {
        const hay = [p.descripcion, p.nombre, p.cod_producto, p.codigobarra]
          .filter(Boolean)
          .some((v) => String(v).toLowerCase().includes(q));
        if (!hay) return false;
      }
      if (catIds.size && !catIds.has(p.categoria_id)) return false;
      if (marIds.size && !marIds.has(p.marca_id)) return false;
      if (soloBajos) {
        const disp = Number(s.cantidad_disponible || 0);
        const min = Number(s.stock_minimo || 0);
        const esBajo = disp <= 0 || (min > 0 && disp <= min);
        if (!esBajo) return false;
      }
      return true;
    });
  }, [stock, busqueda, categoriasSel, marcasSel, soloBajos]);

  const updateMutation = useMutation({
    mutationFn: ({ stockId, data }) => updateStockLimites(stockId, data),
    onSuccess: () => {
      toast.success("Límites actualizados");
      queryClient.invalidateQueries({ queryKey: ["stock-deposito", depositoId] });
    },
    onError: (err) => {
      toast.error(err.response?.data?.message || "Error al actualizar");
    },
  });

  const handleSaveLimite = (stockId, data) => {
    updateMutation.mutate({ stockId, data });
  };

  // Mapas para enriquecer exports con descripciones de catálogo.
  const categoriasMap = useMemo(
    () => new Map(categorias.map((c) => [c.id, c.descripcion])),
    [categorias],
  );
  const marcasMap = useMemo(
    () => new Map(marcas.map((m) => [m.id, m.descripcion])),
    [marcas],
  );

  const filasParaExport = useMemo(
    () =>
      stockFiltrado.map((s) => {
        const p = s.producto || {};
        return {
          codigo: p.cod_producto || p.codigobarra || "",
          descripcion: p.descripcion || p.nombre || "",
          categoria: categoriasMap.get(p.categoria_id) || "",
          marca: marcasMap.get(p.marca_id) || "",
          disponible: Number(s.cantidad_disponible || 0),
          reservada: Number(s.cantidad_reservada || 0),
          minimo: Number(s.stock_minimo || 0),
          maximo: s.stock_maximo ? Number(s.stock_maximo) : null,
          ubicacion: s.ubicacion || "",
          costo: canVerCosto ? s.precios?.costo : null,
          venta: s.precios?.venta,
          totalCosto: canVerCosto ? totalPrecio(s.precios?.costo, s.cantidad_disponible) : null,
          totalVenta: totalPrecio(s.precios?.venta, s.cantidad_disponible),
        };
      }),
    [stockFiltrado, categoriasMap, marcasMap, canVerCosto],
  );
  const fmtPrecio = (p) => (p && p.valor != null ? fmtMoneda(p.valor, p.moneda) : "—");

  // Inventario valorizado (lo que hay en pantalla tras los filtros).
  const valorizacion = useMemo(
    () => ({
      costo: sumarPorMoneda(filasParaExport.map((r) => r.totalCosto)),
      venta: sumarPorMoneda(filasParaExport.map((r) => r.totalVenta)),
    }),
    [filasParaExport],
  );

  // Indicadores operativos del depósito (sobre lo filtrado, igual que la
  // valorización): cuántos productos hay que reponer, cuántos están en cero o
  // negativos (diferencias por corregir con un ajuste) y cuánto margen deja el
  // inventario si se vende todo al precio de lista.
  const indicadores = useMemo(() => {
    let unidades = 0;
    let bajos = 0;
    let sinStock = 0;
    let negativos = 0;
    let sinPrecio = 0;
    for (const s of stockFiltrado) {
      const disp = Number(s.cantidad_disponible || 0);
      const min = Number(s.stock_minimo || 0);
      unidades += disp;
      if (disp < 0) negativos += 1;
      else if (disp === 0) sinStock += 1;
      else if (min > 0 && disp <= min) bajos += 1;
      if (s.precios?.venta?.valor == null) sinPrecio += 1;
    }
    // Margen sólo si costo y venta están en una única moneda y es la misma.
    const [c] = valorizacion.costo;
    const [v] = valorizacion.venta;
    const margen =
      valorizacion.costo.length === 1 && valorizacion.venta.length === 1 && c.moneda === v.moneda && v.valor > 0
        ? { valor: v.valor - c.valor, moneda: v.moneda, pct: ((v.valor - c.valor) / v.valor) * 100 }
        : null;
    return { unidades, bajos, sinStock, negativos, sinPrecio, margen };
  }, [stockFiltrado, valorizacion]);

  const depositoActivo = depositos.find((d) => d.id === depositoId);
  const sucursalActiva = sucursalesItemSelect?.descripcion || sucursalesItemSelect?.nombre;

  const exportExcel = () => {
    if (!filasParaExport.length) {
      toast.info("No hay productos para exportar");
      return;
    }
    const aoa = [
      [
        "Código", "Producto", "Categoría", "Marca", "Disponible", "Reservada",
        ...(canVerCosto ? ["Costo", "Total costo"] : []), "Venta", "Total venta", "Mínimo", "Máximo", "Ubicación",
      ],
      ...filasParaExport.map((r) => [
        r.codigo, r.descripcion, r.categoria, r.marca,
        r.disponible, r.reservada,
        ...(canVerCosto ? [fmtPrecio(r.costo), fmtPrecio(r.totalCosto)] : []), fmtPrecio(r.venta), fmtPrecio(r.totalVenta),
        r.minimo, r.maximo ?? "", r.ubicacion,
      ]),
      [
        "", "TOTAL", "", "", Math.round(filasParaExport.reduce((acc, r) => acc + r.disponible, 0) * 1000) / 1000, "",
        ...(canVerCosto ? ["", fmtSumas(valorizacion.costo)] : []), "", fmtSumas(valorizacion.venta), "", "", "",
      ],
    ];
    const ws = XLSX.utils.aoa_to_sheet(sanitizeRows(aoa));
    ws["!cols"] = [
      { wch: 14 }, { wch: 38 }, { wch: 18 }, { wch: 18 }, { wch: 12 }, { wch: 12 },
      ...(canVerCosto ? [{ wch: 16 }, { wch: 18 }] : []), { wch: 16 }, { wch: 18 }, { wch: 10 }, { wch: 10 }, { wch: 18 },
    ];
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, "Stock");
    const fecha = hoyISO();
    const nombre = `stock-${(depositoActivo?.descripcion || "deposito").replace(/\s+/g, "_")}-${fecha}.xlsx`;
    XLSX.writeFile(wb, nombre);
  };

  const exportPdf = () => {
    if (!filasParaExport.length) {
      toast.info("No hay productos para exportar");
      return;
    }
    const fmt = (n) => Number(n || 0).toLocaleString("es-PY");
    const header = [
      { text: "Código", style: "th" },
      { text: "Producto", style: "th" },
      { text: "Categoría", style: "th" },
      { text: "Marca", style: "th" },
      { text: "Disp.", style: "th", alignment: "right" },
      { text: "Res.", style: "th", alignment: "right" },
      ...(canVerCosto ? [{ text: "Costo", style: "th", alignment: "right" }, { text: "Total costo", style: "th", alignment: "right" }] : []),
      { text: "Venta", style: "th", alignment: "right" },
      { text: "Total venta", style: "th", alignment: "right" },
      { text: "Mín.", style: "th", alignment: "right" },
      { text: "Máx.", style: "th", alignment: "right" },
      { text: "Ubic.", style: "th" },
    ];
    const body = [
      header,
      ...filasParaExport.map((r) => [
        { text: r.codigo, style: "td" },
        { text: r.descripcion, style: "td" },
        { text: r.categoria, style: "td" },
        { text: r.marca, style: "td" },
        { text: fmt(r.disponible), style: "td", alignment: "right" },
        { text: fmt(r.reservada), style: "td", alignment: "right" },
        ...(canVerCosto
          ? [{ text: fmtPrecio(r.costo), style: "td", alignment: "right", noWrap: true }, { text: fmtPrecio(r.totalCosto), style: "td", alignment: "right", noWrap: true }]
          : []),
        { text: fmtPrecio(r.venta), style: "td", alignment: "right", noWrap: true },
        { text: fmtPrecio(r.totalVenta), style: "td", alignment: "right", noWrap: true },
        { text: fmt(r.minimo), style: "td", alignment: "right" },
        { text: r.maximo != null ? fmt(r.maximo) : "—", style: "td", alignment: "right" },
        { text: r.ubicacion || "—", style: "td" },
      ]),
      [
        { text: "", style: "th" },
        { text: "TOTAL", style: "th" },
        { text: "", style: "th" },
        { text: "", style: "th" },
        { text: fmt(filasParaExport.reduce((acc, r) => acc + r.disponible, 0)), style: "th", alignment: "right" },
        { text: "", style: "th" },
        ...(canVerCosto ? [{ text: "", style: "th" }, { text: fmtSumas(valorizacion.costo), style: "th", alignment: "right", noWrap: true }] : []),
        { text: "", style: "th" },
        { text: fmtSumas(valorizacion.venta), style: "th", alignment: "right", noWrap: true },
        { text: "", style: "th" },
        { text: "", style: "th" },
        { text: "", style: "th" },
      ],
    ];
    const totalDisp = filasParaExport.reduce((s, r) => s + r.disponible, 0);
    const docDefinition = {
      pageSize: "A4",
      pageOrientation: "landscape",
      pageMargins: [28, 36, 28, 40],
      content: [
        { text: "Reporte de Stock por Depósito", style: "title" },
        {
          margin: [0, 4, 0, 12],
          columns: [
            [
              { text: `Sucursal: ${sucursalActiva || "—"}`, style: "meta" },
              { text: `Depósito: ${depositoActivo?.descripcion || "—"}`, style: "meta" },
            ],
            [
              { text: `Productos: ${filasParaExport.length} · Total disponible: ${fmt(totalDisp)}`, style: "meta", alignment: "right" },
              {
                text: `Valorizado${canVerCosto ? ` al costo: ${fmtSumas(valorizacion.costo)} ·` : ""} a la venta: ${fmtSumas(valorizacion.venta)}`,
                style: "meta",
                alignment: "right",
              },
              { text: `Generado: ${new Date().toLocaleString("es-PY")}`, style: "meta", alignment: "right" },
            ],
          ],
        },
        {
          table: { headerRows: 1, widths: header.map((h, i) => (i === 1 ? "*" : "auto")), body },
          layout: {
            fillColor: (i, node) => (i === 0 || i === node.table.body.length - 1 ? "#f1f5f9" : i % 2 === 0 ? "#fafafa" : null),
            hLineColor: () => "#e5e7eb",
            vLineColor: () => "#e5e7eb",
          },
        },
      ],
      styles: {
        title: { fontSize: 14, bold: true, color: "#0f172a" },
        meta: { fontSize: 9, color: "#475569", margin: [0, 1, 0, 1] },
        th: { fontSize: 9, bold: true, color: "#334155" },
        td: { fontSize: 9, color: "#0f172a" },
      },
      footer: (current, total) => ({
        text: `Página ${current} de ${total}`,
        alignment: "center",
        fontSize: 8,
        color: "#94a3b8",
        margin: [0, 14, 0, 0],
      }),
    };
    const fecha = hoyISO();
    setPdfDocDef(docDefinition);
    setPdfFilename(`stock-${(depositoActivo?.descripcion || "deposito").replace(/\s+/g, "_")}-${fecha}.pdf`);
    setPdfModalOpen(true);
  };

  const fetchPdfBlob = useMemo(
    () =>
      pdfDocDef
        ? () =>
            new Promise((resolve, reject) => {
              try {
                pdfMake.createPdf(pdfDocDef).getBlob((blob) => resolve(blob));
              } catch (err) {
                reject(err);
              }
            })
        : null,
    [pdfDocDef],
  );

  const columns = [
    {
      key: "producto",
      label: "Producto",
      minWidth: 220,
      primary: true,
      wrap: true,
      sortable: true,
      resizable: true,
      render: (row) => row.producto?.descripcion || row.producto?.nombre || "—",
      sortValue: (row) => row.producto?.descripcion || row.producto?.nombre || "",
    },
    {
      key: "codigo",
      label: "Código",
      minWidth: 120,
      sortable: true,
      resizable: true,
      render: (row) => row.producto?.cod_producto || row.producto?.codigobarra || "—",
      sortValue: (row) => row.producto?.cod_producto || row.producto?.codigobarra || "",
    },
    {
      key: "cantidad_disponible",
      label: "Disponible",
      minWidth: 110,
      align: "center",
      chip: true,
      sortable: true,
      render: (row) => {
        const val = Number(row.cantidad_disponible || 0);
        const min = Number(row.stock_minimo || 0);
        const color = val <= 0 ? "error" : val <= min && min > 0 ? "warning" : "success";
        return <Chip label={val.toLocaleString("es-PY")} color={color} size="small" />;
      },
      sortValue: (row) => Number(row.cantidad_disponible || 0),
    },
    {
      key: "cantidad_reservada",
      label: "Reservado",
      minWidth: 100,
      align: "right",
      sortable: true,
      render: (row) => Number(row.cantidad_reservada || 0).toLocaleString("es-PY"),
      sortValue: (row) => Number(row.cantidad_reservada || 0),
    },
    ...(canVerCosto
      ? [
          {
            key: "costo",
            label: "Costo",
            minWidth: 130,
            align: "right",
            sortable: true,
            render: (row) => (
              <PrecioCell precio={row.precios?.costo} titulo={FUENTE_COSTO[row.precios?.costo?.fuente] || ""} />
            ),
            sortValue: (row) => Number(row.precios?.costo?.valor ?? -1),
          },
          {
            key: "total_costo",
            label: "Total costo",
            minWidth: 140,
            align: "right",
            sortable: true,
            render: (row) => <PrecioCell precio={totalPrecio(row.precios?.costo, row.cantidad_disponible)} titulo="Disponible × costo" />,
            sortValue: (row) => totalPrecio(row.precios?.costo, row.cantidad_disponible)?.valor ?? -1,
          },
        ]
      : []),
    {
      key: "venta",
      label: "Venta",
      minWidth: 130,
      align: "right",
      sortable: true,
      render: (row) => <PrecioCell precio={row.precios?.venta} titulo={fuenteVenta(row.precios?.venta)} />,
      sortValue: (row) => Number(row.precios?.venta?.valor ?? -1),
    },
    {
      key: "total_venta",
      label: "Total venta",
      minWidth: 140,
      align: "right",
      sortable: true,
      render: (row) => <PrecioCell precio={totalPrecio(row.precios?.venta, row.cantidad_disponible)} titulo="Disponible × venta" />,
      sortValue: (row) => totalPrecio(row.precios?.venta, row.cantidad_disponible)?.valor ?? -1,
    },
    {
      key: "stock_minimo",
      label: "Stock Mín.",
      minWidth: 130,
      align: canEdit ? "left" : "right",
      sortable: true,
      render: (row) =>
        canEdit ? (
          <EditableCell row={row} field="stock_minimo" onSave={handleSaveLimite} />
        ) : (
          Number(row.stock_minimo || 0).toLocaleString("es-PY")
        ),
      sortValue: (row) => Number(row.stock_minimo || 0),
    },
    {
      key: "stock_maximo",
      label: "Stock Máx.",
      minWidth: 130,
      align: canEdit ? "left" : "right",
      sortable: true,
      render: (row) =>
        canEdit ? (
          <EditableCell row={row} field="stock_maximo" onSave={handleSaveLimite} />
        ) : (
          row.stock_maximo ? Number(row.stock_maximo).toLocaleString("es-PY") : "—"
        ),
      sortValue: (row) => Number(row.stock_maximo || 0),
    },
    {
      key: "ubicacion",
      label: "Ubicación",
      minWidth: 140,
      wrap: true,
      sortable: true,
      resizable: true,
      render: (row) =>
        canEdit ? (
          <EditableCell row={row} field="ubicacion" type="text" onSave={handleSaveLimite} />
        ) : (
          row.ubicacion || "—"
        ),
      sortValue: (row) => row.ubicacion || "",
    },
  ];

  const errorModulo = getErrorApi(stockError);
  const hasRequiredData = !loadingSucursales && sucursales.length > 0;

  if (!canRead) {
    return <AccesoRestringido mensaje="No tienes permisos para ver el stock." />;
  }
  if (errorModulo) {
    return <AccesoRestringido titulo="Módulo no disponible" mensaje={errorModulo} />;
  }

  return (
    <RequiredDataGuard
      hasRequiredData={hasRequiredData}
      title="Configuración requerida"
      message="Necesitas crear al menos una Sucursal para gestionar el stock."
      buttonText="Ir a Configuración"
      buttonRoute="/configuracion"
    >
      <Wrapper>
        <ScreenGuia
          titulo="¿Cómo funciona el Stock?"
          intro="El stock se controla por depósito. Editá límites mínimos/máximos in-line y monitoreá productos bajos."
          pasos={GUIA_PASOS}
        />

        <SectionBar>
          <div className="title-block">
            <Icon icon="lucide:warehouse" width={22} />
            <div>
              <div className="title">Stock por Depósito</div>
              <div className="subtitle">
                {depositoActivo
                  ? `${stockFiltrado.length} de ${stock.length} producto${stock.length === 1 ? "" : "s"} en ${depositoActivo.descripcion || "depósito"}`
                  : sucursalActiva
                    ? `Elegí un depósito en ${sucursalActiva}`
                    : "Seleccioná sucursal y depósito"}
              </div>
            </div>
          </div>
        </SectionBar>

        <FiltersBox>
          <FormControl size="small" sx={{ minWidth: { xs: "100%", sm: 200 } }}>
            <InputLabel>Sucursal</InputLabel>
            <Select
              value={sucursalesItemSelect?.id || ""}
              label="Sucursal"
              onChange={(e) => {
                const suc = sucursales.find((s) => s.id === e.target.value);
                selectSucursal(suc);
                setDepositoId("");
              }}
              startAdornment={
                <Box sx={{ display: "inline-flex", mr: 0.5, color: "text.secondary" }}>
                  <Icon icon="lucide:store" width={16} />
                </Box>
              }
            >
              {sucursales.map((suc) => (
                <MenuItem key={suc.id} value={suc.id}>
                  {suc.descripcion || suc.nombre}
                </MenuItem>
              ))}
            </Select>
          </FormControl>

          <Autocomplete
            size="small"
            sx={{ minWidth: { xs: "100%", sm: 240 } }}
            options={depositos}
            getOptionLabel={(dep) => dep?.descripcion ?? ""}
            isOptionEqualToValue={(opt, val) => opt?.id === val?.id}
            value={depositos.find((d) => d.id === depositoId) ?? null}
            onChange={(_, newVal) => setDepositoId(newVal?.id ?? "")}
            disabled={!sucursalesItemSelect?.id}
            noOptionsText="Sin depósitos"
            renderInput={(params) => (
              <TextField
                {...params}
                label="Depósito"
                placeholder="Buscar depósito…"
                InputProps={{
                  ...params.InputProps,
                  startAdornment: (
                    <>
                      <Box sx={{ display: "inline-flex", ml: 0.5, mr: 0.5, color: "text.secondary" }}>
                        <Icon icon="lucide:warehouse" width={16} />
                      </Box>
                      {params.InputProps.startAdornment}
                    </>
                  ),
                }}
              />
            )}
          />

          <TextField
            size="small"
            placeholder="Buscar por código o descripción…"
            value={busqueda}
            onChange={(e) => setBusqueda(e.target.value)}
            sx={{ minWidth: { xs: "100%", sm: 240 } }}
            disabled={!depositoId}
            InputProps={{
              startAdornment: (
                <InputAdornment position="start">
                  <Icon icon="lucide:search" width={16} />
                </InputAdornment>
              ),
              endAdornment: busqueda ? (
                <InputAdornment position="end">
                  <IconButton size="small" onClick={() => setBusqueda("")}>
                    <Icon icon="lucide:x" width={14} />
                  </IconButton>
                </InputAdornment>
              ) : null,
            }}
          />

          <Autocomplete
            multiple
            size="small"
            options={categorias}
            value={categoriasSel}
            onChange={(_, v) => setCategoriasSel(v)}
            getOptionLabel={(o) => o.descripcion || ""}
            isOptionEqualToValue={(a, b) => a.id === b.id}
            disabled={!depositoId}
            sx={{ minWidth: { xs: "100%", sm: 220 } }}
            renderInput={(params) => (
              <TextField {...params} label="Categorías" placeholder={categoriasSel.length ? "" : "Todas"} />
            )}
            limitTags={1}
          />

          <Autocomplete
            multiple
            size="small"
            options={marcas}
            value={marcasSel}
            onChange={(_, v) => setMarcasSel(v)}
            getOptionLabel={(o) => o.descripcion || ""}
            isOptionEqualToValue={(a, b) => a.id === b.id}
            disabled={!depositoId}
            sx={{ minWidth: { xs: "100%", sm: 200 } }}
            renderInput={(params) => (
              <TextField {...params} label="Marcas" placeholder={marcasSel.length ? "" : "Todas"} />
            )}
            limitTags={1}
          />

          <Tooltip title="Mostrar solo productos en stock bajo (≤ mínimo o agotados)">
            <Chip
              icon={<Icon icon="lucide:alert-triangle" width={14} />}
              label="Solo bajos"
              size="small"
              color={soloBajos ? "warning" : "default"}
              variant={soloBajos ? "filled" : "outlined"}
              onClick={() => setSoloBajos((v) => !v)}
              disabled={!depositoId}
              sx={{ cursor: depositoId ? "pointer" : "not-allowed" }}
            />
          </Tooltip>

          <Box sx={{ display: "flex", gap: 0.75, ml: { sm: "auto" } }}>
            <Button
              size="small"
              variant="outlined"
              startIcon={<Icon icon="lucide:file-spreadsheet" width={16} />}
              disabled={!depositoId || stockFiltrado.length === 0}
              onClick={exportExcel}
            >
              Excel
            </Button>
            <Button
              size="small"
              variant="outlined"
              color="error"
              startIcon={<Icon icon="lucide:file-text" width={16} />}
              disabled={!depositoId || stockFiltrado.length === 0}
              onClick={exportPdf}
            >
              PDF
            </Button>
          </Box>

          {canEdit && depositoId && (
            <HintText>
              <Icon icon="lucide:info" width={14} />
              Clic en Mín., Máx. o Ubicación para editar
            </HintText>
          )}
        </FiltersBox>

        {depositoId && stock.length > 0 && (
          <KpiGrid>
            {canVerCosto && (
              <KpiCard
                icon="lucide:package-search"
                label="Valorizado al costo"
                value={fmtSumas(valorizacion.costo)}
                color="warning.main"
                hint="Disponible × costo de cada producto (promedio de lotes si maneja lote)"
              />
            )}
            <KpiCard
              icon="lucide:banknote"
              label="Valorizado a la venta"
              value={fmtSumas(valorizacion.venta)}
              color="success.main"
              hint="Disponible × precio de venta (lista general o precio base)"
            />
            {canVerCosto && indicadores.margen && (
              <KpiCard
                icon="lucide:trending-up"
                label="Margen potencial"
                value={fmtMoneda(indicadores.margen.valor, indicadores.margen.moneda)}
                sub={`${indicadores.margen.pct.toLocaleString("es-PY", { maximumFractionDigits: 1 })} % sobre venta`}
                color={indicadores.margen.valor >= 0 ? "info.main" : "error.main"}
                hint="Venta − costo del inventario, si se vendiera todo al precio de lista"
              />
            )}
            <KpiCard
              icon="lucide:boxes"
              label="Unidades disponibles"
              value={indicadores.unidades.toLocaleString("es-PY", { maximumFractionDigits: 3 })}
              sub={`${stockFiltrado.length} producto${stockFiltrado.length === 1 ? "" : "s"}`}
              color="primary.main"
            />
            <KpiCard
              icon="lucide:alert-triangle"
              label="Bajo mínimo"
              value={indicadores.bajos.toLocaleString("es-PY")}
              sub="para reponer"
              color={indicadores.bajos ? "warning.main" : "text.secondary"}
              hint="Productos con disponible ≤ stock mínimo. Clic para ver sólo los bajos."
              onClick={() => setSoloBajos((v) => !v)}
              active={soloBajos}
            />
            <KpiCard
              icon="lucide:package-x"
              label="Sin stock / negativos"
              value={`${indicadores.sinStock.toLocaleString("es-PY")} / ${indicadores.negativos.toLocaleString("es-PY")}`}
              sub={indicadores.negativos ? "hay negativos: ajustar" : "agotados"}
              color={indicadores.negativos ? "error.main" : indicadores.sinStock ? "warning.main" : "text.secondary"}
              hint="Agotados (0) y negativos (< 0). Un negativo es una diferencia de inventario que hay que ajustar."
            />
          </KpiGrid>
        )}

        {!depositoId ? (
          <EmptyState
            icon="lucide:warehouse"
            title="Seleccioná un depósito"
            description="Para ver y editar el stock, elegí primero una sucursal y un depósito en los filtros de arriba."
          />
        ) : isMobile ? (
          <Box sx={{ flex: 1, overflow: "auto" }}>
            {isLoading ? (
              <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
                {[1, 2, 3, 4].map((i) => (
                  <Skeleton key={i} variant="rounded" height={100} />
                ))}
              </Box>
            ) : stockFiltrado.length === 0 ? (
              <EmptyState
                icon="lucide:package-x"
                title={stock.length === 0 ? "Sin stock en este depósito" : "Sin productos que coincidan con los filtros"}
                description={stock.length === 0
                  ? "Los productos aparecerán acá cuando registrés movimientos de entrada."
                  : "Ajustá la búsqueda, categorías o marcas seleccionadas."}
                compact
              />
            ) : (
              <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
                {stockFiltrado.map((item) => {
                  const disponible = Number(item.cantidad_disponible || 0);
                  const minimo = Number(item.stock_minimo || 0);
                  const maximo = item.stock_maximo ? Number(item.stock_maximo) : null;
                  const color =
                    disponible <= 0
                      ? "error"
                      : disponible <= minimo && minimo > 0
                        ? "warning"
                        : "success";
                  return (
                    <MobileStockCard
                      key={item.id}
                      item={item}
                      disponible={disponible}
                      minimo={minimo}
                      maximo={maximo}
                      color={color}
                      canEdit={canEdit}
                      onSave={handleSaveLimite}
                    />
                  );
                })}
              </Box>
            )}
          </Box>
        ) : (
          <StandardTable
            tableId="stock-por-deposito"
            data={stockFiltrado}
            columns={columns}
            loading={isLoading}
            density="compact"
            mobileBreakpoint={0}
            emptyState={
              <EmptyState
                icon="lucide:package-x"
                title={stock.length === 0 ? "Sin stock en este depósito" : "Sin productos que coincidan con los filtros"}
                description={stock.length === 0
                  ? "Los productos aparecerán acá cuando registrés movimientos de entrada."
                  : "Ajustá la búsqueda, categorías o marcas seleccionadas."}
                compact
              />
            }
            pagination={{
              mode: "client",
              pageSize: pageSize,
              onPageSizeChange: setPageSize,
              pageSizeOptions: [10, 25, 50, 100],
              noun: "productos",
            }}
          />
        )}
      </Wrapper>

      <PdfPreviewModal
        open={pdfModalOpen}
        onClose={() => setPdfModalOpen(false)}
        titulo={`Stock — ${depositoActivo?.descripcion || "Depósito"}`}
        fetchBlob={fetchPdfBlob}
        filename={pdfFilename}
      />
    </RequiredDataGuard>
  );
}

function MobileStockCard({ item, disponible, minimo, maximo, color, canEdit, onSave }) {
  const [editField, setEditField] = useState(null);
  const [editValue, setEditValue] = useState("");

  const startEdit = (field, value) => {
    if (!canEdit) return;
    setEditField(field);
    setEditValue(value ?? "");
  };

  const saveEdit = () => {
    const val = editField === "ubicacion" ? editValue : Number(editValue) || 0;
    onSave(item.id, { [editField]: val });
    setEditField(null);
  };

  const cancelEdit = () => {
    setEditField(null);
    setEditValue("");
  };

  const renderField = (label, field, value, isText = false) => {
    if (editField === field) {
      return (
        <Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
          <TextField
            size="small"
            type={isText ? "text" : "number"}
            value={editValue}
            onChange={(e) => setEditValue(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === "Enter") saveEdit();
              if (e.key === "Escape") cancelEdit();
            }}
            autoFocus
            sx={{ width: 80, "& input": { py: 0.25, px: 0.5, fontSize: "0.75rem" } }}
          />
          <IconButton size="small" onClick={saveEdit} color="success" sx={{ p: 0.2 }}>
            <Icon icon="lucide:check" width={14} />
          </IconButton>
          <IconButton size="small" onClick={cancelEdit} sx={{ p: 0.2 }}>
            <Icon icon="lucide:x" width={14} />
          </IconButton>
        </Box>
      );
    }

    const display = isText ? value || "-" : Number(value || 0).toLocaleString("es-PY");

    return (
      <Tooltip title={canEdit ? "Clic para editar" : ""}>
        <Chip
          label={`${label}: ${display}`}
          size="small"
          variant="outlined"
          sx={{ fontSize: "0.65rem", height: 22, cursor: canEdit ? "pointer" : "default" }}
          onClick={() => startEdit(field, value)}
        />
      </Tooltip>
    );
  };

  return (
    <Card variant="outlined" sx={{ borderRadius: 2 }}>
      <CardContent sx={{ p: 1.5, "&:last-child": { pb: 1.5 } }}>
        <Typography variant="body2" fontWeight={600} noWrap>
          {item.producto?.descripcion || item.producto?.nombre || "-"}
        </Typography>
        <Typography variant="caption" color="text.secondary">
          {item.producto?.cod_producto || item.producto?.codigobarra || "Sin código"}
          {item.ubicacion && ` • ${item.ubicacion}`}
        </Typography>
        <Box sx={{ display: "flex", gap: 0.75, mt: 1, alignItems: "center", flexWrap: "wrap" }}>
          <Chip
            label={`Disp: ${disponible.toLocaleString("es-PY")}`}
            color={color}
            size="small"
            sx={{ fontSize: "0.65rem", height: 22 }}
          />
          <Chip
            label={`Res: ${Number(item.cantidad_reservada || 0).toLocaleString("es-PY")}`}
            size="small"
            variant="outlined"
            sx={{ fontSize: "0.65rem", height: 22 }}
          />
          {renderField("Mín", "stock_minimo", minimo)}
          {renderField("Máx", "stock_maximo", maximo)}
          {renderField("Ubic", "ubicacion", item.ubicacion, true)}
        </Box>
      </CardContent>
    </Card>
  );
}
