import { Icon } from "@iconify/react";
import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Alert,
  Box,
  Button,
  Chip,
  CircularProgress,
  Autocomplete,
  Container,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  FormControl,
  InputLabel,
  Divider,
  FormControlLabel,
  IconButton,
  Link,
  MenuItem,
  Paper,
  Select,
  Skeleton,
  Stack,
  Switch,
  Tab,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Tabs,
  TextField,
  ToggleButton,
  ToggleButtonGroup,
  Tooltip,
  Typography,
} from "@mui/material";
import PropTypes from "prop-types";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { toast } from "sonner";
import styled from "styled-components";
import * as XLSX from "xlsx";
import { EmptyState, ImageUploadField, PrereqChecklist, ScreenGuia, TablePaginationBar, TIPO_NOVEDAD_OPTIONS } from "../components/_standards";
import { MonedaInput } from "../components/common/MonedaInput";
import { useConfirmDialog } from "../hooks/useConfirmDialog";
import { OfertasTab } from "../components/inventario";
import WysiwygEmailEditor from "../components/ui/WysiwygEmailEditor";
import ecommerceService from "../api/ecommerce.service";
import { fmtFechaHora, hoyISO } from "../utils/fecha";
import { getNovasisPayProviders } from "../api/novasis-pay.service";
import { buildStoreUrl } from "../utils/storeUrl";
import { ECOMMERCE_GUIAS } from "../features/ecommerce/ecommerceGuias";
import { WorkflowConfigEditor } from "../components/ecommerce/WorkflowConfigEditor";
import { SeccionTienda, CUANDO_RIGE } from "../components/ecommerce/SeccionTienda";
import { VersionesDialog } from "../components/ecommerce/VersionesDialog";
import { FooterConfigPanel } from "../components/ecommerce/FooterConfigPanel";
import { normalizeWorkflowConfig } from "../components/ecommerce/workflowConfig";

// Resultado del procesamiento de un webhook del gateway, para mostrar.
const RESULTADO_WEBHOOK_LABEL = {
  aplicado: "Aplicado",
  aplicado_ecommerce: "Aplicado al pedido",
  cobro_no_encontrado: "Pago sin pedido",
  sin_intent_id: "Sin referencia de pago",
  evento_ignorado: "Evento ignorado",
};

const DEFAULT_DELIVERY_METHODS = [
  {
    id: "delivery-programado",
    type: "delivery",
    name: "Delivery programado",
    cost: 18000,
    enabled: true,
  },
  {
    id: "retiro-local",
    type: "pickup",
    name: "Retiro en local",
    cost: 0,
    enabled: true,
  },
];

function normalizeDeliveryMethods(checkout = {}) {
  if (Array.isArray(checkout.deliveryMethods) && checkout.deliveryMethods.length > 0) {
    return checkout.deliveryMethods.map((method, index) => ({
      id: method.id || `entrega-${index + 1}`,
      type: method.type === "pickup" ? "pickup" : "delivery",
      name: method.name || `Método de entrega ${index + 1}`,
      cost: Math.max(0, Number(method.cost) || 0),
      enabled: method.enabled !== false,
    }));
  }
  const legacyDeliveryCost = Number(checkout.deliveryCost);
  return DEFAULT_DELIVERY_METHODS.filter((method) => (method.type === "delivery" ? checkout.allowDelivery !== false : checkout.allowPickup !== false)).map(
    (method) => ({
      ...method,
      cost: method.type === "delivery" && Number.isFinite(legacyDeliveryCost) ? Math.max(0, legacyDeliveryCost) : method.cost,
    }),
  );
}

const DEFAULT_PAYMENT_METHODS = [
  { id: "novasis-pay", type: "online", name: "Novasis Pay (tarjetas)", enabled: true, discountPercent: 0 },
  { id: "transferencia", type: "manual", name: "Transferencia bancaria", enabled: true, discountPercent: 0 },
  { id: "contra-entrega", type: "manual", name: "Efectivo contra entrega", enabled: true, discountPercent: 0 },
];

function normalizePaymentMethods(checkout = {}) {
  if (Array.isArray(checkout.paymentMethods) && checkout.paymentMethods.length > 0) {
    return checkout.paymentMethods.map((method, index) => ({
      id: method.id || `pago-${index + 1}`,
      type: method.type === "online" ? "online" : "manual",
      name: method.name || `Medio de pago ${index + 1}`,
      enabled: method.enabled !== false,
      discountPercent: Math.max(0, Math.min(100, Number(method.discountPercent) || 0)),
      // Sólo aplica a los medios "online": permite publicar varios (ej. "Tarjeta" con
      // dlocal y "QR / billetera" con dpago) en vez de un único "Novasis Pay".
      provider: method.provider || "",
      platformId: method.platformId || "",
    }));
  }
  return DEFAULT_PAYMENT_METHODS.map((method) => ({ ...method }));
}

// ── Compra a crédito (planes de cuotas del producto) ──────────────
const DEFAULT_CREDITO_CONFIG = {
  enabled: false,
  // Qué descuento gana cuando el plan de cuotas y el medio de pago traen los dos el suyo.
  precedenciaDescuento: "plan",
  estadoInicial: "pendiente_aprobacion",
  intervaloDias: 30,
  condicionPagoId: "",
  vendedorId: "",
  cobradorId: "",
  mostrarEnTarjeta: true,
};

function normalizeCreditoConfig(checkout = {}) {
  const raw = checkout.creditoConfig ?? {};
  const precedencia = ["plan", "medio_pago", "acumular"].includes(raw.precedenciaDescuento)
    ? raw.precedenciaDescuento
    : DEFAULT_CREDITO_CONFIG.precedenciaDescuento;
  return {
    enabled: raw.enabled === true,
    precedenciaDescuento: precedencia,
    estadoInicial: raw.estadoInicial === "borrador" ? "borrador" : "pendiente_aprobacion",
    intervaloDias: Number(raw.intervaloDias) > 0 ? Math.round(Number(raw.intervaloDias)) : 30,
    condicionPagoId: raw.condicionPagoId || "",
    vendedorId: raw.vendedorId || "",
    cobradorId: raw.cobradorId || "",
    mostrarEnTarjeta: raw.mostrarEnTarjeta !== false,
  };
}

// ── Cuerpos editables (WYSIWYG) — solo el mensaje personalizado ───
const EMAIL_BODY_ORDER_CONFIRMED = `<p>Te notificaremos en cada etapa del proceso. ¡Gracias por confiar en nosotros!</p><p><strong>El equipo de {{TIENDA_NOMBRE}} 💚</strong></p>`;
const EMAIL_BODY_ORDER_INVOICED = `<p>Ante cualquier consulta sobre tu factura, respondé este correo y te ayudamos enseguida.</p><p><strong>El equipo de {{TIENDA_NOMBRE}} 💙</strong></p>`;
const EMAIL_BODY_ORDER_SHIPPED = `<p>¡Pronto lo tendrás en tus manos! Cualquier consulta estamos disponibles.</p><p><strong>El equipo de {{TIENDA_NOMBRE}} 🚀</strong></p>`;
const EMAIL_BODY_ORDER_CANCELLED = `<p>Esperamos poder servirte muy pronto. Gracias por tu comprensión.</p><p><strong>El equipo de {{TIENDA_NOMBRE}}</strong></p>`;
const EMAIL_BODY_NEW_ORDER_ADMIN = `<p>Revisá el detalle de arriba e ingresá al panel para confirmar el pago y preparar el pedido.</p>`;

// ── Estructuras visuales fijas (no editables) — generadas por código ──
function buildItemsTable(v) {
  return `<div style='margin-bottom:22px'><p style='margin:0 0 10px;font-size:11px;font-weight:800;color:#9ca3af;text-transform:uppercase;letter-spacing:0.08em'>📦 Detalle del pedido</p><table width='100%' cellpadding='0' cellspacing='0' style='border:1px solid #e5e7eb;border-radius:10px;overflow:hidden'><tr style='background:#f9fafb'><td style='padding:9px 6px 9px 14px;width:44px;border-bottom:1px solid #e5e7eb'></td><td style='padding:9px 8px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb'>PRODUCTO</td><td style='padding:9px 8px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb;text-align:center;white-space:nowrap'>CANT.</td><td style='padding:9px 8px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb;text-align:right;white-space:nowrap'>P. UNIT.</td><td style='padding:9px 14px 9px 8px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb;text-align:right;white-space:nowrap'>SUBTOTAL</td></tr>${v.ITEMS}<tr style='background:#f0fdf4'><td colspan='4' style='padding:14px 8px 14px 14px;font-size:14px;font-weight:700;color:#374151;border-top:2px solid #d1fae5'>Total abonado</td><td style='padding:14px 14px 14px 8px;font-size:20px;font-weight:900;color:#16a34a;border-top:2px solid #d1fae5;text-align:right;white-space:nowrap'>${v.TOTAL}</td></tr></table></div>`;
}

const EMAIL_STRUCTURES = {
  orderConfirmed: (v) =>
    `<p style='margin:0 0 20px;font-size:16px;font-weight:700;color:#111'>Hola, ${v.CLIENTE_NOMBRE} 👋</p>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:linear-gradient(135deg,#f0fdf4,#dcfce7);border:1.5px solid #86efac;border-radius:12px'><tr><td style='padding:18px 22px'><span style='display:inline-block;background:#16a34a;color:#fff;font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:0.1em;padding:3px 10px;border-radius:999px;margin-bottom:10px'>✅ Pedido confirmado</span><p style='margin:0;font-size:28px;font-weight:900;color:#14532d;letter-spacing:-0.5px'>${v.NRO_PEDIDO}</p></td></tr></table></div>` +
    buildItemsTable(v) +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#eff6ff;border:1.5px solid #bfdbfe;border-radius:12px'><tr><td style='padding:14px 20px'><span style='font-size:11px;font-weight:800;color:#3b82f6;text-transform:uppercase;letter-spacing:0.08em'>🚚 Método de entrega</span><p style='margin:5px 0 0;font-size:15px;font-weight:700;color:#1e3a8a'>${v.METODO_ENTREGA}</p></td></tr></table></div>` +
    `<hr style='border:none;border-top:1px solid #f3f4f6;margin:4px 0 20px'/>`,

  orderInvoiced: (v) =>
    `<p style='margin:0 0 20px;font-size:16px;font-weight:700;color:#111'>Hola, ${v.CLIENTE_NOMBRE} 👋</p>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:linear-gradient(135deg,#eff6ff,#dbeafe);border:1.5px solid #93c5fd;border-radius:12px'><tr><td style='padding:18px 22px'><span style='display:inline-block;background:#2563eb;color:#fff;font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:0.1em;padding:3px 10px;border-radius:999px;margin-bottom:10px'>📄 Factura emitida</span><p style='margin:0;font-size:28px;font-weight:900;color:#1e3a8a;letter-spacing:-0.5px'>${v.NRO_PEDIDO}</p></td><td style='padding:18px 22px;text-align:right;font-size:44px;line-height:1;vertical-align:middle'>📄</td></tr></table></div>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#f9fafb;border:1px solid #e5e7eb;border-radius:10px'><tr><td style='padding:16px 20px'><span style='font-size:11px;font-weight:700;color:#9ca3af;text-transform:uppercase;letter-spacing:0.06em'>Total facturado</span><p style='margin:6px 0 0;font-size:30px;font-weight:900;color:#111;letter-spacing:-0.5px'>${v.TOTAL}</p></td><td style='padding:16px 20px;text-align:right;vertical-align:middle'><span style='display:inline-block;background:#dcfce7;color:#16a34a;font-size:12px;font-weight:800;padding:6px 14px;border-radius:999px'>✅ Pagado</span></td></tr></table></div>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#fefce8;border:1.5px solid #fde68a;border-radius:12px'><tr><td style='padding:14px 20px'><span style='font-size:11px;font-weight:800;color:#b45309;text-transform:uppercase;letter-spacing:0.06em'>📎 Archivo adjunto</span><p style='margin:5px 0 0;font-size:13px;color:#78350f;line-height:1.65'>Tu factura en formato PDF está adjunta a este correo. Guardala o imprimila cuando quieras.</p></td></tr></table></div>` +
    `<hr style='border:none;border-top:1px solid #f3f4f6;margin:4px 0 20px'/>`,

  orderShipped: (v) =>
    `<p style='margin:0 0 20px;font-size:16px;font-weight:700;color:#111'>¡Tu pedido está en camino, ${v.CLIENTE_NOMBRE}! 🎉</p>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px'><tr><td style='padding:18px 16px'><table width='100%' cellpadding='0' cellspacing='0'><tr><td style='width:25%;text-align:center;padding:0 4px'><div style='width:34px;height:34px;background:#16a34a;border-radius:50%;line-height:34px;text-align:center;font-size:15px;margin:0 auto 8px'>✅</div><p style='margin:0;font-size:10px;font-weight:700;color:#16a34a'>Pedido</p></td><td style='width:25%;text-align:center;padding:0 4px'><div style='width:34px;height:34px;background:#16a34a;border-radius:50%;line-height:34px;text-align:center;font-size:15px;margin:0 auto 8px'>✅</div><p style='margin:0;font-size:10px;font-weight:700;color:#16a34a'>Confirmado</p></td><td style='width:25%;text-align:center;padding:0 4px'><div style='width:34px;height:34px;background:#f97316;border-radius:50%;line-height:34px;text-align:center;font-size:15px;margin:0 auto 8px'>🚚</div><p style='margin:0;font-size:10px;font-weight:700;color:#f97316'>En camino</p></td><td style='width:25%;text-align:center;padding:0 4px'><div style='width:34px;height:34px;background:#e5e7eb;border-radius:50%;line-height:34px;text-align:center;font-size:15px;margin:0 auto 8px'>🏠</div><p style='margin:0;font-size:10px;font-weight:700;color:#9ca3af'>Entregado</p></td></tr></table></td></tr></table></div>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#fff7ed;border:1.5px solid #fed7aa;border-radius:12px'><tr><td style='padding:16px 20px'><span style='font-size:11px;font-weight:800;color:#ea580c;text-transform:uppercase;letter-spacing:0.08em'>📍 Dirección de entrega</span><p style='margin:6px 0 0;font-size:14px;font-weight:600;color:#1a1a1a;line-height:1.65'>${v.DIRECCION_ENTREGA}</p></td></tr></table></div>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#eff6ff;border:1.5px solid #bfdbfe;border-radius:12px'><tr><td style='padding:14px 20px'><span style='font-size:11px;font-weight:800;color:#3b82f6;text-transform:uppercase;letter-spacing:0.08em'>🚚 Método</span><p style='margin:5px 0 0;font-size:15px;font-weight:700;color:#1e3a8a'>${v.METODO_ENTREGA}</p></td></tr></table></div>` +
    `<hr style='border:none;border-top:1px solid #f3f4f6;margin:4px 0 20px'/>`,

  orderCancelled: (v) =>
    `<p style='margin:0 0 20px;font-size:16px;font-weight:700;color:#111'>Hola, ${v.CLIENTE_NOMBRE}</p>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#fef2f2;border:1.5px solid #fecaca;border-radius:12px'><tr><td style='padding:18px 22px'><span style='display:inline-block;background:#dc2626;color:#fff;font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:0.1em;padding:3px 10px;border-radius:999px;margin-bottom:10px'>❌ Pedido cancelado</span><p style='margin:0;font-size:28px;font-weight:900;color:#7f1d1d;letter-spacing:-0.5px'>${v.NRO_PEDIDO}</p></td></tr></table></div>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='border:1px solid #e5e7eb;border-radius:10px;overflow:hidden'><tr style='background:#f9fafb'><td style='padding:10px 16px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb'>¿QUÉ SIGUE?</td></tr><tr><td style='padding:16px;font-size:13px;color:#374151;line-height:1.9'><p style='margin:0 0 10px'>🔄 <strong style='color:#111'>Reintegro:</strong> Si realizaste algún pago, será procesado en los próximos días hábiles.</p><p style='margin:0 0 10px'>🛒 <strong style='color:#111'>Nuevo pedido:</strong> Podés volver a la tienda y realizar tu compra cuando quieras.</p><p style='margin:0'>💬 <strong style='color:#111'>¿Necesitás ayuda?</strong> Respondé este correo y te asistimos de inmediato.</p></td></tr></table></div>` +
    `<div style='margin-bottom:22px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#f1f5f9;border:1.5px solid #cbd5e1;border-radius:12px'><tr><td style='padding:14px 20px'><span style='font-size:11px;font-weight:800;color:#475569;text-transform:uppercase;letter-spacing:0.08em'>📞 ¿Hablamos?</span><p style='margin:5px 0 0;font-size:13px;color:#334155;line-height:1.65'>Respondé este correo o contactanos directamente. Estamos para ayudarte.</p></td></tr></table></div>` +
    `<hr style='border:none;border-top:1px solid #f3f4f6;margin:4px 0 20px'/>`,

  newOrderAdmin: (v) =>
    `<div style='margin-bottom:20px'><table width='100%' cellpadding='0' cellspacing='0' style='background:linear-gradient(135deg,#fff7ed,#ffedd5);border:1.5px solid #fed7aa;border-radius:12px'><tr><td style='padding:18px 22px'><span style='display:inline-block;background:#ea580c;color:#fff;font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:0.1em;padding:3px 10px;border-radius:999px;margin-bottom:10px'>🔔 Nuevo pedido recibido</span><p style='margin:0;font-size:28px;font-weight:900;color:#7c2d12;letter-spacing:-0.5px'>${v.NRO_PEDIDO}</p></td><td style='padding:18px 22px;text-align:right;font-size:44px;line-height:1;vertical-align:middle'>🛍️</td></tr></table></div>` +
    `<div style='margin-bottom:20px'><p style='margin:0 0 10px;font-size:11px;font-weight:800;color:#9ca3af;text-transform:uppercase;letter-spacing:0.08em'>👤 Datos del cliente</p><table width='100%' cellpadding='0' cellspacing='0' style='border:1px solid #e5e7eb;border-radius:10px;overflow:hidden'><tr style='background:#f9fafb'><td style='width:35%;padding:9px 14px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb'>CAMPO</td><td style='padding:9px 14px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb'>VALOR</td></tr><tr><td style='padding:10px 14px;font-size:12px;color:#6b7280;border-bottom:1px solid #f3f4f6'>Nombre</td><td style='padding:10px 14px;font-size:13px;font-weight:600;color:#111;border-bottom:1px solid #f3f4f6'>${v.CLIENTE_NOMBRE}</td></tr><tr><td style='padding:10px 14px;font-size:12px;color:#6b7280;border-bottom:1px solid #f3f4f6'>Email</td><td style='padding:10px 14px;font-size:13px;font-weight:600;color:#111;border-bottom:1px solid #f3f4f6'>${v.CLIENTE_EMAIL}</td></tr><tr><td style='padding:10px 14px;font-size:12px;color:#6b7280'>Teléfono</td><td style='padding:10px 14px;font-size:13px;font-weight:600;color:#111'>${v.CLIENTE_TELEFONO}</td></tr></table></div>` +
    `<div style='margin-bottom:20px'><p style='margin:0 0 10px;font-size:11px;font-weight:800;color:#9ca3af;text-transform:uppercase;letter-spacing:0.08em'>📦 Productos</p><table width='100%' cellpadding='0' cellspacing='0' style='border:1px solid #e5e7eb;border-radius:10px;overflow:hidden'><tr style='background:#f9fafb'><td style='padding:9px 6px 9px 14px;width:44px;border-bottom:1px solid #e5e7eb'></td><td style='padding:9px 8px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb'>PRODUCTO</td><td style='padding:9px 8px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb;text-align:center;white-space:nowrap'>CANT.</td><td style='padding:9px 8px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb;text-align:right;white-space:nowrap'>P. UNIT.</td><td style='padding:9px 14px 9px 8px;font-size:11px;font-weight:700;color:#6b7280;border-bottom:1px solid #e5e7eb;text-align:right;white-space:nowrap'>SUBTOTAL</td></tr>${v.ITEMS}<tr style='background:#fff7ed'><td colspan='4' style='padding:14px 8px 14px 14px;font-size:14px;font-weight:700;color:#374151;border-top:2px solid #fed7aa'>Total del pedido</td><td style='padding:14px 14px 14px 8px;font-size:20px;font-weight:900;color:#ea580c;border-top:2px solid #fed7aa;text-align:right;white-space:nowrap'>${v.TOTAL}</td></tr></table></div>` +
    `<div style='margin-bottom:20px'><table width='100%' cellpadding='0' cellspacing='0' style='background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px'><tr><td style='padding:14px 18px'><p style='margin:0 0 4px;font-size:12px;font-weight:700;color:#475569'>🚚 ${v.METODO_ENTREGA}</p><p style='margin:0;font-size:13px;color:#334155'>📍 ${v.DIRECCION_ENTREGA}</p></td></tr></table></div>` +
    `<hr style='border:none;border-top:1px solid #f3f4f6;margin:4px 0 20px'/>`,
};

// Textos de secciones de la tienda (copy editable, no microcopy de UI).
const DEFAULT_STORE_TEXTS = {
  b2bPanelTitle: "Compra recurrente B2B",
  b2bPanelSubtitle: "Inicia sesion para repetir pedidos y ver precio mayorista.",
  novedadesTitle: "Novedades",
  novedadesSubtitle: "Anuncios, nuevos ingresos y campanas activas",
  categoryBannerTitleSuffix: "",
  categoryBannerDescription: "Seleccion especializada de {categoria} con disponibilidad actualizada, ofertas activas y opciones para compra recurrente.",
  mostViewedLabel: "Mas vistos",
  poweredByLabel: "Powered by Novasis Store",
};

const DEFAULT_FOOTER_CONFIG = {
  description: "Tienda online multi tenant para compras rápidas, pagos seguros y seguimiento de pedidos desde Novasis.",
  labels: {
    leadEyebrow: "Compra con confianza",
    paymentTrustTitle: "Pago protegido",
    deliveryTrustTitle: "Entrega flexible",
    supportTrustTitle: "Atención directa",
    whatsappAction: "WhatsApp",
    contactAction: "Contactar",
    contactColumnTitle: "Contacto",
    socialTitle: "Seguinos",
    paymentsTitle: "Pagos aceptados",
    deliveryAndPickup: "Delivery o retiro en local",
    deliveryOnly: "Delivery disponible",
    pickupOnly: "Retiro en local",
    copyrightTemplate: "© {year} {store}. Todos los derechos reservados.",
  },
  contact: {
    phone: "021 555 010",
    whatsapp: "595982124618",
    email: "ventas@aurora.novasis.store",
    address: "Asunción, Paraguay",
    hours: "Lunes a sábado, 08:00 a 19:00",
    description: "Atención para consultas sobre productos, pagos, facturación, delivery y seguimiento de pedidos.",
    map: {
      title: "Mercado Aurora",
      subtitle: "Asunción, Paraguay",
      embedUrl: "https://www.openstreetmap.org/export/embed.html?bbox=-57.6530%2C-25.3100%2C-57.5000%2C-25.2100&layer=mapnik&marker=-25.2637%2C-57.5759",
      directionsUrl: "https://www.openstreetmap.org/?mlat=-25.2637&mlon=-57.5759#map=14/-25.2637/-57.5759",
    },
  },
  linkGroups: [
    {
      id: "comprar",
      title: "Comprar",
      enabled: true,
      links: [
        { id: "catalogo", label: "Catálogo", href: "#catalogo", enabled: true },
        { id: "ofertas", label: "Ofertas", href: "#ofertas", enabled: true },
        { id: "novedades", label: "Novedades", href: "#novedades", enabled: true },
      ],
    },
    {
      id: "soporte",
      title: "Soporte",
      enabled: true,
      links: [
        { id: "seguimiento", label: "Seguimiento de pedido", pageKey: "seguimiento", enabled: true },
        { id: "devoluciones", label: "Cambios y devoluciones", pageKey: "devoluciones", enabled: true },
        { id: "faq", label: "Preguntas frecuentes", pageKey: "faq", enabled: true },
        { id: "contacto", label: "Contacto", pageKey: "contacto", enabled: true },
      ],
    },
    {
      id: "legal",
      title: "Legal",
      enabled: true,
      links: [
        { id: "terminos", label: "Términos y condiciones", pageKey: "terminos", enabled: true },
        { id: "privacidad", label: "Política de privacidad", pageKey: "privacidad", enabled: true },
        { id: "facturacion", label: "Facturación", pageKey: "facturacion", enabled: true },
      ],
    },
  ],
  paymentMethods: ["Novasis Pay", "Tarjetas", "Transferencia", "Contra entrega"],
  socialLinks: [
    { label: "Instagram", href: "#instagram", enabled: true },
    { label: "Facebook", href: "#facebook", enabled: true },
    { label: "TikTok", href: "#tiktok", enabled: true },
  ],
};

/**
 * Campos del footer que siguen con el valor de la plantilla (teléfono, email y local
 * de ejemplo, redes sin URL). Comercial Kety publicó así «ventas@aurora.novasis.store»
 * y «Mercado Aurora» sin enterarse.
 */
function camposDeEjemplo(footer) {
  if (!footer) return [];
  const c = footer.contact ?? {};
  const d = DEFAULT_FOOTER_CONFIG.contact;
  const campos = [];
  if (c.email && c.email === d.email) campos.push("email");
  if (c.phone && c.phone === d.phone) campos.push("teléfono");
  if (c.whatsapp && c.whatsapp === d.whatsapp) campos.push("WhatsApp");
  if (c.map?.title && c.map.title === d.map?.title) campos.push("nombre del local");
  const redesSinUrl = (footer.socialLinks ?? []).filter((r) => r.enabled !== false && (!r.href || r.href.startsWith("#")));
  if (redesSinUrl.length) campos.push(`redes sin link (${redesSinUrl.map((r) => r.label).join(", ")})`);
  return campos;
}

function normalizeFooterConfig(value = {}) {
  const contact = value.contact ?? {};
  return {
    ...DEFAULT_FOOTER_CONFIG,
    ...value,
    labels: { ...DEFAULT_FOOTER_CONFIG.labels, ...(value.labels ?? {}) },
    contact: {
      ...DEFAULT_FOOTER_CONFIG.contact,
      ...contact,
      map: { ...DEFAULT_FOOTER_CONFIG.contact.map, ...(contact.map ?? {}) },
    },
    linkGroups: Array.isArray(value.linkGroups) ? value.linkGroups : DEFAULT_FOOTER_CONFIG.linkGroups,
    paymentMethods: Array.isArray(value.paymentMethods) ? value.paymentMethods : DEFAULT_FOOTER_CONFIG.paymentMethods,
    socialLinks: Array.isArray(value.socialLinks) ? value.socialLinks : DEFAULT_FOOTER_CONFIG.socialLinks,
  };
}

// ── Helpers de estado del borrador ────────────────────────────────
// Mapea la respuesta del backend (columnas + JSONB) al estado plano del panel.
function buildDraftState(data) {
  const theme = data?.theme_config?.theme ?? data?.theme_config ?? {};
  const typography = data?.theme_config?.typography ?? {};
  const layout = data?.layout_config?.layout ?? data?.layout_config ?? {};
  const texts = data?.layout_config?.texts ?? {};
  const catalog = data?.catalog_config ?? {};
  const checkout = data?.checkout_config ?? {};
  const footer = normalizeFooterConfig(data?.footer_config ?? {});
  const notif = data?.notifications_config ?? {};
  const defNotif = {
    orderConfirmed: {
      enabled: true,
      subject: "Recibimos tu pedido {{NRO_PEDIDO}} — {{TIENDA_NOMBRE}}",
      body: EMAIL_BODY_ORDER_CONFIRMED,
    },
    orderInvoiced: {
      enabled: true,
      subject: "Tu factura está lista — Pedido {{NRO_PEDIDO}}",
      body: EMAIL_BODY_ORDER_INVOICED,
    },
    orderShipped: {
      enabled: true,
      subject: "Tu pedido {{NRO_PEDIDO}} está en camino",
      body: EMAIL_BODY_ORDER_SHIPPED,
    },
    orderCancelled: {
      enabled: true,
      subject: "Tu pedido {{NRO_PEDIDO}} fue cancelado",
      body: EMAIL_BODY_ORDER_CANCELLED,
    },
    newOrderAdmin: {
      enabled: true,
      subject: "Nuevo pedido recibido — {{NRO_PEDIDO}}",
      body: EMAIL_BODY_NEW_ORDER_ADMIN,
    },
  };
  return {
    subdominio: data?.subdominio ?? "",
    dominio_custom: data?.dominio_custom ?? "",
    estado: data?.estado ?? "borrador",
    sucursal_id: data?.sucursal_id ?? "",
    deposito_id: data?.deposito_id ?? "",
    lista_precios_id: data?.lista_precios_id ?? "",
    favicon_url: data?.favicon_url ?? "",
    identity: {
      name: data?.nombre_comercial ?? "",
      slogan: data?.slogan ?? "",
      logoUrl: data?.logo_url ?? "",
    },
    theme: {
      primary: theme.primary ?? "#0f766e",
      secondary: theme.secondary ?? "#1d4ed8",
      accent: theme.accent ?? "#f97316",
      background: theme.background ?? "#ffffff",
      surface: theme.surface ?? "#ffffff",
      text: theme.text ?? "#14211f",
      muted: theme.muted ?? "#66736f",
      success: theme.success ?? "#15803d",
      danger: theme.danger ?? "#dc2626",
      priceColor: theme.priceColor ?? theme.primary ?? "#0f766e",
    },
    typography: {
      fontFamily: typography.fontFamily ?? "Inter",
      scale: typography.scale ?? "normal",
      fontWeight: typography.fontWeight ?? "medio",
      priceFontSize: typography.priceFontSize ?? 18,
    },
    bannerHoverEffect: layout.bannerHoverEffect ?? "effect-5",
    layout: {
      motionPreset: layout.motionPreset ?? "dynamic",
      transitionSpeed: layout.transitionSpeed ?? "normal",
      heroVariant: layout.heroVariant ?? "immersive",
      sectionBackgroundMode: layout.sectionBackgroundMode ?? "alternating",
      cardHoverEffect: layout.cardHoverEffect ?? "lift",
      headerVariant: layout.headerVariant ?? "compact",
      stickyHeader: layout.stickyHeader ?? true,
      showScrollAnimations: layout.showScrollAnimations ?? true,
      showCarouselProgress: layout.showCarouselProgress ?? true,
    },
    texts: { ...DEFAULT_STORE_TEXTS, ...texts },
    footer,
    catalog: {
      cardSize: catalog.cardSize ?? "normal",
      displayMode: catalog.displayMode ?? "categoryCarousels",
      imageRatio: catalog.imageRatio ?? "4 / 3",
      columnsDesktop: catalog.columnsDesktop ?? 4,
      columnsTablet: catalog.columnsTablet ?? 3,
      columnsMobile: catalog.columnsMobile ?? 2,
      maxCategoriesOnHome: catalog.maxCategoriesOnHome ?? 6,
      homeCategories: Array.isArray(catalog.homeCategories) ? catalog.homeCategories : [],
      maxProductsPerCategory: catalog.maxProductsPerCategory ?? 10,
      pageSize: catalog.pageSize ?? 12,
      showStock: catalog.showStock ?? true,
      showBrand: catalog.showBrand ?? true,
      showCategory: catalog.showCategory ?? true,
      showPromotionBadge: catalog.showPromotionBadge ?? true,
    },
    seasons: Array.isArray(data?.seasonal_templates) ? data.seasonal_templates : [],
    promotions: data?.promotion_slots ?? {},
    checkout: {
      ...checkout,
      deliveryMethods: normalizeDeliveryMethods(checkout),
      paymentMethods: normalizePaymentMethods(checkout),
      creditoConfig: normalizeCreditoConfig(checkout),
    },
    workflow: normalizeWorkflowConfig(data?.workflow_config),
    contentPages: Array.isArray(data?.content_pages_config) ? data.content_pages_config : [],
    notifications: {
      adminEmail: notif.adminEmail ?? "",
      orderConfirmed: {
        ...defNotif.orderConfirmed,
        ...(notif.orderConfirmed ?? {}),
      },
      orderInvoiced: {
        ...defNotif.orderInvoiced,
        ...(notif.orderInvoiced ?? {}),
      },
      orderShipped: { ...defNotif.orderShipped, ...(notif.orderShipped ?? {}) },
      orderCancelled: {
        ...defNotif.orderCancelled,
        ...(notif.orderCancelled ?? {}),
      },
      newOrderAdmin: {
        ...defNotif.newOrderAdmin,
        ...(notif.newOrderAdmin ?? {}),
      },
    },
  };
}

// Convierte el estado plano del panel al payload que espera el backend.
function serializeDraft(draft) {
  return {
    subdominio: draft.subdominio || undefined,
    dominio_custom: draft.dominio_custom || null,
    estado: draft.estado || undefined,
    // null (no undefined) al vaciarlos: con undefined el backend ignoraba el campo y
    // no había forma de volver a "Stock total" o al precio base.
    sucursal_id: draft.sucursal_id || null,
    deposito_id: draft.deposito_id || null,
    lista_precios_id: draft.lista_precios_id || null,
    nombre_comercial: draft.identity.name,
    slogan: draft.identity.slogan,
    logo_url: draft.identity.logoUrl || null,
    favicon_url: draft.favicon_url || null,
    theme_config: {
      theme: draft.theme,
      typography: draft.typography,
    },
    layout_config: {
      layout: { ...draft.layout, bannerHoverEffect: draft.bannerHoverEffect },
      texts: draft.texts,
    },
    catalog_config: draft.catalog,
    seasonal_templates: draft.seasons,
    promotion_slots: draft.promotions,
    checkout_config: draft.checkout,
    workflow_config: draft.workflow,
    notifications_config: draft.notifications,
    footer_config: draft.footer,
    content_pages_config: draft.contentPages,
  };
}

const TABS = [
  { value: "pedidos", label: "Pedidos", icon: "lucide:shopping-bag" },
  { value: "pagos", label: "Pagos", icon: "lucide:credit-card" },
  { value: "configuracion", label: "Configuración", icon: "lucide:settings" },
  { value: "diseno", label: "Diseño", icon: "lucide:palette" },
  { value: "catalogo", label: "Catálogo", icon: "lucide:package-search" },
  { value: "promociones", label: "Promociones", icon: "lucide:badge-percent" },
  { value: "paginas", label: "Páginas", icon: "lucide:file-text" },
  { value: "novedades", label: "Novedades", icon: "lucide:newspaper" },
  { value: "seguimiento", label: "Seguimiento", icon: "lucide:map" },
  { value: "notificaciones", label: "Notificaciones", icon: "lucide:mail" },
];

const BANNER_EFFECT_OPTIONS = [
  {
    value: "none",
    label: "Sin efecto",
    description: "Banner estático sin animación.",
    icon: "lucide:minus",
  },
  {
    value: "effect-1",
    label: "Opacidad suave",
    description: "Reduce suavemente la opacidad.",
    icon: "lucide:blend",
  },
  {
    value: "effect-2",
    label: "Barrido claro",
    description: "Capa translúcida lateral.",
    icon: "lucide:scan-line",
  },
  {
    value: "effect-3",
    label: "Anillo central",
    description: "Anillo expansivo circular.",
    icon: "lucide:circle-dot",
  },
  {
    value: "effect-4",
    label: "Corte diagonal",
    description: "Bandas diagonales oscuras.",
    icon: "lucide:slash",
  },
  {
    value: "effect-5",
    label: "Marco doble",
    description: "Borde doble que colapsa.",
    icon: "lucide:scan",
  },
  {
    value: "effect-6",
    label: "Zoom oscuro",
    description: "Zoom lento con fondo oscuro.",
    icon: "lucide:zoom-in",
  },
  {
    value: "effect-7",
    label: "Cortina central",
    description: "Overlay desde el centro.",
    icon: "lucide:panel-left-open",
  },
  {
    value: "effect-8",
    label: "Marco fino",
    description: "Líneas internas animadas.",
    icon: "lucide:box-select",
  },
  {
    value: "effect-9",
    label: "Profundidad",
    description: "Perspectiva con capa oscura.",
    icon: "lucide:layers-3",
  },
  {
    value: "effect-10",
    label: "Gradiente inferior",
    description: "Gradiente desde la base.",
    icon: "lucide:panel-bottom",
  },
  {
    value: "effect-11",
    label: "Esquinas cruzadas",
    description: "Bloques desde esquinas.",
    icon: "lucide:move-diagonal",
  },
];

function getActiveTab(rawTab) {
  return TABS.some((tab) => tab.value === rawTab) ? rawTab : "pedidos";
}

const NOTIF_VARS_CLIENTE = "{{CLIENTE_NOMBRE}}, {{NRO_PEDIDO}}, {{TIENDA_NOMBRE}}, {{TOTAL}}, {{ITEMS}}, {{METODO_ENTREGA}}, {{DIRECCION_ENTREGA}}";
const NOTIF_VARS_ADMIN =
  "{{NRO_PEDIDO}}, {{CLIENTE_NOMBRE}}, {{CLIENTE_EMAIL}}, {{CLIENTE_TELEFONO}}, {{TOTAL}}, {{ITEMS}}, {{METODO_ENTREGA}}, {{DIRECCION_ENTREGA}}";

const EMAIL_TEMPLATES = [
  {
    key: "orderConfirmed",
    label: "Confirmación de pedido",
    description: "Se envía al cliente cuando confirma su compra en la tienda.",
    icon: "lucide:shopping-cart",
    color: "success",
    vars: NOTIF_VARS_CLIENTE,
    isAdmin: false,
  },
  {
    key: "orderInvoiced",
    label: "Pedido facturado",
    description: "Se envía al cliente cuando se factura su pedido. Adjunta el PDF de la factura si está disponible.",
    icon: "lucide:file-check",
    color: "primary",
    vars: NOTIF_VARS_CLIENTE,
    isAdmin: false,
  },
  {
    key: "orderShipped",
    label: "Pedido despachado",
    description: "Se envía al cliente cuando el pedido se marca como listo/despachado.",
    icon: "lucide:truck",
    color: "info",
    vars: NOTIF_VARS_CLIENTE,
    isAdmin: false,
  },
  {
    key: "orderCancelled",
    label: "Pedido cancelado",
    description: "Se envía al cliente cuando el pedido es cancelado.",
    icon: "lucide:x-circle",
    color: "error",
    vars: NOTIF_VARS_CLIENTE,
    isAdmin: false,
  },
  {
    key: "newOrderAdmin",
    label: "Nuevo pedido (admin)",
    description: "Se envía al email del administrador cuando llega un nuevo pedido.",
    icon: "lucide:bell-ring",
    color: "warning",
    vars: NOTIF_VARS_ADMIN,
    isAdmin: true,
  },
];

function makePreviewItemRow(emoji, name, qty, unitPrice, subtotal) {
  const gs = (n) => "Gs. " + n.toLocaleString("es-PY");
  return (
    `<tr>` +
    `<td style='padding:10px 6px 10px 14px;vertical-align:middle;border-bottom:1px solid #f3f4f6'><div style='width:36px;height:36px;background:#f3f4f6;border-radius:6px;text-align:center;line-height:36px;font-size:18px'>${emoji}</div></td>` +
    `<td style='padding:10px 8px;font-size:13px;font-weight:600;color:#111;vertical-align:middle;border-bottom:1px solid #f3f4f6'>${name}</td>` +
    `<td style='padding:10px 8px;font-size:13px;color:#6b7280;text-align:center;vertical-align:middle;border-bottom:1px solid #f3f4f6;white-space:nowrap'>× ${qty}</td>` +
    `<td style='padding:10px 8px;font-size:13px;color:#374151;text-align:right;vertical-align:middle;border-bottom:1px solid #f3f4f6;white-space:nowrap'>${gs(unitPrice)}</td>` +
    `<td style='padding:10px 14px 10px 8px;font-size:13px;font-weight:700;color:#111;text-align:right;vertical-align:middle;border-bottom:1px solid #f3f4f6;white-space:nowrap'>${gs(subtotal)}</td>` +
    `</tr>`
  );
}

const PREVIEW_SAMPLE = {
  CLIENTE_NOMBRE: "María García",
  NRO_PEDIDO: "ECOM-2026-0042",
  TOTAL: "Gs. 250.000",
  ITEMS:
    makePreviewItemRow("🧀", "Queso cremoso 400g", 2, 55000, 110000) +
    makePreviewItemRow("🥚", "Huevos frescos x12", 1, 32000, 32000) +
    makePreviewItemRow("🥛", "Leche entera 1L", 3, 18000, 54000),
  METODO_ENTREGA: "Delivery programado",
  DIRECCION_ENTREGA: "Av. España 1234, Asunción",
  CLIENTE_EMAIL: "maria@email.com",
  CLIENTE_TELEFONO: "0981 123456",
};

function applyPreviewVars(text, storeName) {
  const vars = { ...PREVIEW_SAMPLE, TIENDA_NOMBRE: storeName || "Mi Tienda" };
  return Object.entries(vars).reduce((acc, [k, v]) => acc.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v), text ?? "");
}

function buildEmailSrcdoc(body, subject, storeName, primaryColor) {
  const initial = (storeName || "T").charAt(0).toUpperCase();
  const year = new Date().getFullYear();
  const color = primaryColor || "#0f766e";
  return `<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{background:#eef0f3;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;font-size:14px;line-height:1.65;color:#1a1a1a;padding:20px 8px 32px}
.wrap{max-width:600px;margin:0 auto}
.card{background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 6px 32px rgba(0,0,0,0.12)}
.hdr{background:linear-gradient(135deg,${color},color-mix(in srgb,${color} 70%,#000));padding:26px 32px;text-align:center}
.hdr-ring{display:inline-flex;align-items:center;justify-content:center;width:52px;height:52px;border-radius:50%;background:rgba(255,255,255,0.18);border:2px solid rgba(255,255,255,0.35);font-size:22px;font-weight:900;color:#fff;margin-bottom:10px;letter-spacing:-1px}
.hdr-name{color:rgba(255,255,255,0.95);font-size:18px;font-weight:800;margin:0;letter-spacing:-0.3px}
.hdr-tag{display:inline-block;background:rgba(255,255,255,0.18);color:rgba(255,255,255,0.85);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:0.1em;padding:2px 10px;border-radius:999px;margin-top:6px}
.body{padding:28px 32px}
.body p{margin:0 0 14px;font-size:14px;color:#374151;line-height:1.7}
.body p:last-child{margin-bottom:0}
.body strong{font-weight:700;color:#111}
table{border-collapse:collapse;width:100%}
td{vertical-align:top}
.footer{background:#f3f4f6;border-top:1px solid #e5e7eb;padding:18px 32px;text-align:center;font-size:11px;color:#9ca3af;line-height:1.8}
.footer a{color:#6b7280;text-decoration:none}
</style>
</head>
<body>
<div class="wrap">
  <div class="card">
    <div class="hdr">
      <div class="hdr-ring">${initial}</div>
      <p class="hdr-name">${storeName || "Mi Tienda"}</p>
      <span class="hdr-tag">notificación automática</span>
    </div>
    <div class="body">${body}</div>
    <div class="footer">
      Este correo fue enviado automáticamente · Por favor no respondas<br/>
      © ${year} <strong>${storeName || "Mi Tienda"}</strong> — Todos los derechos reservados
    </div>
  </div>
</div>
</body>
</html>`;
}

function EmailClientPreview({ subject, body, tplKey, storeName, isAdmin, adminEmail, primaryColor }) {
  const [device, setDevice] = useState("desktop");
  const iframeRef = useRef(null);

  const renderedSubject = applyPreviewVars(subject, storeName);
  const previewVars = {
    ...PREVIEW_SAMPLE,
    TIENDA_NOMBRE: storeName || "Mi Tienda",
  };
  const structureHtml = EMAIL_STRUCTURES[tplKey]?.(previewVars) ?? "";
  const editableHtml = applyPreviewVars(body, storeName);
  const renderedBody = structureHtml + (editableHtml ? `<div style="margin-top:4px">${editableHtml}</div>` : "");
  const srcdoc = buildEmailSrcdoc(renderedBody, renderedSubject, storeName, primaryColor);

  const toAddress = isAdmin ? adminEmail || "admin@tienda.com" : "maria@email.com";
  const initial = (storeName || "T").charAt(0).toUpperCase();
  const timeStr = new Date().toLocaleTimeString("es-PY", {
    hour: "2-digit",
    minute: "2-digit",
  });

  // Auto-resize iframe height on load
  const handleIframeLoad = () => {
    try {
      const doc = iframeRef.current?.contentDocument;
      if (doc) {
        const h = doc.documentElement.scrollHeight;
        iframeRef.current.style.height = `${Math.max(200, h)}px`;
      }
    } catch {
      // cross-origin guard — won't happen with srcdoc
    }
  };

  return (
    <Box
      sx={{
        border: "1px solid",
        borderColor: "divider",
        borderRadius: 2,
        overflow: "hidden",
        bgcolor: "grey.50",
      }}
    >
      {/* Toolbar */}
      <Stack
        direction="row"
        alignItems="center"
        spacing={1}
        sx={{
          px: 2,
          py: 0.75,
          bgcolor: "background.paper",
          borderBottom: "1px solid",
          borderColor: "divider",
        }}
      >
        <Icon icon="lucide:mail" style={{ fontSize: 14, opacity: 0.5 }} />
        <Typography variant="caption" fontWeight={700} sx={{ flex: 1, color: "text.secondary" }}>
          Vista previa del correo
        </Typography>
        <ToggleButtonGroup
          size="small"
          value={device}
          exclusive
          onChange={(_, v) => v && setDevice(v)}
          sx={{ "& .MuiToggleButton-root": { py: 0.25, px: 0.75 } }}
        >
          <ToggleButton value="desktop">
            <Tooltip title="Escritorio">
              <Icon icon="lucide:monitor" style={{ fontSize: 15 }} />
            </Tooltip>
          </ToggleButton>
          <ToggleButton value="mobile">
            <Tooltip title="Móvil">
              <Icon icon="lucide:smartphone" style={{ fontSize: 15 }} />
            </Tooltip>
          </ToggleButton>
        </ToggleButtonGroup>
      </Stack>

      {/* Gmail-style email header */}
      <Box
        sx={{
          px: 2.5,
          py: 1.5,
          bgcolor: "background.paper",
          borderBottom: "1px solid",
          borderColor: "divider",
        }}
      >
        <Typography variant="subtitle2" fontWeight={700} sx={{ mb: 1, fontSize: 15 }}>
          {renderedSubject || "(sin asunto)"}
        </Typography>
        <Stack direction="row" alignItems="flex-start" spacing={1.25}>
          <Box
            sx={{
              width: 36,
              height: 36,
              borderRadius: "50%",
              background: primaryColor || "#0f766e",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              color: "#fff",
              fontSize: 14,
              fontWeight: 800,
              flexShrink: 0,
            }}
          >
            {initial}
          </Box>
          <Box sx={{ flex: 1, minWidth: 0 }}>
            <Stack direction="row" alignItems="center" justifyContent="space-between" spacing={1}>
              <Typography variant="body2" fontWeight={600} noWrap sx={{ fontSize: 13 }}>
                {storeName || "Mi Tienda"}{" "}
                <Typography component="span" variant="caption" color="text.secondary" sx={{ fontWeight: 400 }}>
                  &lt;notificaciones@tienda.com&gt;
                </Typography>
              </Typography>
              <Stack direction="row" alignItems="center" spacing={0.5} sx={{ flexShrink: 0 }}>
                <Typography variant="caption" color="text.secondary">
                  {timeStr}
                </Typography>
                <Icon icon="lucide:star" style={{ fontSize: 14, opacity: 0.4 }} />
                <Icon icon="lucide:reply" style={{ fontSize: 14, opacity: 0.4 }} />
                <Icon icon="lucide:more-vertical" style={{ fontSize: 14, opacity: 0.4 }} />
              </Stack>
            </Stack>
            <Typography variant="caption" color="text.secondary">
              Para: <strong>{toAddress}</strong>
            </Typography>
          </Box>
        </Stack>
      </Box>

      {/* Email body */}
      <Box
        sx={{
          display: "flex",
          justifyContent: "center",
          alignItems: "flex-start",
          bgcolor: "#f0f2f5",
          p: device === "mobile" ? 2 : 0,
          minHeight: 200,
        }}
      >
        {device === "mobile" ? (
          /* Phone frame */
          <Box
            sx={{
              width: 375,
              border: "10px solid #1c1c1e",
              borderRadius: "36px",
              overflow: "hidden",
              boxShadow: "0 20px 60px rgba(0,0,0,0.35), inset 0 0 0 1px rgba(255,255,255,0.08)",
              position: "relative",
            }}
          >
            {/* Notch */}
            <Box
              sx={{
                bgcolor: "#1c1c1e",
                height: 28,
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
            >
              <Box
                sx={{
                  width: 80,
                  height: 10,
                  bgcolor: "#111",
                  borderRadius: 99,
                }}
              />
            </Box>
            <iframe
              ref={iframeRef}
              srcDoc={srcdoc}
              title="Vista previa móvil"
              onLoad={handleIframeLoad}
              style={{
                width: "100%",
                height: 520,
                border: "none",
                display: "block",
              }}
              sandbox="allow-same-origin"
            />
          </Box>
        ) : (
          /* Desktop — full width */
          <iframe
            ref={iframeRef}
            srcDoc={srcdoc}
            title="Vista previa escritorio"
            onLoad={handleIframeLoad}
            style={{
              width: "100%",
              height: 400,
              border: "none",
              display: "block",
            }}
            sandbox="allow-same-origin"
          />
        )}
      </Box>
    </Box>
  );
}

EmailClientPreview.propTypes = {
  subject: PropTypes.string,
  body: PropTypes.string,
  tplKey: PropTypes.string,
  storeName: PropTypes.string,
  isAdmin: PropTypes.bool,
  adminEmail: PropTypes.string,
  primaryColor: PropTypes.string,
};

function NotificacionesPanel({ notifications, onChange, storeName, primaryColor }) {
  const patch = (key, value) => onChange({ ...notifications, [key]: value });

  const patchTemplate = (templateKey, field, value) => patch(templateKey, { ...notifications[templateKey], [field]: value });

  const [enviandoTest, setEnviandoTest] = useState(null);
  const enviarPrueba = async (tplKey) => {
    setEnviandoTest(tplKey);
    try {
      const res = await ecommerceService.enviarNotificacionPrueba(tplKey, notifications.adminEmail || undefined);
      toast.success(`Email de prueba enviado a ${res.to}`);
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudo enviar el email de prueba");
    } finally {
      setEnviandoTest(null);
    }
  };

  return (
    <Stack spacing={3}>
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Email del administrador" donde="Email «Nuevo pedido» que recibís vos" zona="email" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Dirección donde se reciben las notificaciones internas (nuevo pedido, alertas).
            </Typography>
          </SeccionTienda>
          <Alert severity="info" icon={<Icon icon="lucide:mail-check" width={18} />} sx={{ py: 0.5 }}>
            Los emails de los pedidos reales salen con las plantillas <strong>publicadas</strong>. «Enviar prueba» usa lo que estás
            editando ahora, así podés revisar cómo llega antes de publicar.
          </Alert>
          <TextField
            label="Email del administrador"
            size="small"
            fullWidth
            type="email"
            value={notifications.adminEmail ?? ""}
            onChange={(e) => patch("adminEmail", e.target.value)}
            placeholder="ventas@miempresa.com"
            helperText="Dejá vacío para no recibir notificaciones internas."
          />
        </Stack>
      </Paper>

      {EMAIL_TEMPLATES.map((tpl) => {
        const cfg = notifications[tpl.key] ?? {};
        return (
          <Paper key={tpl.key} variant="outlined" sx={{ p: 2 }}>
            <Stack spacing={2}>
              {/* Header */}
              <Stack direction="row" alignItems="center" justifyContent="space-between" spacing={1}>
                <Stack direction="row" alignItems="center" spacing={1}>
                  <Icon icon={tpl.icon} style={{ fontSize: 18, opacity: 0.7 }} />
                  <Box>
                    <Typography variant="subtitle1" fontWeight={700}>
                      {tpl.label}
                    </Typography>
                    <Typography variant="body2" color="text.secondary">
                      {tpl.description}
                    </Typography>
                  </Box>
                </Stack>
                <Stack direction="row" alignItems="center" spacing={1} sx={{ flexShrink: 0 }}>
                  <Tooltip title="Envía este email, tal como lo estás editando, con datos de ejemplo al email del administrador" arrow>
                    <span>
                      <Button
                        size="small"
                        variant="outlined"
                        disabled={enviandoTest === tpl.key || cfg.enabled === false}
                        onClick={() => enviarPrueba(tpl.key)}
                        startIcon={
                          enviandoTest === tpl.key ? (
                            <CircularProgress size={13} />
                          ) : (
                            <Icon icon="lucide:send" width={14} />
                          )
                        }
                      >
                        Enviar prueba
                      </Button>
                    </span>
                  </Tooltip>
                  <FormControlLabel
                    control={<Switch checked={cfg.enabled ?? true} onChange={(e) => patchTemplate(tpl.key, "enabled", e.target.checked)} color={tpl.color} />}
                    label={<Typography variant="caption">{cfg.enabled !== false ? "Activo" : "Inactivo"}</Typography>}
                    labelPlacement="start"
                    sx={{ m: 0 }}
                  />
                </Stack>
              </Stack>

              {/* Fields */}
              <TextField
                label="Asunto"
                size="small"
                fullWidth
                value={cfg.subject ?? ""}
                onChange={(e) => patchTemplate(tpl.key, "subject", e.target.value)}
                disabled={cfg.enabled === false}
              />

              <Box>
                <Typography variant="caption" color="text.secondary" sx={{ mb: 0.75, display: "block" }}>
                  Mensaje personalizado <span style={{ opacity: 0.65 }}>— el diseño estructural se genera automáticamente</span>
                </Typography>
                <WysiwygEmailEditor value={cfg.body ?? ""} onChange={(html) => patchTemplate(tpl.key, "body", html)} disabled={cfg.enabled === false} />
                <Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: "block" }}>
                  Variables disponibles: <code style={{ fontSize: 11 }}>{tpl.vars}</code>
                </Typography>
              </Box>

              {/* Email preview */}
              {cfg.enabled !== false && (
                <EmailClientPreview
                  subject={cfg.subject ?? ""}
                  body={cfg.body ?? ""}
                  tplKey={tpl.key}
                  storeName={storeName}
                  isAdmin={tpl.isAdmin}
                  adminEmail={notifications.adminEmail}
                  primaryColor={primaryColor}
                />
              )}
            </Stack>
          </Paper>
        );
      })}
    </Stack>
  );
}

NotificacionesPanel.propTypes = {
  notifications: PropTypes.object.isRequired,
  onChange: PropTypes.func.isRequired,
  storeName: PropTypes.string,
  primaryColor: PropTypes.string,
};

// Nota: ya no queda ninguna pestaña sin implementar — el PlaceholderPanel que
// vivía acá se eliminó al construir la pestaña Pagos.

const NOVEDAD_EMPTY = {
  tipo: "anuncio",
  titulo: "",
  descripcion: "",
  tag: "",
  cta_label: "",
  target_category: "",
  imagen_url: "",
  prioridad: 10,
  activo: true,
};

// Alineado con las opciones del selector de TablePaginationBar (10/20/50/100).
const NOVEDADES_PAGE_SIZE = 10;

function NovedadesPanel() {
  const [items, setItems] = useState([]);
  const [categorias, setCategorias] = useState([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [imgUploading, setImgUploading] = useState(false);
  const [dialogOpen, setDialogOpen] = useState(false);
  const [editing, setEditing] = useState(null);
  const [form, setForm] = useState(NOVEDAD_EMPTY);
  const [search, setSearch] = useState("");
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(NOVEDADES_PAGE_SIZE);
  const [deleteTarget, setDeleteTarget] = useState(null);
  const [deleting, setDeleting] = useState(false);
  const dialogFileRef = useRef(null);

  const cargar = useCallback(async () => {
    setLoading(true);
    try {
      const [novedades, cats] = await Promise.all([ecommerceService.listNovedades(), ecommerceService.getCategorias()]);
      setItems(novedades);
      setCategorias(cats);
    } catch {
      toast.error("No se pudieron cargar las novedades");
    } finally {
      setLoading(false);
    }
  }, []);

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

  const filtered = useMemo(() => {
    const q = search.trim().toLowerCase();
    return q ? items.filter((i) => i.titulo.toLowerCase().includes(q) || (i.tag ?? "").toLowerCase().includes(q)) : items;
  }, [items, search]);

  const pageCount = Math.max(1, Math.ceil(filtered.length / pageSize));
  const pageItems = filtered.slice((page - 1) * pageSize, page * pageSize);

  const openCreate = () => {
    setEditing(null);
    setForm(NOVEDAD_EMPTY);
    setDialogOpen(true);
  };

  const openEdit = (item) => {
    setEditing(item);
    setForm({
      tipo: item.tipo,
      titulo: item.titulo,
      descripcion: item.descripcion ?? "",
      tag: item.tag ?? "",
      cta_label: item.cta_label ?? "",
      target_category: item.target_category ?? "",
      imagen_url: item.imagen_url ?? "",
      prioridad: item.prioridad,
      activo: item.activo,
    });
    setDialogOpen(true);
  };

  const handleSave = async () => {
    if (!form.titulo.trim()) {
      toast.error("El título es obligatorio");
      return;
    }
    setSaving(true);
    try {
      if (editing) {
        const updated = await ecommerceService.updateNovedad(editing.id, form);
        setItems((prev) => prev.map((i) => (i.id === editing.id ? updated : i)));
        setEditing((prev) => ({ ...prev, ...updated }));
      } else {
        const created = await ecommerceService.createNovedad(form);
        setItems((prev) => [...prev, created]);
        setEditing(created);
      }
      toast.success(editing ? "Novedad guardada" : "Novedad creada");
    } catch {
      toast.error("No se pudo guardar la novedad");
    } finally {
      setSaving(false);
    }
  };

  const handleClose = () => {
    setDialogOpen(false);
    setEditing(null);
  };

  const handleDelete = async () => {
    if (!deleteTarget) return;
    setDeleting(true);
    try {
      await ecommerceService.deleteNovedad(deleteTarget.id);
      setItems((prev) => prev.filter((i) => i.id !== deleteTarget.id));
      toast.success("Novedad eliminada");
      setDeleteTarget(null);
    } catch {
      toast.error("No se pudo eliminar");
    } finally {
      setDeleting(false);
    }
  };

  const handleToggleActivo = async (item) => {
    try {
      const updated = await ecommerceService.updateNovedad(item.id, {
        activo: !item.activo,
      });
      setItems((prev) => prev.map((i) => (i.id === item.id ? { ...i, activo: updated.activo } : i)));
    } catch {
      toast.error("No se pudo cambiar el estado");
    }
  };

  const handleDialogFileChange = async (e) => {
    const file = e.target.files?.[0];
    if (!file || !editing?.id) return;
    e.target.value = "";
    setImgUploading(true);
    try {
      const updated = await ecommerceService.uploadNovedadImage(editing.id, file);
      setItems((prev) => prev.map((i) => (i.id === editing.id ? { ...i, imagen_url: updated.imagen_url } : i)));
      setEditing((prev) => ({ ...prev, imagen_url: updated.imagen_url }));
      toast.success("Imagen subida");
    } catch {
      toast.error("No se pudo subir la imagen");
    } finally {
      setImgUploading(false);
    }
  };

  const handleDeleteImg = async () => {
    if (!editing?.id) return;
    setImgUploading(true);
    try {
      await ecommerceService.deleteNovedadImage(editing.id);
      setItems((prev) => prev.map((i) => (i.id === editing.id ? { ...i, imagen_url: null } : i)));
      setEditing((prev) => ({ ...prev, imagen_url: null }));
      toast.success("Imagen eliminada");
    } catch {
      toast.error("No se pudo eliminar la imagen");
    } finally {
      setImgUploading(false);
    }
  };

  const tipoMeta = (tipo) => TIPO_NOVEDAD_OPTIONS.find((o) => o.value === tipo) ?? TIPO_NOVEDAD_OPTIONS[1];

  return (
    <Stack spacing={2}>
      <input type="file" accept="image/*" ref={dialogFileRef} style={{ display: "none" }} onChange={handleDialogFileChange} />

      {/* Barra superior */}
      <Paper variant="outlined" sx={{ p: 1.5 }}>
        <Stack direction={{ xs: "column", sm: "row" }} alignItems={{ sm: "center" }} spacing={1.5}>
          <Box sx={{ flex: 1, minWidth: 0 }}>
            <SeccionTienda titulo="Novedades" donde="Bloque «Novedades» al costado de la portada" zona="carruseles" cuando="instante">
              <Typography variant="body2" color="text.secondary">
                Anuncios, ingresos y campañas. Aparecen en la tienda apenas las creás activas y dentro de su vigencia, sin publicar.
              </Typography>
            </SeccionTienda>
          </Box>
          <Stack direction="row" spacing={1} alignItems="center">
            <TextField
              size="small"
              placeholder="Buscar..."
              value={search}
              onChange={(e) => {
                setSearch(e.target.value);
                setPage(1);
              }}
              sx={{ width: 180 }}
            />
            <Button variant="contained" size="small" startIcon={<Icon icon="lucide:plus" />} onClick={openCreate}>
              Nueva
            </Button>
          </Stack>
        </Stack>
      </Paper>

      {/* Lista */}
      {loading ? (
        <Stack alignItems="center" sx={{ py: 5 }}>
          <CircularProgress size={26} />
        </Stack>
      ) : filtered.length === 0 ? (
        <EmptyState
          icon={search ? "lucide:search-x" : "lucide:newspaper"}
          title={search ? "Sin coincidencias" : "Sin novedades creadas"}
          description={search ? `Ninguna novedad coincide con "${search}".` : "Creá anuncios, nuevos ingresos o campañas para mostrar en la tienda."}
          ctaLabel={!search ? "Nueva novedad" : undefined}
          ctaIcon="lucide:plus"
          onCta={!search ? openCreate : undefined}
          compact
        />
      ) : (
        <>
          <Stack spacing={0.75}>
            {pageItems.map((item) => {
              const meta = tipoMeta(item.tipo);
              return (
                <Paper
                  key={item.id}
                  variant="outlined"
                  sx={{
                    p: 1.25,
                    opacity: item.activo ? 1 : 0.55,
                    cursor: "pointer",
                    "&:hover": { borderColor: "primary.main" },
                  }}
                  onClick={() => openEdit(item)}
                >
                  <Stack direction="row" alignItems="center" spacing={1.5}>
                    <CategoryImageThumb sx={{ flexShrink: 0 }}>
                      {item.imagen_url ? (
                        <img
                          src={item.imagen_url}
                          alt={item.titulo}
                          style={{
                            width: "100%",
                            height: "100%",
                            objectFit: "cover",
                            borderRadius: 8,
                          }}
                        />
                      ) : (
                        <Icon icon={meta.icon} style={{ color: "#94a3b8", width: 20, height: 20 }} />
                      )}
                    </CategoryImageThumb>
                    <Box sx={{ flex: 1, minWidth: 0 }}>
                      <Stack direction="row" alignItems="center" spacing={0.75} sx={{ mb: 0.25 }}>
                        <Chip size="small" label={meta.label} variant="outlined" sx={{ fontSize: "0.67rem", height: 20 }} />
                        {item.tag && (
                          <Typography variant="caption" color="text.secondary">
                            #{item.tag}
                          </Typography>
                        )}
                      </Stack>
                      <Typography variant="body2" fontWeight={700} noWrap>
                        {item.titulo}
                      </Typography>
                      {item.descripcion && (
                        <Typography variant="caption" color="text.secondary" noWrap>
                          {item.descripcion}
                        </Typography>
                      )}
                    </Box>
                    <Stack direction="row" spacing={0.25} flexShrink={0} alignItems="center" onClick={(e) => e.stopPropagation()}>
                      <Tooltip title={item.activo ? "Pausar" : "Activar"} enterDelay={300}>
                        <Switch size="small" checked={item.activo} onChange={() => handleToggleActivo(item)} />
                      </Tooltip>
                      <Tooltip title="Editar" enterDelay={300}>
                        <IconButton size="small" onClick={() => openEdit(item)}>
                          <Icon icon="lucide:pencil" width={14} />
                        </IconButton>
                      </Tooltip>
                      <Tooltip title="Eliminar" enterDelay={300}>
                        <IconButton size="small" color="error" onClick={() => setDeleteTarget(item)}>
                          <Icon icon="lucide:trash-2" width={14} />
                        </IconButton>
                      </Tooltip>
                    </Stack>
                  </Stack>
                </Paper>
              );
            })}
          </Stack>
          <TablePaginationBar
            page={page}
            totalPages={pageCount}
            onPageChange={setPage}
            total={filtered.length}
            shown={pageItems.length}
            noun="novedades"
            rowsPerPage={pageSize}
            onRowsPerPageChange={(n) => {
              setPageSize(n);
              setPage(1);
            }}
          />
        </>
      )}

      {/* Dialog crear / editar */}
      <Dialog open={dialogOpen} onClose={handleClose} maxWidth="sm" fullWidth>
        <DialogTitle sx={{ display: "flex", alignItems: "center", gap: 1.25, pb: 1 }}>
          <Box
            sx={{
              width: 36,
              height: 36,
              borderRadius: "10px",
              bgcolor: "action.hover",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              color: "primary.main",
              flexShrink: 0,
            }}
          >
            <Icon icon={editing ? "lucide:edit-3" : "lucide:newspaper"} width={18} />
          </Box>
          <Box sx={{ flex: 1, minWidth: 0 }}>
            <Typography variant="subtitle1" fontWeight={700} lineHeight={1.2}>
              {editing ? "Editar novedad" : "Nueva novedad"}
            </Typography>
            {editing && (
              <Typography variant="caption" color="text.secondary" noWrap>
                {editing.titulo}
              </Typography>
            )}
          </Box>
          <IconButton size="small" onClick={handleClose}>
            <Icon icon="lucide:x" width={16} />
          </IconButton>
        </DialogTitle>

        <DialogContent dividers sx={{ p: 2 }}>
          <Stack spacing={2}>
            <TextField
              select
              size="small"
              label="Tipo"
              SelectProps={{ native: true }}
              InputLabelProps={{ shrink: true }}
              value={form.tipo}
              onChange={(e) => setForm((p) => ({ ...p, tipo: e.target.value }))}
            >
              {TIPO_NOVEDAD_OPTIONS.map((o) => (
                <option key={o.value} value={o.value}>
                  {o.label}
                </option>
              ))}
            </TextField>

            <TextField
              size="small"
              label="Título *"
              value={form.titulo}
              onChange={(e) => setForm((p) => ({ ...p, titulo: e.target.value }))}
              inputProps={{ maxLength: 200 }}
            />

            <TextField
              size="small"
              label="Descripción"
              multiline
              rows={2}
              value={form.descripcion}
              onChange={(e) => setForm((p) => ({ ...p, descripcion: e.target.value }))}
            />

            <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
              <TextField
                size="small"
                label="Tag / Etiqueta"
                sx={{ flex: 1 }}
                value={form.tag}
                onChange={(e) => setForm((p) => ({ ...p, tag: e.target.value }))}
                helperText="Ej: Nuevo ingreso"
                inputProps={{ maxLength: 60 }}
              />
              <TextField
                size="small"
                label="Texto del botón CTA"
                sx={{ flex: 1 }}
                value={form.cta_label}
                onChange={(e) => setForm((p) => ({ ...p, cta_label: e.target.value }))}
                helperText="Ej: Ver frescos"
                inputProps={{ maxLength: 80 }}
              />
            </Stack>

            <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
              <TextField
                select
                size="small"
                label="Categoría destino (filtro)"
                sx={{ flex: 1 }}
                SelectProps={{ native: true }}
                InputLabelProps={{ shrink: true }}
                value={form.target_category}
                onChange={(e) => setForm((p) => ({ ...p, target_category: e.target.value }))}
                helperText="Al hacer click filtra por esta categoría"
              >
                <option value="">— Sin filtro —</option>
                {categorias.map((c) => (
                  <option key={c.id} value={c.name}>
                    {c.name} ({c.count})
                  </option>
                ))}
              </TextField>
              <TextField
                type="number"
                size="small"
                label="Prioridad"
                sx={{ width: 120 }}
                value={form.prioridad}
                onChange={(e) => setForm((p) => ({ ...p, prioridad: Number(e.target.value) }))}
                inputProps={{ min: 0, max: 100 }}
                helperText="Mayor = primero"
              />
            </Stack>

            <FormControlLabel
              control={<Switch checked={form.activo} onChange={(e) => setForm((p) => ({ ...p, activo: e.target.checked }))} />}
              label="Activa en la tienda"
            />

            <Divider />
            <ImageUploadField
              label="Imagen de la novedad"
              value={form.imagen_url}
              onChange={(url) => setForm((prev) => ({ ...prev, imagen_url: url }))}
              uploadFn={ecommerceService.uploadStoreImage}
              slot="novedad"
              height={150}
              helperText="Se muestra en la tarjeta de la novedad. Se guarda al crear/actualizar."
            />
          </Stack>
        </DialogContent>

        <DialogActions sx={{ px: 2, py: 1.5 }}>
          <Button onClick={handleClose}>Cancelar</Button>
          <Button variant="contained" onClick={handleSave} disabled={saving}>
            {saving ? <CircularProgress size={16} color="inherit" /> : editing ? "Guardar cambios" : "Crear"}
          </Button>
        </DialogActions>
      </Dialog>

      {/* Confirmación de eliminación */}
      <Dialog open={!!deleteTarget} onClose={() => setDeleteTarget(null)} maxWidth="xs" fullWidth>
        <DialogTitle sx={{ display: "flex", alignItems: "center", gap: 1 }}>
          <Box
            sx={{
              width: 34,
              height: 34,
              borderRadius: "8px",
              bgcolor: "error.50",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              color: "error.main",
              flexShrink: 0,
            }}
          >
            <Icon icon="lucide:trash-2" width={16} />
          </Box>
          Eliminar novedad
        </DialogTitle>
        <DialogContent>
          <Typography variant="body2">
            ¿Eliminar <strong>{deleteTarget?.titulo}</strong>? Esta acción no se puede deshacer.
          </Typography>
        </DialogContent>
        <DialogActions sx={{ px: 2, py: 1.5 }}>
          <Button onClick={() => setDeleteTarget(null)} disabled={deleting}>
            Cancelar
          </Button>
          <Button variant="contained" color="error" onClick={handleDelete} disabled={deleting}>
            {deleting ? <CircularProgress size={16} color="inherit" /> : "Eliminar"}
          </Button>
        </DialogActions>
      </Dialog>
    </Stack>
  );
}

function SeguimientoPanel({ storeConfig }) {
  const trackingUrl = buildStoreUrl(storeConfig, "/?seguimiento");
  const [qrDataUrl, setQrDataUrl] = useState("");

  useEffect(() => {
    if (!trackingUrl) return undefined;
    let active = true;
    import("qrcode")
      .then((QRCode) => QRCode.toDataURL(trackingUrl, { width: 480, margin: 1 }))
      .then((url) => active && setQrDataUrl(url))
      .catch(() => undefined);
    return () => {
      active = false;
    };
  }, [trackingUrl]);

  const copiarLink = async () => {
    try {
      await navigator.clipboard.writeText(trackingUrl);
      toast.success("Link de seguimiento copiado");
    } catch {
      toast.error("No se pudo copiar el link");
    }
  };

  return (
    <Stack spacing={2}>
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack direction={{ xs: "column", sm: "row" }} spacing={2.5} alignItems={{ sm: "center" }}>
          <Box sx={{ flex: 1, minWidth: 0 }}>
            <Typography variant="subtitle1" fontWeight={700}>
              Link público de seguimiento
            </Typography>
            <Typography variant="body2" color="text.secondary" sx={{ mb: 1.5 }}>
              Compartilo con tus clientes o imprimí el QR para el local o el paquete: abre la tienda con el consultor de pedidos listo para ingresar el código.
            </Typography>
            {trackingUrl ? (
              <Stack direction="row" spacing={1} useFlexGap flexWrap="wrap" alignItems="center">
                <Chip size="small" variant="outlined" sx={{ maxWidth: "100%" }} label={trackingUrl} />
                <Button size="small" variant="outlined" onClick={copiarLink} startIcon={<Icon icon="lucide:copy" width={14} />}>
                  Copiar link
                </Button>
                <Button
                  size="small"
                  variant="outlined"
                  component="a"
                  href={qrDataUrl || undefined}
                  download={`seguimiento-${storeConfig?.subdominio || "tienda"}.png`}
                  disabled={!qrDataUrl}
                  startIcon={<Icon icon="lucide:download" width={14} />}
                >
                  Descargar QR
                </Button>
              </Stack>
            ) : (
              <Typography variant="body2" color="text.secondary">
                Configurá el subdominio o el dominio propio de la tienda para generar el link.
              </Typography>
            )}
          </Box>
          {qrDataUrl && (
            <Box sx={{ flexShrink: 0, textAlign: "center" }}>
              <img src={qrDataUrl} alt="QR de seguimiento" style={{ width: 132, height: 132, borderRadius: 8, border: "1px solid rgba(128,128,128,.3)" }} />
              <Typography variant="caption" color="text.secondary" display="block">
                Escaneá para probarlo
              </Typography>
            </Box>
          )}
        </Stack>
      </Paper>

      {/* Acá había un «Mapa de seguimiento» con texto de desarrollo («Primera versión
          preparada para Leaflet…», «Vista de mapa pendiente») visible para el comercio,
          y una línea de tiempo que no coincidía con los estados reales. */}
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Typography variant="subtitle1" fontWeight={700}>
          Qué ve el cliente
        </Typography>
        <Typography variant="body2" color="text.secondary" sx={{ mb: 1.5 }}>
          Con el código del pedido, el seguimiento muestra cada paso a medida que lo avanzás en Pedidos.
        </Typography>
        <Stack spacing={1.25}>
          {[
            "Pedido recibido",
            "Pago confirmado",
            "En verificación y verificado",
            "En preparación y empaquetado",
            "En camino (delivery) o listo para retiro",
            "Entregado o retirado",
          ].map((label) => (
            <TimelineRow key={label}>
              <Icon icon="lucide:circle" />
              <Typography variant="body2">{label}</Typography>
            </TimelineRow>
          ))}
        </Stack>
      </Paper>
    </Stack>
  );
}

SeguimientoPanel.propTypes = {
  storeConfig: PropTypes.shape({
    subdominio: PropTypes.string,
    dominio_custom: PropTypes.string,
  }),
};

const COLOR_FIELDS = [
  { key: "primary", label: "Primario" },
  { key: "secondary", label: "Secundario" },
  { key: "accent", label: "Acento" },
  { key: "background", label: "Fondo" },
  { key: "surface", label: "Superficie" },
  { key: "text", label: "Texto" },
  { key: "muted", label: "Texto suave" },
  { key: "success", label: "Éxito" },
  { key: "danger", label: "Error" },
  { key: "priceColor", label: "Color del precio" },
];

// Valores por defecto del tema (mismos fallbacks que usa la normalización del
// config al cargar). El botón "Restablecer" los reaplica de una.
const DEFAULT_THEME = Object.freeze({
  primary: "#0f766e",
  secondary: "#1d4ed8",
  accent: "#f97316",
  background: "#ffffff",
  surface: "#ffffff",
  text: "#14211f",
  muted: "#66736f",
  success: "#15803d",
  danger: "#dc2626",
  priceColor: "#0f766e",
});

// Réplica de la lógica del store (getActiveSeason): temporada habilitada cuya
// franja de fechas incluye hoy. Sus themeOverrides pisan la paleta base.
function findActiveSeason(seasons, today = new Date()) {
  if (!Array.isArray(seasons)) return null;
  const now = today.getTime();
  return (
    seasons.find((s) => {
      if (!s?.enabled || !s.startDate || !s.endDate) return false;
      const start = new Date(`${s.startDate}T00:00:00`).getTime();
      const end = new Date(`${s.endDate}T23:59:59`).getTime();
      return now >= start && now <= end;
    }) ?? null
  );
}

const COLOR_PRESETS = [
  {
    name: "Verde Novasis",
    theme: {
      primary: "#0f766e",
      secondary: "#1d4ed8",
      accent: "#f97316",
      background: "#ffffff",
      surface: "#ffffff",
      text: "#14211f",
      muted: "#66736f",
      success: "#15803d",
      danger: "#dc2626",
    },
  },
  {
    name: "Violeta",
    theme: {
      primary: "#9333ea",
      secondary: "#4f46e5",
      accent: "#ec4899",
      background: "#ffffff",
      surface: "#ffffff",
      text: "#1e1b2e",
      muted: "#6b7280",
      success: "#16a34a",
      danger: "#dc2626",
    },
  },
  {
    name: "Azul corporativo",
    theme: {
      primary: "#1d4ed8",
      secondary: "#0ea5e9",
      accent: "#f59e0b",
      background: "#ffffff",
      surface: "#ffffff",
      text: "#0f172a",
      muted: "#64748b",
      success: "#15803d",
      danger: "#dc2626",
    },
  },
  {
    name: "Rojo retail",
    theme: {
      primary: "#dc2626",
      secondary: "#1f2937",
      accent: "#f59e0b",
      background: "#ffffff",
      surface: "#ffffff",
      text: "#1f2937",
      muted: "#6b7280",
      success: "#16a34a",
      danger: "#b91c1c",
    },
  },
  {
    name: "Naranja vibrante",
    theme: {
      primary: "#ea580c",
      secondary: "#0f766e",
      accent: "#facc15",
      background: "#ffffff",
      surface: "#fffaf5",
      text: "#231a12",
      muted: "#7c6f63",
      success: "#15803d",
      danger: "#dc2626",
    },
  },
  {
    name: "Oscuro elegante",
    theme: {
      primary: "#a855f7",
      secondary: "#22d3ee",
      accent: "#f472b6",
      background: "#0f111a",
      surface: "#171a26",
      text: "#f8fafc",
      muted: "#94a3b8",
      success: "#22c55e",
      danger: "#f87171",
    },
  },
];

const FONT_OPTIONS = ["Inter", "Poppins", "Roboto", "Montserrat", "Nunito", "Work Sans", "Playfair Display"];

const SCALE_OPTIONS = [
  { value: "compact", label: "Compacta" },
  { value: "normal", label: "Normal" },
  { value: "wide", label: "Amplia" },
];

function DesignPanel({
  identity,
  faviconUrl,
  theme,
  typography,
  layout,
  selectedBannerEffect,
  seasons,
  onIdentityChange,
  onFaviconChange,
  onThemeChange,
  onTypographyChange,
  onLayoutChange,
  onBannerEffectChange,
  onApplyPreset,
}) {
  const matchesPreset = (preset) => Object.keys(preset.theme).every((k) => (theme[k] || "").toLowerCase() === preset.theme[k].toLowerCase());
  const selected = BANNER_EFFECT_OPTIONS.find((effect) => effect.value === selectedBannerEffect);
  // La tienda aplica los colores de la temporada activa por encima de la paleta
  // base (activeTheme = { ...base, ...season.themeOverrides }). Si hay una activa,
  // avisamos: si no, el usuario cambia los colores base y "no pasa nada".
  const activeSeason = findActiveSeason(seasons);

  return (
    <Stack spacing={2}>
      {activeSeason && (
        <Alert severity="warning" icon={<Icon icon="lucide:calendar-clock" width={18} />}>
          La temporada <strong>{activeSeason.name || "activa"}</strong> está vigente
          {activeSeason.endDate ? ` hasta el ${activeSeason.endDate.split("-").reverse().join("/")}` : ""} y
          <strong> sobrescribe estos colores</strong> en la tienda: mientras esté activa vas a ver la paleta de la
          temporada, no la base. Para ver tus colores base, editá los colores de la temporada o desactivala en{" "}
          <strong>Temporadas</strong> (más abajo).
        </Alert>
      )}
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Box>
          <Typography variant="subtitle1" fontWeight={700}>
            Diseño de la tienda
          </Typography>
          <Typography variant="body2" color="text.secondary">
            Colores, tipografía, tarjetas, buscador, espacios de promociones y efectos visuales de tu tienda.
          </Typography>
        </Box>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Identidad" donde="Encabezado de todas las páginas y pestaña del navegador" zona="header" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Nombre comercial y logo visibles en el encabezado de la tienda.
            </Typography>
          </SeccionTienda>
          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
            <TextField
              size="small"
              label="Nombre comercial"
              value={identity.name || ""}
              onChange={(e) => onIdentityChange("name", e.target.value)}
              sx={{ minWidth: 240, flex: 1 }}
            />
          </Stack>
          <Stack direction="row" spacing={3} useFlexGap flexWrap="wrap">
            <Box sx={{ flex: "1 1 260px", minWidth: 240 }}>
              <ImageUploadField
                label="Logo"
                value={identity.logoUrl}
                onChange={(url) => onIdentityChange("logoUrl", url)}
                uploadFn={ecommerceService.uploadStoreImage}
                slot="logo"
                height={90}
                helperText="Se muestra en el encabezado. Si lo dejás vacío, se usa la inicial del nombre."
              />
            </Box>
            <Box sx={{ flex: "1 1 220px", minWidth: 200 }}>
              <ImageUploadField
                label="Favicon"
                value={faviconUrl}
                onChange={onFaviconChange}
                uploadFn={ecommerceService.uploadStoreImage}
                slot="favicon"
                height={72}
                helperText="Ícono de la pestaña del navegador. Ideal cuadrado (512×512)."
              />
            </Box>
          </Stack>
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda
            titulo="Paletas rápidas"
            donde="Toda la tienda"
            zona="todo"
            cuando="publicar"
            accion={
              <Button
                size="small"
                variant="outlined"
                color="inherit"
                startIcon={<Icon icon="lucide:rotate-ccw" width={15} />}
                onClick={() => onApplyPreset(DEFAULT_THEME)}
                sx={{ flexShrink: 0 }}
              >
                Restablecer valores por defecto
              </Button>
            }
          >
            <Typography variant="body2" color="text.secondary">
              Aplicá una combinación predefinida y ajustá colores individuales debajo.
            </Typography>
          </SeccionTienda>
          <Stack direction="row" spacing={1} useFlexGap flexWrap="wrap">
            {COLOR_PRESETS.map((preset) => (
              <PresetSwatch key={preset.name} type="button" $active={matchesPreset(preset)} onClick={() => onApplyPreset(preset.theme)}>
                <span className="swatches">
                  <span style={{ background: preset.theme.primary }} />
                  <span style={{ background: preset.theme.secondary }} />
                  <span style={{ background: preset.theme.accent }} />
                </span>
                <small>{preset.name}</small>
              </PresetSwatch>
            ))}
          </Stack>
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Colores" donde="Toda la tienda: botones, fondos, textos y precios" zona="todo" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Estos colores se aplican en toda la tienda.
            </Typography>
          </SeccionTienda>
          <ColorGrid>
            {COLOR_FIELDS.map((field) => (
              <ColorField key={field.key}>
                <input type="color" value={theme[field.key] || "#000000"} onChange={(e) => onThemeChange(field.key, e.target.value)} />
                <div>
                  <strong>{field.label}</strong>
                  <small>{theme[field.key]}</small>
                </div>
              </ColorField>
            ))}
          </ColorGrid>
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Tipografía" donde="Todos los textos de la tienda" zona="todo" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Fuente y escala de tamaños de la tienda.
            </Typography>
          </SeccionTienda>
          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
            <TextField
              select
              size="small"
              label="Fuente"
              SelectProps={{ native: true }}
              value={typography.fontFamily || "Inter"}
              onChange={(e) => onTypographyChange("fontFamily", e.target.value)}
              sx={{ minWidth: 200 }}
            >
              {FONT_OPTIONS.map((f) => (
                <option key={f} value={f}>
                  {f}
                </option>
              ))}
            </TextField>
            <TextField
              select
              size="small"
              label="Escala"
              SelectProps={{ native: true }}
              value={typography.scale || "normal"}
              onChange={(e) => onTypographyChange("scale", e.target.value)}
              sx={{ minWidth: 160 }}
            >
              {SCALE_OPTIONS.map((s) => (
                <option key={s.value} value={s.value}>
                  {s.label}
                </option>
              ))}
            </TextField>
            <TextField
              select
              size="small"
              label="Peso tipográfico"
              SelectProps={{ native: true }}
              value={typography.fontWeight || "medio"}
              onChange={(e) => onTypographyChange("fontWeight", e.target.value)}
              sx={{ minWidth: 180 }}
              helperText="Grosor del texto en toda la tienda"
            >
              <option value="suave">Suave (ligero)</option>
              <option value="medio">Medio (recomendado)</option>
              <option value="intenso">Intenso (negrita)</option>
            </TextField>
            <FontPreview
              style={{
                fontFamily: `'${typography.fontFamily || "Inter"}', sans-serif`,
              }}
            >
              <strong>Aa</strong> {typography.fontFamily || "Inter"}
            </FontPreview>
          </Stack>
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Precio" donde="Precios en tarjetas y fichas de producto" zona="tarjetas" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Color y tamaño de fuente de los precios en toda la tienda.
            </Typography>
          </SeccionTienda>
          <Stack direction="row" spacing={2} alignItems="center" useFlexGap flexWrap="wrap">
            <Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
              <Typography variant="body2" fontWeight={600} color="text.secondary" sx={{ minWidth: 80 }}>
                Color precio
              </Typography>
              <Box sx={{ position: "relative", width: 36, height: 36 }}>
                <input
                  type="color"
                  value={theme.priceColor || theme.primary || "#0f766e"}
                  onChange={(e) => onThemeChange("priceColor", e.target.value)}
                  style={{
                    position: "absolute",
                    inset: 0,
                    width: "100%",
                    height: "100%",
                    opacity: 0,
                    cursor: "pointer",
                  }}
                />
                <Box
                  sx={{
                    width: 36,
                    height: 36,
                    borderRadius: "8px",
                    background: theme.priceColor || theme.primary || "#0f766e",
                    border: "2px solid rgba(0,0,0,0.15)",
                    boxShadow: "0 2px 6px rgba(0,0,0,0.12)",
                    pointerEvents: "none",
                  }}
                />
              </Box>
              <Typography variant="caption" color="text.secondary">
                {theme.priceColor || theme.primary || "#0f766e"}
              </Typography>
            </Box>
            <TextField
              size="small"
              type="number"
              label="Tamaño (px)"
              value={typography.priceFontSize ?? 18}
              onChange={(e) => {
                const v = Math.max(10, Math.min(40, Number(e.target.value)));
                onTypographyChange("priceFontSize", v);
              }}
              inputProps={{ min: 10, max: 40, step: 1 }}
              sx={{ width: 130 }}
              helperText="10–40 px · tarjetas"
            />
            <Box
              sx={{
                px: 2,
                py: 1,
                borderRadius: 2,
                background: "rgba(0,0,0,0.04)",
                display: "flex",
                alignItems: "baseline",
                gap: 0.5,
              }}
            >
              <Typography variant="caption" color="text.secondary">
                Vista previa:
              </Typography>
              <Typography
                component="span"
                fontWeight={900}
                sx={{
                  color: theme.priceColor || theme.primary || "#0f766e",
                  fontSize: typography.priceFontSize ?? 18,
                }}
              >
                Gs. 150.000
              </Typography>
            </Box>
          </Stack>
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={2}>
          <SeccionTienda titulo="Experiencia y movimiento" donde="Portada: banner, encabezado, fondos y tarjetas" zona="todo" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Define la presencia visual del inicio, las transiciones y el comportamiento de los elementos comerciales.
            </Typography>
          </SeccionTienda>
          <Box sx={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(210px, 1fr))", gap: 1.5 }}>
            <TextField select label="Hero principal" value={layout.heroVariant} onChange={(e) => onLayoutChange("heroVariant", e.target.value)} size="small" helperText={LAYOUT_AYUDA.heroVariant[layout.heroVariant]}>
              <MenuItem value="immersive">Fotografía inmersiva</MenuItem>
              <MenuItem value="split">Texto e imagen</MenuItem>
            </TextField>
            <TextField select label="Movimiento" value={layout.motionPreset} onChange={(e) => onLayoutChange("motionPreset", e.target.value)} size="small" helperText={LAYOUT_AYUDA.motionPreset[layout.motionPreset]}>
              <MenuItem value="dynamic">Dinámico</MenuItem>
              <MenuItem value="subtle">Sutil</MenuItem>
              <MenuItem value="none">Sin animaciones</MenuItem>
            </TextField>
            <TextField select label="Velocidad" value={layout.transitionSpeed} onChange={(e) => onLayoutChange("transitionSpeed", e.target.value)} size="small" helperText={LAYOUT_AYUDA.transitionSpeed[layout.transitionSpeed]}>
              <MenuItem value="fast">Rápida</MenuItem>
              <MenuItem value="normal">Normal</MenuItem>
              <MenuItem value="relaxed">Relajada</MenuItem>
            </TextField>
            <TextField select label="Hover de productos" value={layout.cardHoverEffect} onChange={(e) => onLayoutChange("cardHoverEffect", e.target.value)} size="small" helperText={LAYOUT_AYUDA.cardHoverEffect[layout.cardHoverEffect]}>
              <MenuItem value="lift">Elevar</MenuItem>
              <MenuItem value="zoom">Zoom de imagen</MenuItem>
              <MenuItem value="glow">Resplandor</MenuItem>
              <MenuItem value="none">Sin efecto</MenuItem>
            </TextField>
            <TextField select label="Fondos de secciones" value={layout.sectionBackgroundMode} onChange={(e) => onLayoutChange("sectionBackgroundMode", e.target.value)} size="small" helperText={LAYOUT_AYUDA.sectionBackgroundMode[layout.sectionBackgroundMode]}>
              <MenuItem value="alternating">Alternados</MenuItem>
              <MenuItem value="plain">Uniformes</MenuItem>
            </TextField>
            <TextField select label="Cabecera" value={layout.headerVariant} onChange={(e) => onLayoutChange("headerVariant", e.target.value)} size="small" helperText={LAYOUT_AYUDA.headerVariant[layout.headerVariant]}>
              <MenuItem value="compact">Compacta</MenuItem>
              <MenuItem value="standard">Estándar</MenuItem>
            </TextField>
          </Box>
          <Stack direction="row" spacing={2} useFlexGap flexWrap="wrap">
            <Tooltip title="El encabezado (logo, buscador y carrito) queda visible arriba mientras el cliente baja por la página." arrow enterDelay={300}>
              <FormControlLabel control={<Switch checked={layout.stickyHeader} onChange={(e) => onLayoutChange("stickyHeader", e.target.checked)} />} label="Cabecera fija" />
            </Tooltip>
            <Tooltip title="Las secciones de la portada se deslizan suavemente a medida que aparecen al bajar." arrow enterDelay={300}>
              <FormControlLabel control={<Switch checked={layout.showScrollAnimations} onChange={(e) => onLayoutChange("showScrollAnimations", e.target.checked)} />} label="Animar al recorrer" />
            </Tooltip>
            <Tooltip title="Barra que muestra cuánto falta para pasar a la siguiente diapositiva del banner." arrow enterDelay={300}>
              <FormControlLabel control={<Switch checked={layout.showCarouselProgress} onChange={(e) => onLayoutChange("showCarouselProgress", e.target.checked)} />} label="Progreso del carrusel" />
            </Tooltip>
          </Stack>
        </Stack>
      </Paper>

      <ContentGrid>
        <Paper variant="outlined" sx={{ p: 2 }}>
          <Stack spacing={1.5}>
            <SeccionTienda titulo="Efecto hover de banners" donde="Banner de la portada y banners de categorías" zona="hero" cuando="publicar">
              <Typography variant="body2" color="text.secondary">
                Animación al pasar el mouse por el banner principal, los banners promocionales, el mega menú y los banners de cada categoría.
              </Typography>
            </SeccionTienda>

            <EffectOptionsGrid>
              {BANNER_EFFECT_OPTIONS.map((effect) => (
                <EffectOption
                  key={effect.value}
                  type="button"
                  $active={selectedBannerEffect === effect.value}
                  onClick={() => onBannerEffectChange(effect.value)}
                >
                  <EffectPreview data-effect={effect.value}>
                    <Icon icon={effect.icon} />
                  </EffectPreview>
                  <span>{effect.label}</span>
                  <small>{effect.description}</small>
                </EffectOption>
              ))}
            </EffectOptionsGrid>
          </Stack>
        </Paper>

        <Paper variant="outlined" sx={{ p: 2 }}>
          <Stack spacing={1.5}>
            <Box>
              <Typography variant="subtitle1" fontWeight={700}>
                Vista previa
              </Typography>
              <Typography variant="body2" color="text.secondary">
                Selección actual: {selected?.label || "Sin efecto"}.
              </Typography>
            </Box>
            <BannerPreview data-effect={selectedBannerEffect}>
              <img
                src={identity.logoUrl || "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='360'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%230f766e'/%3E%3Cstop offset='1' stop-color='%231d4ed8'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='900' height='360' fill='url(%23g)'/%3E%3C/svg%3E"}
                alt="Vista previa del efecto de banner"
              />
              <div>
                <span>Promoción</span>
                <strong>Banner de temporada</strong>
              </div>
            </BannerPreview>
          </Stack>
        </Paper>
      </ContentGrid>
    </Stack>
  );
}

DesignPanel.propTypes = {
  identity: PropTypes.object.isRequired,
  faviconUrl: PropTypes.string,
  theme: PropTypes.object.isRequired,
  typography: PropTypes.object.isRequired,
  layout: PropTypes.object.isRequired,
  selectedBannerEffect: PropTypes.string.isRequired,
  onIdentityChange: PropTypes.func.isRequired,
  onFaviconChange: PropTypes.func.isRequired,
  onThemeChange: PropTypes.func.isRequired,
  onTypographyChange: PropTypes.func.isRequired,
  onLayoutChange: PropTypes.func.isRequired,
  onBannerEffectChange: PropTypes.func.isRequired,
  onApplyPreset: PropTypes.func.isRequired,
};

// Qué hace cada opción de «Experiencia y movimiento», para mostrarlo debajo del select.
const LAYOUT_AYUDA = {
  heroVariant: {
    immersive: "La foto ocupa todo el banner y el texto va encima.",
    split: "Texto a un lado y la imagen al otro, sin superponerse.",
  },
  motionPreset: {
    dynamic: "Los bloques se deslizan al aparecer y los botones reaccionan al pasar el mouse.",
    subtle: "Movimientos mínimos, apenas perceptibles.",
    none: "Todo quieto: ninguna animación ni transición.",
  },
  transitionSpeed: {
    fast: "Las animaciones duran menos: la tienda se siente más ágil.",
    normal: "Duración estándar de las animaciones.",
    relaxed: "Animaciones más lentas y suaves.",
  },
  cardHoverEffect: {
    lift: "Al pasar el mouse, la tarjeta del producto se eleva un poco.",
    zoom: "Al pasar el mouse, se agranda la foto del producto.",
    glow: "Al pasar el mouse, la tarjeta se eleva y se ilumina con el color de acento.",
    none: "Las tarjetas no cambian al pasar el mouse.",
  },
  sectionBackgroundMode: {
    alternating: "Las secciones de la portada alternan un fondo tenue para separarse.",
    plain: "Todas las secciones con el mismo fondo.",
  },
  headerVariant: {
    compact: "Encabezado más bajo: deja más lugar para los productos.",
    standard: "Encabezado más alto y aireado.",
  },
};

const CARD_SIZE_OPTIONS = [
  { value: "compact", label: "Compacta" },
  { value: "normal", label: "Normal" },
  { value: "large", label: "Grande" },
];

const DISPLAY_MODE_OPTIONS = [
  { value: "categoryCarousels", label: "Carruseles por categoría" },
  { value: "allWithPagination", label: "Grilla con paginación" },
];

const IMAGE_RATIO_OPTIONS = [
  { value: "1 / 1", label: "Cuadrada (1:1)" },
  { value: "4 / 3", label: "Horizontal (4:3)" },
  { value: "3 / 4", label: "Vertical (3:4)" },
];

const CATALOG_TOGGLES = [
  { key: "showStock", label: "Mostrar stock" },
  { key: "showBrand", label: "Mostrar marca" },
  { key: "showCategory", label: "Mostrar categoría" },
  { key: "showPromotionBadge", label: "Badge de promoción" },
];

function CategoryImagesPanel() {
  const [categorias, setCategorias] = useState([]);
  const [loading, setLoading] = useState(true);
  const [uploading, setUploading] = useState(null);
  const fileInputRef = useRef(null);
  const [activeId, setActiveId] = useState(null);

  useEffect(() => {
    ecommerceService
      .getCategorias()
      .then(setCategorias)
      .catch(() => toast.error("No se pudieron cargar las categorías"))
      .finally(() => setLoading(false));
  }, []);

  const handleUploadClick = (id) => {
    setActiveId(id);
    fileInputRef.current?.click();
  };

  const handleFileChange = async (e) => {
    const file = e.target.files?.[0];
    if (!file || !activeId) return;
    e.target.value = "";
    setUploading(activeId);
    try {
      const { imageUrl } = await ecommerceService.uploadCategoryImage(activeId, file);
      setCategorias((prev) => prev.map((c) => (c.id === activeId ? { ...c, image: imageUrl } : c)));
      toast.success("Imagen subida correctamente");
    } catch {
      toast.error("No se pudo subir la imagen");
    } finally {
      setUploading(null);
      setActiveId(null);
    }
  };

  const handleDelete = async (id) => {
    setUploading(id);
    try {
      await ecommerceService.deleteCategoryImage(id);
      setCategorias((prev) => prev.map((c) => (c.id === id ? { ...c, image: null } : c)));
      toast.success("Imagen eliminada");
    } catch {
      toast.error("No se pudo eliminar la imagen");
    } finally {
      setUploading(null);
    }
  };

  if (loading)
    return (
      <Stack alignItems="center" sx={{ py: 4 }}>
        <CircularProgress size={24} />
      </Stack>
    );

  return (
    <Paper variant="outlined" sx={{ p: 2 }}>
      <input type="file" accept="image/*" ref={fileInputRef} style={{ display: "none" }} onChange={handleFileChange} />
      <Stack spacing={1.5}>
        <SeccionTienda titulo="Imágenes de categorías" donde="Carruseles de la portada, página de cada categoría y menú" zona="carruseles" cuando="instante">
          <Typography variant="body2" color="text.secondary">
            Imagen de portada por categoría o subcategoría. Si no hay imagen propia, se usa la de un producto representativo.
          </Typography>
        </SeccionTienda>
        <CategoryImageGrid>
          {categorias.map((cat) => (
            <CategoryImageCard key={cat.id}>
              <CategoryImageThumb>
                {cat.image ? (
                  <img
                    src={cat.image}
                    alt={cat.name}
                    style={{
                      width: "100%",
                      height: "100%",
                      objectFit: "cover",
                    }}
                  />
                ) : (
                  <Icon icon="lucide:image-plus" style={{ color: "#94a3b8", width: 28, height: 28 }} />
                )}
                {uploading === cat.id && (
                  <CategoryImageOverlay>
                    <CircularProgress size={22} sx={{ color: "#fff" }} />
                  </CategoryImageOverlay>
                )}
              </CategoryImageThumb>
              <Box sx={{ minWidth: 0, flex: 1 }}>
                <Typography variant="body2" fontWeight={600} noWrap>
                  {cat.name}
                </Typography>
                <Typography variant="caption" color="text.secondary">
                  {cat.count} productos
                </Typography>
              </Box>
              <Stack direction="row" spacing={0.5} flexShrink={0}>
                <Tooltip title="Subir imagen" enterDelay={300}>
                  <span>
                    <IconButton size="small" onClick={() => handleUploadClick(cat.id)} disabled={!!uploading}>
                      <Icon icon="lucide:upload" width={15} />
                    </IconButton>
                  </span>
                </Tooltip>
                {cat.image && (
                  <Tooltip title="Quitar imagen" enterDelay={300}>
                    <span>
                      <IconButton size="small" color="error" onClick={() => handleDelete(cat.id)} disabled={!!uploading}>
                        <Icon icon="lucide:trash-2" width={15} />
                      </IconButton>
                    </span>
                  </Tooltip>
                )}
              </Stack>
            </CategoryImageCard>
          ))}
        </CategoryImageGrid>
      </Stack>
    </Paper>
  );
}

const DEFAULT_PAGES = [
  { key: "terminos", title: "Términos y condiciones" },
  { key: "privacidad", title: "Política de privacidad" },
  { key: "facturacion", title: "Facturación" },
  { key: "seguimiento", title: "Seguimiento de pedido" },
  { key: "devoluciones", title: "Cambios y devoluciones" },
  { key: "faq", title: "Preguntas frecuentes" },
  { key: "contacto", title: "Contacto" },
];

// Render del "markdown liviano" de las páginas de contenido — espejo del
// renderer de la tienda (novasis-ecommerce-store/src/lib/storeRichText.tsx):
// ## títulos, **negrita**, *itálica*, - listas y párrafos. Mantener en sincronía.
function renderRichTextPreview(content) {
  if (!content) return null;
  const inline = (text) =>
    text
      .split(/(\*\*[^*]+\*\*|\*[^*]+\*)/g)
      .filter(Boolean)
      .map((tok, i) => {
        if (tok.startsWith("**") && tok.endsWith("**") && tok.length > 4) return <strong key={i}>{tok.slice(2, -2)}</strong>;
        if (tok.startsWith("*") && tok.endsWith("*") && tok.length > 2) return <em key={i}>{tok.slice(1, -1)}</em>;
        return tok;
      });

  const out = [];
  let list = [];
  const flush = () => {
    if (!list.length) return;
    out.push(
      <ul key={`ul-${out.length}`} style={{ margin: "4px 0", paddingLeft: 20 }}>
        {list.map((item, i) => (
          <li key={i}>{inline(item)}</li>
        ))}
      </ul>,
    );
    list = [];
  };
  for (const raw of content.replace(/\r\n/g, "\n").split("\n")) {
    const line = raw.trim();
    if (!line) {
      flush();
      continue;
    }
    if (line.startsWith("- ") || line.startsWith("* ")) {
      list.push(line.slice(2));
      continue;
    }
    flush();
    if (line.startsWith("## ")) {
      out.push(
        <Typography key={`h-${out.length}`} variant="subtitle2" fontWeight={700} sx={{ mt: 1 }}>
          {inline(line.slice(3))}
        </Typography>,
      );
    } else {
      out.push(
        <Typography key={`p-${out.length}`} variant="body2" sx={{ my: 0.5 }}>
          {inline(line)}
        </Typography>,
      );
    }
  }
  flush();
  return out;
}

function PaginasPanel({ pages, onPagesChange, accent, primary }) {
  const storeAccent = accent || "#22c1e6";
  const storePrimary = primary || "#1f6feb";
  const [selectedKey, setSelectedKey] = useState(pages[0]?.key ?? null);
  const [preview, setPreview] = useState(false);
  const pageIndex = pages.findIndex((p) => p.key === selectedKey);
  const page = pageIndex >= 0 ? pages[pageIndex] : null;

  const updatePage = (updater) => {
    const next = pages.map((p, i) => (i === pageIndex ? updater(p) : p));
    onPagesChange(next);
  };

  const addPage = () => {
    const key = `pagina-${Date.now()}`;
    const newPage = {
      key,
      title: "Nueva página",
      subtitle: "",
      blocks: [{ title: "", content: "" }],
    };
    onPagesChange([...pages, newPage]);
    setSelectedKey(key);
  };

  const deletePage = () => {
    if (!page) return;
    const isDefault = DEFAULT_PAGES.some((d) => d.key === page.key);
    if (isDefault) return;
    const next = pages.filter((_, i) => i !== pageIndex);
    onPagesChange(next);
    setSelectedKey(next[0]?.key ?? null);
  };

  const addBlock = () =>
    updatePage((p) => ({
      ...p,
      blocks: [...(p.blocks ?? []), { title: "", content: "" }],
    }));

  const removeBlock = (bi) => updatePage((p) => ({ ...p, blocks: p.blocks.filter((_, i) => i !== bi) }));

  const updateBlock = (bi, field, value) =>
    updatePage((p) => ({
      ...p,
      blocks: p.blocks.map((b, i) => (i === bi ? { ...b, [field]: value } : b)),
    }));

  const isDefault = page ? DEFAULT_PAGES.some((d) => d.key === page.key) : false;

  return (
    <Stack spacing={2} direction={{ xs: "column", md: "row" }} alignItems="flex-start">
      {/* sidebar */}
      <Paper variant="outlined" sx={{ minWidth: 220, width: { xs: "100%", md: 240 }, flexShrink: 0 }}>
        <Stack
          direction="row"
          alignItems="center"
          justifyContent="space-between"
          sx={{
            px: 2,
            py: 1.5,
            borderBottom: "1px solid",
            borderColor: "divider",
          }}
        >
          <Typography variant="subtitle2" fontWeight={700}>
            Páginas
          </Typography>
          <Tooltip title="Agregar página personalizada">
            <IconButton size="small" onClick={addPage}>
              <Icon icon="lucide:plus" width={16} />
            </IconButton>
          </Tooltip>
        </Stack>
        <Stack>
          {pages.map((p) => {
            const def = DEFAULT_PAGES.find((d) => d.key === p.key);
            return (
              <Box
                key={p.key}
                onClick={() => setSelectedKey(p.key)}
                sx={{
                  px: 2,
                  py: 1.2,
                  cursor: "pointer",
                  bgcolor: p.key === selectedKey ? "action.selected" : "transparent",
                  "&:hover": { bgcolor: "action.hover" },
                  borderLeft: p.key === selectedKey ? "3px solid" : "3px solid transparent",
                  borderColor: p.key === selectedKey ? "primary.main" : "transparent",
                }}
              >
                <Typography variant="body2" fontWeight={p.key === selectedKey ? 700 : 400} noWrap>
                  {p.title || p.key}
                </Typography>
                {def && (
                  <Typography variant="caption" color="text.secondary">
                    Enlace footer: {p.key}
                  </Typography>
                )}
              </Box>
            );
          })}
        </Stack>
      </Paper>

      {/* editor */}
      {page ? (
        <Stack spacing={2} flex={1} sx={{ width: "100%" }}>
          <Paper variant="outlined" sx={{ p: 2 }}>
            <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 2 }}>
              <Typography variant="subtitle1" fontWeight={700}>
                <Icon icon="lucide:file-text" style={{ marginRight: 6, verticalAlign: "middle" }} />
                {isDefault ? "Página del sistema" : "Página personalizada"}
              </Typography>
              {!isDefault && (
                <Tooltip title="Eliminar página">
                  <IconButton size="small" color="error" onClick={deletePage}>
                    <Icon icon="lucide:trash-2" width={16} />
                  </IconButton>
                </Tooltip>
              )}
            </Stack>
            <Stack spacing={2}>
              <TextField
                label="Título"
                size="small"
                fullWidth
                value={page.title ?? ""}
                onChange={(e) => updatePage((p) => ({ ...p, title: e.target.value }))}
              />
              <TextField
                label="Subtítulo (opcional)"
                size="small"
                fullWidth
                value={page.subtitle ?? ""}
                onChange={(e) => updatePage((p) => ({ ...p, subtitle: e.target.value }))}
                helperText="Frase breve que aparece debajo del título de la página"
              />
              {isDefault && (
                <Typography variant="caption" color="text.secondary">
                  Esta página aparece en el footer de la tienda (enlace: <strong>{page.key}</strong>). Podés editar su contenido libremente.
                </Typography>
              )}
            </Stack>
          </Paper>

          {/* bloques de contenido */}
          <Paper variant="outlined" sx={{ p: 2 }}>
            <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 2 }}>
              <Typography variant="subtitle2" fontWeight={700}>
                Bloques de contenido
              </Typography>
              <Stack direction="row" alignItems="center" spacing={1}>
                <FormControlLabel
                  control={<Switch size="small" checked={preview} onChange={(e) => setPreview(e.target.checked)} />}
                  label={<Typography variant="caption">Vista previa</Typography>}
                  sx={{ m: 0 }}
                />
                <Button size="small" startIcon={<Icon icon="lucide:plus" width={14} />} onClick={addBlock}>
                  Agregar bloque
                </Button>
              </Stack>
            </Stack>

            {/* Vista previa FIEL de la página: reproduce el hero (badge + título +
                subtítulo) y cada bloque como tarjeta con la línea de acento real
                del tenant, tal como los renderiza la tienda. */}
            {preview && (
              <Box sx={{ mb: 2, borderRadius: 2, bgcolor: "#eef1f4", p: { xs: 1.5, sm: 2.5 }, display: "grid", gap: 1.75 }}>
                <Typography variant="caption" color="text.secondary" fontWeight={700}>
                  Así se ve la página en la tienda
                </Typography>
                <Box
                  sx={{
                    borderRadius: "18px",
                    bgcolor: "#fff",
                    border: "1px solid rgba(20,33,31,0.08)",
                    boxShadow: "0 18px 48px rgba(15,23,42,0.08)",
                    p: { xs: 2.5, sm: "34px 44px" },
                    display: "grid",
                    justifyItems: "start",
                    gap: 1.25,
                  }}
                >
                  <Box sx={{ bgcolor: storePrimary, color: "#fff", fontWeight: 700, fontSize: 12, borderRadius: 999, px: 1.25, py: 0.4, lineHeight: 1.6 }}>
                    Información
                  </Box>
                  <Box component="h1" sx={{ m: 0, color: "#14211f", fontWeight: 950, fontSize: { xs: 28, sm: 40 }, lineHeight: 1.05 }}>
                    {page.title || "Título de la página"}
                  </Box>
                  {page.subtitle && (
                    <Box component="p" sx={{ m: 0, color: "rgba(20,33,31,0.6)", fontSize: 16, lineHeight: 1.55 }}>
                      {page.subtitle}
                    </Box>
                  )}
                </Box>
                {(page.blocks ?? [])
                  .filter((b) => (b.content ?? "").trim() || (b.title ?? "").trim())
                  .map((block, bi) => (
                    <Box
                      key={bi}
                      sx={{
                        borderRadius: 2,
                        bgcolor: "#fff",
                        border: "1px solid rgba(20,33,31,0.08)",
                        boxShadow: "0 8px 24px rgba(15,23,42,0.06)",
                        px: { xs: 2, sm: 2.5 },
                        py: 2,
                        display: "grid",
                        gap: 1.25,
                      }}
                    >
                      {block.title && (
                        <Box
                          component="h2"
                          sx={{ m: 0, pb: "10px", borderBottom: `3px solid ${storeAccent}`, color: "#14211f", fontSize: 20, fontWeight: 950 }}
                        >
                          {block.title}
                        </Box>
                      )}
                      <Box sx={{ color: "#14211f", "& .MuiTypography-root": { color: "#14211f", fontSize: 15, lineHeight: 1.7 } }}>
                        {renderRichTextPreview(block.content)}
                      </Box>
                    </Box>
                  ))}
              </Box>
            )}

            <Stack spacing={2}>
              {(page.blocks ?? []).map((block, bi) => (
                <Paper key={bi} variant="outlined" sx={{ p: 1.5 }}>
                  <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 1 }}>
                    <Typography variant="caption" color="text.secondary" fontWeight={600}>
                      Bloque {bi + 1}
                    </Typography>
                    <IconButton size="small" color="error" onClick={() => removeBlock(bi)} disabled={(page.blocks ?? []).length <= 1}>
                      <Icon icon="lucide:x" width={14} />
                    </IconButton>
                  </Stack>
                  <Stack spacing={1.5}>
                    <TextField
                      label="Título del bloque (opcional)"
                      size="small"
                      fullWidth
                      value={block.title ?? ""}
                      onChange={(e) => updateBlock(bi, "title", e.target.value)}
                    />
                    <TextField
                      label="Contenido"
                      size="small"
                      fullWidth
                      multiline
                      minRows={4}
                      value={block.content ?? ""}
                      onChange={(e) => updateBlock(bi, "content", e.target.value)}
                      helperText="Formato: ## Título · **negrita** · *itálica* · - lista · línea en blanco = nuevo párrafo"
                    />
                  </Stack>
                </Paper>
              ))}
              {(page.blocks ?? []).length === 0 && (
                <Typography variant="body2" color="text.secondary" sx={{ textAlign: "center", py: 2 }}>
                  Sin bloques. Agregá uno para mostrar contenido.
                </Typography>
              )}
            </Stack>
          </Paper>
        </Stack>
      ) : (
        <Box
          flex={1}
          sx={{
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
            py: 8,
          }}
        >
          <Typography color="text.secondary">Seleccioná una página para editarla</Typography>
        </Box>
      )}
    </Stack>
  );
}

/**
 * Qué categorías salen en la portada y en qué orden. Antes sólo se elegía la
 * cantidad y la tienda tomaba las primeras por orden alfabético (Abdominal, Aire
 * acondicionado, Alacenas, Alambre…). Vacío = las más surtidas.
 */
function HomeCategoriesPicker({ value, max, onChange }) {
  const [opciones, setOpciones] = useState([]);
  useEffect(() => {
    let vivo = true;
    ecommerceService
      .getCategorias()
      .then((cats) => vivo && setOpciones((cats || []).map((c) => c.name).filter(Boolean)))
      .catch(() => undefined);
    return () => {
      vivo = false;
    };
  }, []);
  const mover = (i, d) => {
    const next = [...value];
    [next[i], next[i + d]] = [next[i + d], next[i]];
    onChange(next);
  };
  return (
    <Paper variant="outlined" sx={{ p: 2 }}>
      <Stack spacing={1.5}>
        <SeccionTienda titulo="Categorías de la portada" donde="Carruseles de la portada" zona="carruseles" cuando="publicar">
          <Typography variant="body2" color="text.secondary">
            Elegí y ordená las categorías que se muestran en la portada. Si no elegís ninguna, salen las {max} con más
            productos. Se muestran hasta {max} (lo define «Categorías en portada»).
          </Typography>
        </SeccionTienda>
        <Autocomplete
          // Se remonta al agregar para que el campo quede vacío y listo para la siguiente.
          key={value.length}
          id="home-categories-add"
          size="small"
          options={opciones.filter((o) => !value.includes(o))}
          value={null}
          onChange={(_, v) => v && onChange([...value, v])}
          renderInput={(params) => <TextField {...params} label="Agregar categoría" />}
          sx={{ maxWidth: 420 }}
        />
        {value.length > 0 && (
          <Stack spacing={0.75}>
            {value.map((nombre, i) => (
              <Stack key={nombre} direction="row" alignItems="center" spacing={1} sx={{ opacity: i < max ? 1 : 0.5 }}>
                <Typography variant="body2" sx={{ width: 24, color: "text.secondary", fontVariantNumeric: "tabular-nums" }}>
                  {i + 1}.
                </Typography>
                <Typography variant="body2" sx={{ flex: 1 }}>
                  {nombre}
                  {i >= max ? " (no entra: subí «Categorías en portada»)" : ""}
                </Typography>
                <Tooltip title="Subir">
                  <span>
                    <IconButton size="small" disabled={i === 0} onClick={() => mover(i, -1)} aria-label={`Subir ${nombre}`}>
                      <Icon icon="lucide:chevron-up" width={16} />
                    </IconButton>
                  </span>
                </Tooltip>
                <Tooltip title="Bajar">
                  <span>
                    <IconButton size="small" disabled={i === value.length - 1} onClick={() => mover(i, 1)} aria-label={`Bajar ${nombre}`}>
                      <Icon icon="lucide:chevron-down" width={16} />
                    </IconButton>
                  </span>
                </Tooltip>
                <Tooltip title="Quitar">
                  <IconButton size="small" onClick={() => onChange(value.filter((x) => x !== nombre))} aria-label={`Quitar ${nombre}`}>
                    <Icon icon="lucide:x" width={16} />
                  </IconButton>
                </Tooltip>
              </Stack>
            ))}
          </Stack>
        )}
      </Stack>
    </Paper>
  );
}

HomeCategoriesPicker.propTypes = {
  value: PropTypes.arrayOf(PropTypes.string).isRequired,
  max: PropTypes.number.isRequired,
  onChange: PropTypes.func.isRequired,
};

function CatalogPanel({ catalog, onCatalogChange }) {
  return (
    <Stack spacing={2}>
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Box>
          <Typography variant="subtitle1" fontWeight={700}>
            Catálogo público
          </Typography>
          <Typography variant="body2" color="text.secondary">
            Productos, categorías, marcas e imágenes se reutilizan de tu catálogo. Acá configurás cómo se muestran en la tienda.
          </Typography>
        </Box>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={2}>
          <SeccionTienda titulo="Presentación de la grilla" donde="Portada y listados de productos" zona="carruseles" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Cómo se arman los listados: carruseles por categoría o grilla con paginación, tamaño de las tarjetas, columnas y proporción de las fotos.
            </Typography>
          </SeccionTienda>
          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
            <TextField
              select
              size="small"
              label="Modo de visualización"
              SelectProps={{ native: true }}
              value={catalog.displayMode}
              onChange={(e) => onCatalogChange("displayMode", e.target.value)}
              helperText={
                catalog.displayMode === "allWithPagination"
                  ? "La portada lista todos los productos en una grilla con filtros y páginas."
                  : "La portada muestra un carrusel por categoría; al entrar a una, grilla con filtros."
              }
              sx={{ minWidth: 240, maxWidth: 320 }}
            >
              {DISPLAY_MODE_OPTIONS.map((o) => (
                <option key={o.value} value={o.value}>
                  {o.label}
                </option>
              ))}
            </TextField>
            <TextField
              select
              size="small"
              label="Tamaño de card"
              SelectProps={{ native: true }}
              value={catalog.cardSize}
              onChange={(e) => onCatalogChange("cardSize", e.target.value)}
              helperText="Alto de las tarjetas de producto: más chicas, entran más por pantalla."
              sx={{ minWidth: 180, maxWidth: 260 }}
            >
              {CARD_SIZE_OPTIONS.map((o) => (
                <option key={o.value} value={o.value}>
                  {o.label}
                </option>
              ))}
            </TextField>
            <TextField
              select
              size="small"
              label="Relación de imagen"
              SelectProps={{ native: true }}
              value={catalog.imageRatio}
              onChange={(e) => onCatalogChange("imageRatio", e.target.value)}
              helperText="Forma de la foto en la tarjeta. Elegí la de tus fotos para que no se recorten."
              sx={{ minWidth: 200, maxWidth: 280 }}
            >
              {IMAGE_RATIO_OPTIONS.map((o) => (
                <option key={o.value} value={o.value}>
                  {o.label}
                </option>
              ))}
            </TextField>
          </Stack>

          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
            <TextField
              type="number"
              size="small"
              label="Columnas escritorio"
              value={catalog.columnsDesktop}
              onChange={(e) => onCatalogChange("columnsDesktop", Number(e.target.value))}
              inputProps={{ min: 1, max: 6 }}
              helperText="Tarjetas por fila"
              sx={{ width: 170 }}
            />
            <TextField
              type="number"
              size="small"
              label="Columnas tablet"
              value={catalog.columnsTablet}
              onChange={(e) => onCatalogChange("columnsTablet", Number(e.target.value))}
              inputProps={{ min: 1, max: 5 }}
              helperText="Tarjetas por fila"
              sx={{ width: 160 }}
            />
            <TextField
              type="number"
              size="small"
              label="Columnas móvil"
              value={catalog.columnsMobile}
              onChange={(e) => onCatalogChange("columnsMobile", Number(e.target.value))}
              inputProps={{ min: 1, max: 3 }}
              helperText="Tarjetas por fila"
              sx={{ width: 150 }}
            />
            <TextField
              type="number"
              size="small"
              label="Categorías en portada"
              value={catalog.maxCategoriesOnHome}
              onChange={(e) => onCatalogChange("maxCategoriesOnHome", Number(e.target.value))}
              inputProps={{ min: 1, max: 20 }}
              sx={{ width: 190 }}
              helperText={catalog.displayMode === "allWithPagination" ? "Sólo en modo carruseles" : "Carruseles de la portada"}
            />
            <TextField
              type="number"
              size="small"
              label="Productos por página"
              value={catalog.pageSize}
              onChange={(e) => onCatalogChange("pageSize", Number(e.target.value))}
              inputProps={{ min: 4, max: 60 }}
              sx={{ width: 180 }}
              helperText="Grilla y página de cada categoría"
            />
            <TextField
              type="number"
              size="small"
              label="Máx. por categoría"
              value={catalog.maxProductsPerCategory}
              onChange={(e) => onCatalogChange("maxProductsPerCategory", Number(e.target.value))}
              inputProps={{ min: 4, max: 30 }}
              sx={{ width: 180 }}
              helperText={catalog.displayMode === "allWithPagination" ? "Sólo en modo carruseles" : "Productos en cada carrusel"}
            />
          </Stack>
        </Stack>
      </Paper>

      <HomeCategoriesPicker
        value={catalog.homeCategories ?? []}
        max={Math.max(1, Number(catalog.maxCategoriesOnHome) || 6)}
        onChange={(v) => onCatalogChange("homeCategories", v)}
      />

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Datos visibles en la card" donde="Tarjetas de producto en la portada y en los listados" zona="tarjetas" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Qué datos acompañan a cada producto en su tarjeta, además del nombre, la foto y el precio.
            </Typography>
          </SeccionTienda>
          <Stack direction="row" spacing={1} useFlexGap flexWrap="wrap">
            {CATALOG_TOGGLES.map((t) => (
              <Chip
                key={t.key}
                label={t.label}
                color={catalog[t.key] ? "primary" : "default"}
                variant={catalog[t.key] ? "filled" : "outlined"}
                onClick={() => onCatalogChange(t.key, !catalog[t.key])}
                icon={<Icon icon={catalog[t.key] ? "lucide:check" : "lucide:x"} width={14} />}
              />
            ))}
          </Stack>
        </Stack>
      </Paper>

      <CategoryImagesPanel />
    </Stack>
  );
}

CatalogPanel.propTypes = {
  catalog: PropTypes.object.isRequired,
  onCatalogChange: PropTypes.func.isRequired,
};

// Textos de secciones de la tienda (copy editable de marketing, no microcopy de UI).
function StoreTextsPanel({ texts, onTextsChange }) {
  const t = texts ?? {};
  return (
    <Paper variant="outlined" sx={{ p: 2 }}>
      <Stack spacing={2}>
        <SeccionTienda titulo="Textos de secciones" donde="Banner de categoría, panel lateral, novedades y pie" zona="todo" cuando="publicar">
          <Typography variant="body2" color="text.secondary">
            Textos de las secciones del catálogo y el pie de página. Se muestran tal cual en la tienda.
          </Typography>
        </SeccionTienda>

        <Divider textAlign="left">
          <Typography variant="caption" color="text.secondary">
            Banner de categoría
          </Typography>
        </Divider>
        <Typography variant="caption" color="text.secondary">
          Aparece arriba del listado al entrar a una categoría o subcategoría con imagen. El sufijo del título sólo se agrega en categorías de una sola palabra.
        </Typography>
        <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
          <TextField
            size="small"
            label="Sufijo del título (categoría de una palabra)"
            placeholder="Ej: store"
            value={t.categoryBannerTitleSuffix || ""}
            onChange={(e) => onTextsChange("categoryBannerTitleSuffix", e.target.value)}
            helperText='Opcional. Ej: «Bebidas store». Vacío: sólo el nombre.'
            sx={{ minWidth: 260, flex: 1 }}
          />
        </Stack>
        <TextField
          size="small"
          fullWidth
          multiline
          minRows={2}
          label="Descripción del banner de categoría"
          value={t.categoryBannerDescription || ""}
          onChange={(e) => onTextsChange("categoryBannerDescription", e.target.value)}
          helperText="Usá {categoria} donde quieras insertar el nombre de la categoría."
        />

        <Divider textAlign="left">
          <Typography variant="caption" color="text.secondary">
            Panel lateral B2B
          </Typography>
        </Divider>
        <Typography variant="caption" color="text.secondary">
          Aparece en la barra lateral de filtros cuando el cliente entra a una categoría o hace una búsqueda (no en la portada).
        </Typography>
        <TextField
          size="small"
          fullWidth
          label="Título del panel B2B"
          value={t.b2bPanelTitle || ""}
          onChange={(e) => onTextsChange("b2bPanelTitle", e.target.value)}
        />
        <TextField
          size="small"
          fullWidth
          multiline
          minRows={2}
          label="Subtítulo del panel B2B"
          value={t.b2bPanelSubtitle || ""}
          onChange={(e) => onTextsChange("b2bPanelSubtitle", e.target.value)}
        />

        <Divider textAlign="left">
          <Typography variant="caption" color="text.secondary">
            Bloque de Novedades
          </Typography>
        </Divider>
        <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
          <TextField
            size="small"
            label="Título de Novedades"
            value={t.novedadesTitle || ""}
            onChange={(e) => onTextsChange("novedadesTitle", e.target.value)}
            sx={{ minWidth: 220, flex: 1 }}
          />
          <TextField
            size="small"
            label="Subtítulo de Novedades"
            value={t.novedadesSubtitle || ""}
            onChange={(e) => onTextsChange("novedadesSubtitle", e.target.value)}
            sx={{ minWidth: 260, flex: 2 }}
          />
        </Stack>

        <Divider textAlign="left">
          <Typography variant="caption" color="text.secondary">
            Otros
          </Typography>
        </Divider>
        <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
          <TextField
            size="small"
            label='Etiqueta "más vistos"'
            value={t.mostViewedLabel || ""}
            onChange={(e) => onTextsChange("mostViewedLabel", e.target.value)}
            helperText="Lista destacada por categoría"
            sx={{ minWidth: 220, flex: 1 }}
          />
          <TextField
            size="small"
            label="Atribución del pie de página"
            value={t.poweredByLabel || ""}
            onChange={(e) => onTextsChange("poweredByLabel", e.target.value)}
            helperText="Texto al pie de la tienda"
            sx={{ minWidth: 260, flex: 1 }}
          />
        </Stack>
      </Stack>
    </Paper>
  );
}

StoreTextsPanel.propTypes = {
  texts: PropTypes.object,
  onTextsChange: PropTypes.func.isRequired,
};

const DECORATION_OPTIONS = [
  { value: "none", label: "Sin decoración" },
  { value: "snow", label: "Nieve (invierno)" },
  { value: "spring", label: "Primavera" },
  { value: "summer", label: "Verano" },
  { value: "autumn", label: "Otoño" },
];

const SEASON_OVERRIDE_COLORS = [
  { key: "primary", label: "Primario" },
  { key: "accent", label: "Acento" },
  { key: "background", label: "Fondo" },
  { key: "text", label: "Texto" },
];

function todayISO() {
  return hoyISO();
}

function isSeasonActive(season) {
  if (!season.enabled) return false;
  const t = todayISO();
  return (season.startDate || "") <= t && t <= (season.endDate || "");
}

function SeasonsPanel({ seasons, onSeasonsChange }) {
  const cardRefs = useRef({});
  const pendingScrollId = useRef(null);
  const [expandedId, setExpandedId] = useState(() => {
    const active = seasons.find((s) => isSeasonActive(s));
    return active?.id ?? (seasons.length === 1 ? seasons[0]?.id : null) ?? null;
  });

  useEffect(() => {
    if (!pendingScrollId.current) return;
    const id = pendingScrollId.current;
    const el = cardRefs.current[id];
    if (el) {
      el.scrollIntoView({ behavior: "smooth", block: "start" });
      pendingScrollId.current = null;
    }
  }, [seasons]);

  const update = (idx, patch) => {
    onSeasonsChange(seasons.map((s, i) => (i === idx ? { ...s, ...patch } : s)));
  };
  const updateTheme = (idx, key, value) => {
    const s = seasons[idx];
    update(idx, {
      themeOverrides: { ...(s.themeOverrides || {}), [key]: value },
    });
  };
  const updatePromo = (idx, key, value) => {
    const s = seasons[idx];
    update(idx, { promotions: { ...(s.promotions || {}), [key]: value } });
  };
  const remove = (idx) => {
    const removedId = seasons[idx]?.id ?? idx;
    onSeasonsChange(seasons.filter((_, i) => i !== idx));
    setExpandedId((prev) => (prev === removedId ? null : prev));
  };
  const add = () => {
    const newId = `temp-${Date.now()}`;
    onSeasonsChange([
      ...seasons,
      {
        id: newId,
        name: "Nueva temporada",
        enabled: false,
        startDate: todayISO(),
        endDate: todayISO(),
        priority: 10,
        themeOverrides: {},
        promotions: {},
        decoration: { type: "none", intensity: "low" },
      },
    ]);
    setExpandedId(newId);
    pendingScrollId.current = newId;
  };

  return (
    <Stack spacing={2}>
      <Paper variant="outlined" sx={{ p: 2 }}>
        <SeccionTienda
          titulo="Temporadas"
          donde="Barra superior, banner y colores mientras dure cada fecha"
          zona="hero"
          cuando="publicar"
          accion={
            <Button size="small" variant="outlined" startIcon={<Icon icon="lucide:plus" />} onClick={add}>
              Agregar temporada
            </Button>
          }
        >
          <Typography variant="body2" color="text.secondary">
            Cada temporada tiene fechas de inicio y fin. Mientras está vigente, la tienda cambia sola los colores, la decoración, la
            barra superior y el banner principal (el bloque grande de la portada); al terminar vuelve a lo de siempre. Se publican
            una vez y se activan solas por fecha.
          </Typography>
        </SeccionTienda>
      </Paper>

      {seasons.length === 0 && (
        <Paper variant="outlined" sx={{ p: 3, textAlign: "center" }}>
          <Typography variant="body2" color="text.secondary">
            No hay temporadas configuradas. Agregá una para personalizar la tienda por fechas.
          </Typography>
        </Paper>
      )}

      {seasons.map((season, idx) => {
        const id = season.id || idx;
        return (
        <Accordion
          key={id}
          ref={(el) => {
            if (el) cardRefs.current[id] = el;
          }}
          variant="outlined"
          disableGutters
          expanded={expandedId === id}
          onChange={(_, isExpanded) => setExpandedId(isExpanded ? id : null)}
        >
          <AccordionSummary expandIcon={<Icon icon="lucide:chevron-down" />}>
            <Stack direction="row" alignItems="center" spacing={1} useFlexGap flexWrap="wrap" sx={{ width: "100%", pr: 1 }}>
              <Typography fontWeight={600}>{season.name || "Sin nombre"}</Typography>
              <Chip
                size="small"
                label={season.enabled ? "Activa" : "Inactiva"}
                color={season.enabled ? "success" : "default"}
                variant={season.enabled ? "filled" : "outlined"}
                onClick={(e) => {
                  e.stopPropagation();
                  update(idx, { enabled: !season.enabled });
                }}
                icon={<Icon icon={season.enabled ? "lucide:eye" : "lucide:eye-off"} width={14} />}
              />
              {isSeasonActive(season) && <Chip size="small" color="primary" label="Vigente hoy" />}
              <Box sx={{ flex: 1 }} />
              <Typography variant="caption" color="text.secondary">
                {season.startDate ? season.startDate.split("-").reverse().join("/") : "?"} — {season.endDate ? season.endDate.split("-").reverse().join("/") : "?"}
              </Typography>
            </Stack>
          </AccordionSummary>
          <AccordionDetails>
          <Stack spacing={1.5}>
            <Stack direction="row" justifyContent="flex-end">
              <Button size="small" color="error" startIcon={<Icon icon="lucide:trash-2" width={14} />} onClick={() => remove(idx)}>
                Quitar
              </Button>
            </Stack>

            <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
              <TextField
                size="small"
                label="Nombre"
                value={season.name || ""}
                onChange={(e) => update(idx, { name: e.target.value })}
                sx={{ minWidth: 200, flex: 1 }}
              />
              <TextField
                size="small"
                type="date"
                label="Desde"
                InputLabelProps={{ shrink: true }}
                value={season.startDate || ""}
                onChange={(e) => update(idx, { startDate: e.target.value })}
                sx={{ width: 170 }}
              />
              <TextField
                size="small"
                type="date"
                label="Hasta"
                InputLabelProps={{ shrink: true }}
                value={season.endDate || ""}
                onChange={(e) => update(idx, { endDate: e.target.value })}
                sx={{ width: 170 }}
              />
              <TextField
                size="small"
                type="number"
                label="Prioridad"
                value={season.priority ?? 10}
                onChange={(e) => update(idx, { priority: Number(e.target.value) })}
                sx={{ width: 120 }}
              />
              <TextField
                select
                size="small"
                label="Decoración"
                SelectProps={{ native: true }}
                value={season.decoration?.type || "none"}
                onChange={(e) =>
                  update(idx, {
                    decoration: {
                      ...(season.decoration || {}),
                      type: e.target.value,
                      intensity: season.decoration?.intensity || "low",
                    },
                  })
                }
                sx={{ minWidth: 180 }}
              >
                {DECORATION_OPTIONS.map((o) => (
                  <option key={o.value} value={o.value}>
                    {o.label}
                  </option>
                ))}
              </TextField>
            </Stack>

            <Box>
              <Typography variant="caption" color="text.secondary" fontWeight={700}>
                COLORES DE TEMPORADA
              </Typography>
              <Stack direction="row" spacing={1} useFlexGap flexWrap="wrap" sx={{ mt: 0.5 }}>
                {SEASON_OVERRIDE_COLORS.map((c) => (
                  <ColorField key={c.key}>
                    <input type="color" value={season.themeOverrides?.[c.key] || "#ffffff"} onChange={(e) => updateTheme(idx, c.key, e.target.value)} />
                    <div>
                      <strong>{c.label}</strong>
                      <small>{season.themeOverrides?.[c.key] || "—"}</small>
                    </div>
                  </ColorField>
                ))}
              </Stack>
            </Box>

            <Box>
              <Typography variant="caption" color="text.secondary" fontWeight={700}>
                BANNER PRINCIPAL (HERO) DE LA TEMPORADA
              </Typography>
              <Typography variant="caption" color="text.secondary" display="block" sx={{ mt: 0.25, mb: 1 }}>
                Reemplaza el banner grande de arriba de la tienda mientras la temporada esté vigente.
              </Typography>
              <Stack spacing={1.5}>
                <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
                  <TextField
                    size="small"
                    label="Mensaje barra superior"
                    placeholder="Ej: Envío gratis en compras de temporada"
                    value={season.promotions?.topBar || ""}
                    onChange={(e) => updatePromo(idx, "topBar", e.target.value)}
                    sx={{ minWidth: 280, flex: 1 }}
                  />
                  <TextField
                    size="small"
                    label="Título del hero"
                    placeholder="Ej: Verano fresco en tu carrito"
                    value={season.promotions?.heroTitle || ""}
                    onChange={(e) => updatePromo(idx, "heroTitle", e.target.value)}
                    sx={{ minWidth: 240, flex: 1 }}
                  />
                </Stack>
                <TextField
                  size="small"
                  fullWidth
                  label="Subtítulo del hero"
                  placeholder="Ej: Promociones en bebidas, frutas y productos livianos para todos los días."
                  value={season.promotions?.heroSubtitle || ""}
                  onChange={(e) => updatePromo(idx, "heroSubtitle", e.target.value)}
                  multiline
                  minRows={2}
                />
                <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
                  <TextField
                    size="small"
                    label="Badge"
                    placeholder="Ej: Temporada verano"
                    value={season.promotions?.heroBadge || ""}
                    onChange={(e) => updatePromo(idx, "heroBadge", e.target.value)}
                    sx={{ minWidth: 160, flex: 1 }}
                  />
                  <TextField
                    size="small"
                    label="Texto de oferta"
                    placeholder="Ej: 2x1 verano"
                    value={season.promotions?.heroDealTitle || ""}
                    onChange={(e) => updatePromo(idx, "heroDealTitle", e.target.value)}
                    sx={{ minWidth: 160, flex: 1 }}
                  />
                  <TextField
                    size="small"
                    label="Subtexto de oferta"
                    placeholder="Ej: en bebidas seleccionadas"
                    value={season.promotions?.heroDealSubtitle || ""}
                    onChange={(e) => updatePromo(idx, "heroDealSubtitle", e.target.value)}
                    sx={{ minWidth: 160, flex: 1 }}
                  />
                </Stack>
                <ImageUploadField
                  label="Imagen del hero de la temporada"
                  value={season.promotions?.heroImage}
                  onChange={(url) => updatePromo(idx, "heroImage", url)}
                  uploadFn={ecommerceService.uploadStoreImage}
                  slot={`temporada-${season.id || idx}`}
                  height={140}
                  helperText="Reemplaza la imagen del banner principal mientras la temporada esté vigente."
                />
              </Stack>
            </Box>
          </Stack>
          </AccordionDetails>
        </Accordion>
        );
      })}
    </Stack>
  );
}

SeasonsPanel.propTypes = {
  seasons: PropTypes.array.isRequired,
  onSeasonsChange: PropTypes.func.isRequired,
};

function PromotionsContentPanel({ promotions, onPromotionChange, seasons }) {
  const p = promotions || {};
  // Igual que con los colores, la temporada activa sobrescribe el hero/promo
  // base en la tienda (activePromotions = { ...base, ...season.promotions }).
  const activeSeason = findActiveSeason(seasons);
  const [categorias, setCategorias] = useState([]);
  useEffect(() => {
    let active = true;
    ecommerceService
      .getCategorias()
      .then((cats) => active && setCategorias(Array.isArray(cats) ? cats : []))
      .catch(() => undefined);
    return () => {
      active = false;
    };
  }, []);
  const targetCategoryValue = p.popupTargetCategory && p.popupTargetCategory !== "Todos" ? p.popupTargetCategory : "";

  // Diapositivas propias del carrusel del hero (viven en promotions.heroSlides).
  const heroSlides = Array.isArray(p.heroSlides) ? p.heroSlides : [];
  const setHeroSlides = (next) => onPromotionChange("heroSlides", next);
  const addHeroSlide = () =>
    setHeroSlides([
      ...heroSlides,
      { id: `slide-${Date.now()}`, badge: "", title: "", description: "", ctaLabel: "Ver colección", category: "", image: "", enabled: true },
    ]);
  const updateHeroSlide = (idx, key, value) => setHeroSlides(heroSlides.map((s, i) => (i === idx ? { ...s, [key]: value } : s)));
  const removeHeroSlide = (idx) => setHeroSlides(heroSlides.filter((_, i) => i !== idx));
  const moveHeroSlide = (idx, dir) => {
    const j = idx + dir;
    if (j < 0 || j >= heroSlides.length) return;
    const next = [...heroSlides];
    [next[idx], next[j]] = [next[j], next[idx]];
    setHeroSlides(next);
  };

  return (
    <Stack spacing={2}>
      {activeSeason && (
        <Alert severity="warning" icon={<Icon icon="lucide:calendar-clock" width={18} />}>
          La temporada <strong>{activeSeason.name || "activa"}</strong> está vigente
          {activeSeason.endDate ? ` hasta el ${activeSeason.endDate.split("-").reverse().join("/")}` : ""} y
          <strong> reemplaza este banner</strong> por el de la temporada en la tienda. Para cambiar el banner que se ve ahora,
          editalo en <strong>Diseño → Temporadas → {activeSeason.name || "la temporada activa"}</strong>. Lo de acá se muestra
          cuando no hay ninguna temporada activa.
        </Alert>
      )}
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Box>
          <Typography variant="subtitle1" fontWeight={700}>
            Contenido promocional de la tienda
          </Typography>
          <Typography variant="body2" color="text.secondary">
            Lo primero que ve el cliente al entrar: barra superior, banner principal o diapositivas y la ventana de bienvenida.
            Más abajo, las ofertas de productos.
          </Typography>
        </Box>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Barra superior" donde="Arriba de todo, en todas las páginas" zona="topbar" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              La franja fina que se ve arriba de todo. Ideal para envío gratis, horarios o una promo corta.
            </Typography>
          </SeccionTienda>
          <TextField
            size="small"
            fullWidth
            label="Mensaje de la barra superior"
            placeholder="Ej: Envío gratis desde Gs. 250.000"
            value={p.topBar || ""}
            onChange={(e) => onPromotionChange("topBar", e.target.value)}
          />
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Banner principal (hero)" donde="Primer bloque de la portada" zona="hero" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              El bloque grande que se ve al entrar. Si no cargás diapositivas propias, el carrusel arranca con este banner y sigue con dos categorías destacadas.
            </Typography>
          </SeccionTienda>
          {heroSlides.some((s) => s && s.enabled !== false && (s.title || s.image)) && (
            <Alert severity="warning" sx={{ py: 0.5 }}>
              Tenés diapositivas propias visibles: la tienda muestra sólo esas y este banner no aparece. Para usarlo, ocultá o
              eliminá las diapositivas de abajo.
            </Alert>
          )}
          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
            <TextField
              size="small"
              label="Título"
              value={p.heroTitle || ""}
              onChange={(e) => onPromotionChange("heroTitle", e.target.value)}
              sx={{ minWidth: 260, flex: 1 }}
            />
            <TextField
              size="small"
              label="Badge"
              placeholder="Ej: Envío gratis"
              helperText="Etiqueta chica arriba del título"
              value={p.heroBadge || ""}
              onChange={(e) => onPromotionChange("heroBadge", e.target.value)}
              sx={{ minWidth: 180 }}
            />
          </Stack>
          <TextField
            size="small"
            fullWidth
            label="Subtítulo"
            value={p.heroSubtitle || ""}
            onChange={(e) => onPromotionChange("heroSubtitle", e.target.value)}
            multiline
            minRows={2}
          />
          <ImageUploadField
            label="Imagen del banner (hero)"
            value={p.heroImage}
            onChange={(url) => onPromotionChange("heroImage", url)}
            uploadFn={ecommerceService.uploadStoreImage}
            slot="hero"
            height={160}
            helperText="Se muestra grande arriba de la tienda. Ideal apaisada (ancha)."
          />
          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
            <TextField
              size="small"
              label="Texto de oferta (grande)"
              placeholder="Ej: Hasta 25%"
              helperText="Grande y destacado, debajo del subtítulo"
              value={p.heroDealTitle || ""}
              onChange={(e) => onPromotionChange("heroDealTitle", e.target.value)}
              sx={{ minWidth: 200, flex: 1 }}
            />
            <TextField
              size="small"
              label="Subtexto de oferta"
              placeholder="Ej: en seleccionados"
              helperText="Al lado del texto de oferta, más chico"
              value={p.heroDealSubtitle || ""}
              onChange={(e) => onPromotionChange("heroDealSubtitle", e.target.value)}
              sx={{ minWidth: 200, flex: 1 }}
            />
          </Stack>
        </Stack>
      </Paper>

      {/* Diapositivas propias del carrusel */}
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={2}>
          <SeccionTienda
            titulo="Diapositivas del carrusel"
            donde="Banner de la portada (reemplazan al principal)"
            zona="hero"
            cuando="publicar"
            accion={
              <Button size="small" variant="outlined" startIcon={<Icon icon="lucide:plus" width={15} />} onClick={addHeroSlide} sx={{ flexShrink: 0 }}>
                Agregar diapositiva
              </Button>
            }
          >
            <Typography variant="body2" color="text.secondary">
              Creá las diapositivas del banner en el orden que quieras, cada una con su texto, imagen y botón. Si hay al menos una
              visible, la tienda muestra sólo tus diapositivas; si no, arma el carrusel automático (banner principal + categorías
              destacadas).
            </Typography>
          </SeccionTienda>

          {heroSlides.length === 0 ? (
            <Typography variant="caption" color="text.secondary">
              Sin diapositivas propias. La tienda muestra el carrusel automático.
            </Typography>
          ) : (
            heroSlides.map((slide, idx) => (
              <Paper key={slide.id ?? idx} variant="outlined" sx={{ p: 1.5, bgcolor: "action.hover" }}>
                <Stack spacing={1.5}>
                  <Stack direction="row" alignItems="center" justifyContent="space-between">
                    <FormControlLabel
                      control={<Switch size="small" checked={slide.enabled !== false} onChange={(e) => updateHeroSlide(idx, "enabled", e.target.checked)} />}
                      label={
                        <Typography variant="caption" fontWeight={600}>
                          {slide.enabled !== false ? "Visible" : "Oculta"} · Diapositiva {idx + 1}
                        </Typography>
                      }
                    />
                    <Stack direction="row" spacing={0.5}>
                      <Tooltip title="Subir">
                        <span>
                          <IconButton size="small" disabled={idx === 0} onClick={() => moveHeroSlide(idx, -1)}>
                            <Icon icon="lucide:chevron-up" width={16} />
                          </IconButton>
                        </span>
                      </Tooltip>
                      <Tooltip title="Bajar">
                        <span>
                          <IconButton size="small" disabled={idx === heroSlides.length - 1} onClick={() => moveHeroSlide(idx, 1)}>
                            <Icon icon="lucide:chevron-down" width={16} />
                          </IconButton>
                        </span>
                      </Tooltip>
                      <Tooltip title="Eliminar">
                        <IconButton size="small" color="error" onClick={() => removeHeroSlide(idx)}>
                          <Icon icon="lucide:trash-2" width={16} />
                        </IconButton>
                      </Tooltip>
                    </Stack>
                  </Stack>
                  <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
                    <TextField size="small" label="Etiqueta" placeholder="Ej: Exclusivo" value={slide.badge || ""} onChange={(e) => updateHeroSlide(idx, "badge", e.target.value)} sx={{ minWidth: 180, flex: 1 }} />
                    <TextField size="small" label="Título" value={slide.title || ""} onChange={(e) => updateHeroSlide(idx, "title", e.target.value)} sx={{ minWidth: 220, flex: 2 }} />
                  </Stack>
                  <TextField size="small" fullWidth multiline minRows={2} label="Descripción" value={slide.description || ""} onChange={(e) => updateHeroSlide(idx, "description", e.target.value)} />
                  <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
                    <TextField size="small" label="Texto del botón" placeholder="Ej: Ver colección" value={slide.ctaLabel || ""} onChange={(e) => updateHeroSlide(idx, "ctaLabel", e.target.value)} sx={{ minWidth: 200, flex: 1 }} />
                    <TextField
                      select
                      size="small"
                      label="Categoría del botón"
                      SelectProps={{ native: true }}
                      InputLabelProps={{ shrink: true }}
                      value={slide.category || ""}
                      onChange={(e) => updateHeroSlide(idx, "category", e.target.value)}
                      helperText="Al tocar el botón, la tienda filtra por esta categoría."
                      sx={{ minWidth: 220, flex: 1 }}
                    >
                      <option value="">Todo el catálogo</option>
                      {categorias.map((c) => (
                        <option key={c.id} value={c.name}>
                          {c.name}
                        </option>
                      ))}
                    </TextField>
                  </Stack>
                  <ImageUploadField
                    label="Imagen de la diapositiva"
                    value={slide.image}
                    onChange={(url) => updateHeroSlide(idx, "image", url)}
                    uploadFn={ecommerceService.uploadStoreImage}
                    slot="hero-slide"
                    height={120}
                    helperText="Imagen de fondo o ilustración de la diapositiva."
                  />
                </Stack>
              </Paper>
            ))
          )}
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={2}>
          <Stack
            direction={{ xs: "column", sm: "row" }}
            alignItems={{ sm: "center" }}
            justifyContent="space-between"
            spacing={1.5}
          >
            <Box sx={{ flex: 1, minWidth: 0 }}>
              <SeccionTienda titulo="Promoción al ingresar" donde="Ventana que aparece al entrar a la tienda" zona="popup" cuando="publicar">
                <Typography variant="body2" color="text.secondary">
                  Muestra una campaña destacada al abrir la tienda, respetando vigencia y frecuencia (por ejemplo, una vez por día por visitante).
                </Typography>
              </SeccionTienda>
            </Box>
            <FormControlLabel
              control={
                <Switch
                  checked={p.popupEnabled === true}
                  onChange={(e) => onPromotionChange("popupEnabled", e.target.checked)}
                />
              }
              label={p.popupEnabled ? "Activa" : "Desactivada"}
            />
          </Stack>

          {p.popupEnabled && (
            <>
              <Divider />
              <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
                <TextField
                  size="small"
                  label="Etiqueta"
                  value={p.popupBadge || ""}
                  onChange={(e) => onPromotionChange("popupBadge", e.target.value)}
                  placeholder="Oferta especial"
                  sx={{ minWidth: 180 }}
                />
                <TextField
                  size="small"
                  label="Título"
                  value={p.popupTitle || ""}
                  onChange={(e) => onPromotionChange("popupTitle", e.target.value)}
                  sx={{ minWidth: 260, flex: 1 }}
                />
              </Stack>

              <TextField
                size="small"
                fullWidth
                multiline
                minRows={2}
                label="Descripción"
                value={p.popupDescription || ""}
                onChange={(e) => onPromotionChange("popupDescription", e.target.value)}
              />

              <ImageUploadField
                label="Imagen de la promoción"
                value={p.popupImage}
                onChange={(url) => onPromotionChange("popupImage", url)}
                uploadFn={ecommerceService.uploadStoreImage}
                slot="promo"
                height={150}
                helperText="Si la dejás vacía, se usa la imagen del banner principal."
              />

              <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
                <TextField
                  size="small"
                  label="Texto del botón"
                  value={p.popupCtaLabel || ""}
                  onChange={(e) => onPromotionChange("popupCtaLabel", e.target.value)}
                  placeholder="Comprar ahora"
                  sx={{ minWidth: 190 }}
                />
                <TextField
                  select
                  size="small"
                  label="Categoría destino"
                  SelectProps={{ native: true }}
                  InputLabelProps={{ shrink: true }}
                  value={targetCategoryValue}
                  onChange={(e) => onPromotionChange("popupTargetCategory", e.target.value)}
                  helperText="Al tocar la promo, la tienda filtra por esta categoría."
                  sx={{ minWidth: 220, flex: 1 }}
                >
                  <option value="">Todos (sin categoría específica)</option>
                  {categorias.map((c) => (
                    <option key={c.id} value={c.name}>
                      {c.name}
                      {typeof c.count === "number" ? ` (${c.count})` : ""}
                    </option>
                  ))}
                </TextField>
                <TextField
                  select
                  size="small"
                  label="Frecuencia"
                  SelectProps={{ native: true }}
                  InputLabelProps={{ shrink: true }}
                  value={p.popupFrequency || "oncePerSession"}
                  onChange={(e) => onPromotionChange("popupFrequency", e.target.value)}
                  sx={{ minWidth: 210 }}
                >
                  <option value="oncePerSession">Una vez por sesión</option>
                  <option value="oncePerDay">Una vez por día</option>
                  <option value="always">En cada carga</option>
                </TextField>
              </Stack>

              <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
                <TextField
                  type="date"
                  size="small"
                  label="Mostrar desde"
                  InputLabelProps={{ shrink: true }}
                  value={p.popupStartDate || ""}
                  onChange={(e) => onPromotionChange("popupStartDate", e.target.value)}
                  sx={{ minWidth: 190 }}
                />
                <TextField
                  type="date"
                  size="small"
                  label="Mostrar hasta"
                  InputLabelProps={{ shrink: true }}
                  value={p.popupEndDate || ""}
                  onChange={(e) => onPromotionChange("popupEndDate", e.target.value)}
                  sx={{ minWidth: 190 }}
                />
              </Stack>

              {!p.popupImage && p.heroImage && (
                <Box>
                  <Typography variant="caption" color="text.secondary">
                    Vista previa (usa la imagen del banner principal):
                  </Typography>
                  <Box
                    sx={{
                      mt: 0.5,
                      width: "min(100%, 520px)",
                      aspectRatio: "16 / 7",
                      overflow: "hidden",
                      borderRadius: 2,
                      border: 1,
                      borderColor: "divider",
                    }}
                  >
                    <img
                      src={p.heroImage}
                      alt="Vista previa de la promoción de ingreso"
                      style={{ width: "100%", height: "100%", display: "block", objectFit: "cover" }}
                    />
                  </Box>
                </Box>
              )}
            </>
          )}
        </Stack>
      </Paper>
    </Stack>
  );
}

PromotionsContentPanel.propTypes = {
  promotions: PropTypes.object.isRequired,
  onPromotionChange: PropTypes.func.isRequired,
  seasons: PropTypes.array,
};

const ESTADO_OPTIONS = [
  { value: "borrador", label: "Borrador (no visible)" },
  { value: "activa", label: "Activa (tienda online)" },
  { value: "pausada", label: "Pausada (avisa que está en pausa)" },
];

function DeliveryMethodsEditor({ methods, onChange, envioProductoId, productosServicio = [], onEnvioProductoChange }) {
  const cobraEnvio = methods.some((m) => m.enabled !== false && Number(m.cost) > 0);
  const updateMethod = (id, field, value) => {
    onChange(methods.map((method) => (method.id === id ? { ...method, [field]: value } : method)));
  };

  const addMethod = () => {
    const id = `entrega-${Date.now()}`;
    onChange([
      ...methods,
      {
        id,
        type: "delivery",
        name: "Nuevo método de entrega",
        cost: 0,
        enabled: true,
      },
    ]);
  };

  return (
    <Paper variant="outlined" sx={{ p: 2 }}>
      <Stack spacing={1.5}>
        <SeccionTienda titulo="Métodos de entrega" donde="Checkout: paso de entrega y total del pedido" zona="checkout" cuando="publicar">
          <Typography variant="body2" color="text.secondary">
            Definí las opciones que verá el cliente en checkout y el costo que el backend sumará al pedido.
          </Typography>
        </SeccionTienda>

        {methods.map((method) => (
          <Paper key={method.id} variant="outlined" sx={{ p: 1.5 }}>
            <Stack direction={{ xs: "column", md: "row" }} spacing={1.25} alignItems={{ md: "center" }}>
              <FormControlLabel
                control={
                  <Switch checked={method.enabled !== false} onChange={(event) => updateMethod(method.id, "enabled", event.target.checked)} color="success" />
                }
                label={method.enabled !== false ? "Activo" : "Inactivo"}
                sx={{ m: 0, minWidth: 100 }}
              />
              <TextField
                select
                size="small"
                label="Tipo"
                SelectProps={{ native: true }}
                value={method.type}
                onChange={(event) => updateMethod(method.id, "type", event.target.value)}
                sx={{ minWidth: 160 }}
              >
                <option value="delivery">Delivery</option>
                <option value="pickup">Retiro en local</option>
              </TextField>
              <TextField
                size="small"
                label="Nombre visible"
                value={method.name}
                onChange={(event) => updateMethod(method.id, "name", event.target.value)}
                sx={{ minWidth: 240, flex: 1 }}
              />
              <MonedaInput
                label="Costo"
                value={method.cost}
                onChange={(value) => updateMethod(method.id, "cost", value)}
                sx={{ minWidth: 170 }}
              />
              <Tooltip title={methods.length === 1 ? "Debe existir al menos un método" : "Eliminar método"}>
                <span>
                  <IconButton
                    color="error"
                    disabled={methods.length === 1}
                    onClick={() => onChange(methods.filter((item) => item.id !== method.id))}
                    aria-label={`Eliminar ${method.name}`}
                  >
                    <Icon icon="lucide:trash-2" width={18} />
                  </IconButton>
                </span>
              </Tooltip>
            </Stack>
          </Paper>
        ))}

        <Box>
          <Button variant="outlined" startIcon={<Icon icon="lucide:plus" width={17} />} onClick={addMethod}>
            Agregar método
          </Button>
        </Box>

        {/* El costo de envío entra al pedido ERP (y a la factura) como una línea de este
            producto. Sin él, el envío se cobraba en la tienda pero no se facturaba. */}
        <Divider />
        <Stack spacing={1}>
          <TextField
            select
            size="small"
            label="Producto para facturar el envío"
            SelectProps={{ native: true }}
            InputLabelProps={{ shrink: true }}
            value={envioProductoId || ""}
            onChange={(e) => onEnvioProductoChange?.(e.target.value)}
            helperText="Un producto sin inventario (servicio). El costo de envío del pedido entra a la factura como una línea de este producto."
            sx={{ maxWidth: 480 }}
          >
            <option value="">— Sin producto: el envío no se factura —</option>
            {productosServicio.map((p) => (
              <option key={p.id} value={p.id}>
                {p.descripcion}
              </option>
            ))}
          </TextField>
          {cobraEnvio && !envioProductoId && (
            <Alert severity="warning" sx={{ py: 0.5 }}>
              {productosServicio.length === 0
                ? "Cobrás envío pero no hay ningún producto de servicio para facturarlo. Creá en Productos uno llamado «Envío», sin manejo de inventario, y elegilo acá."
                : "Cobrás envío pero no elegiste con qué producto facturarlo: el pedido ERP lo deja anotado y hay que agregarlo a mano en la factura."}
            </Alert>
          )}
        </Stack>
      </Stack>
    </Paper>
  );
}

DeliveryMethodsEditor.propTypes = {
  methods: PropTypes.arrayOf(PropTypes.object).isRequired,
  onChange: PropTypes.func.isRequired,
  envioProductoId: PropTypes.string,
  productosServicio: PropTypes.arrayOf(PropTypes.object),
  onEnvioProductoChange: PropTypes.func,
};

function PaymentMethodsEditor({ methods, onChange }) {
  // Providers reales del gateway. Si la consulta falla (por permisos o porque la
  // cuenta todavía no está configurada) el campo queda como texto libre en vez de
  // bloquear la pantalla.
  const [providers, setProviders] = useState([]);
  const [providersError, setProvidersError] = useState(false);

  useEffect(() => {
    let vivo = true;
    getNovasisPayProviders()
      .then((data) => {
        if (!vivo) return;
        setProviders(Array.isArray(data?.providers) ? data.providers : []);
      })
      .catch(() => vivo && setProvidersError(true));
    return () => {
      vivo = false;
    };
  }, []);

  const updateMethod = (id, field, value) => {
    onChange(methods.map((method) => (method.id === id ? { ...method, [field]: value } : method)));
  };

  const addMethod = () => {
    const id = `pago-${Date.now()}`;
    onChange([
      ...methods,
      {
        id,
        type: "manual",
        name: "Nuevo medio de pago",
        enabled: true,
        discountPercent: 0,
        provider: "",
        platformId: "",
      },
    ]);
  };

  return (
    <Paper variant="outlined" sx={{ p: 2 }}>
      <Stack spacing={1.5}>
        <SeccionTienda titulo="Medios de pago" donde="Checkout: paso de pago" zona="checkout" cuando="publicar">
          <Typography variant="body2" color="text.secondary">
            Definí qué medios verá el cliente en el checkout. El tipo &ldquo;Online&rdquo; dispara el cobro por Novasis Pay; los demás
            quedan pendientes de pago para que el operador los confirme manualmente. El descuento (si tiene) se
            aplica sobre el subtotal. Podés publicar varios medios online con distinto proveedor —por ejemplo
            &ldquo;Tarjeta&rdquo; con dLocal y &ldquo;QR / billetera&rdquo; con Dpago— y el comprador elige.
          </Typography>
        </SeccionTienda>

        {methods.map((method) => (
          <Paper key={method.id} variant="outlined" sx={{ p: 1.5 }}>
            <Stack direction={{ xs: "column", md: "row" }} spacing={1.25} alignItems={{ md: "center" }}>
              <FormControlLabel
                control={
                  <Switch checked={method.enabled !== false} onChange={(event) => updateMethod(method.id, "enabled", event.target.checked)} color="success" />
                }
                label={method.enabled !== false ? "Activo" : "Inactivo"}
                sx={{ m: 0, minWidth: 100 }}
              />
              <TextField
                select
                size="small"
                label="Tipo"
                SelectProps={{ native: true }}
                value={method.type}
                onChange={(event) => updateMethod(method.id, "type", event.target.value)}
                sx={{ minWidth: 160 }}
              >
                <option value="online">Online (Novasis Pay)</option>
                <option value="manual">Manual</option>
              </TextField>
              <TextField
                size="small"
                label="Nombre visible"
                value={method.name}
                onChange={(event) => updateMethod(method.id, "name", event.target.value)}
                sx={{ minWidth: 220, flex: 1 }}
              />
              {method.type === "online" && (
                <TextField
                  select
                  size="small"
                  label="Proveedor"
                  SelectProps={{ native: true }}
                  // Select nativo con valor "" (Predeterminado): sin shrink, MUI deja
                  // el label encima del texto de la opción.
                  InputLabelProps={{ shrink: true }}
                  value={method.provider || ""}
                  onChange={(event) => updateMethod(method.id, "provider", event.target.value)}
                  helperText={providersError ? "No se pudo leer el catálogo" : "Vacío = el del gateway"}
                  sx={{ minWidth: 170 }}
                >
                  <option value="">Predeterminado</option>
                  {providers.map((p) => (
                    <option key={p.id} value={p.id}>
                      {p.displayName || "Proveedor de pago"}
                    </option>
                  ))}
                </TextField>
              )}
              <TextField
                size="small"
                type="number"
                label="Descuento %"
                value={method.discountPercent ?? 0}
                onChange={(event) => updateMethod(method.id, "discountPercent", Math.max(0, Math.min(100, Number(event.target.value) || 0)))}
                inputProps={{ min: 0, max: 100, step: 1 }}
                sx={{ minWidth: 130 }}
              />
              <Tooltip title={methods.length === 1 ? "Debe existir al menos un medio de pago" : "Eliminar medio"}>
                <span>
                  <IconButton
                    color="error"
                    disabled={methods.length === 1}
                    onClick={() => onChange(methods.filter((item) => item.id !== method.id))}
                    aria-label={`Eliminar ${method.name}`}
                  >
                    <Icon icon="lucide:trash-2" width={18} />
                  </IconButton>
                </span>
              </Tooltip>
            </Stack>
          </Paper>
        ))}

        <Box>
          <Button variant="outlined" startIcon={<Icon icon="lucide:plus" width={17} />} onClick={addMethod}>
            Agregar medio de pago
          </Button>
        </Box>
      </Stack>
    </Paper>
  );
}

function CreditoConfigEditor({ config, onChange }) {
  const set = (key, value) => onChange({ ...config, [key]: value });

  return (
    <Paper variant="outlined" sx={{ p: 2.5, borderRadius: 2 }}>
      <Stack spacing={2}>
        <SeccionTienda titulo="Compra en cuotas" donde="Ficha del producto, tarjetas y checkout" zona="ficha" cuando="publicar">
          <Typography variant="body2" color="text.secondary">
            Los planes de cuotas se cargan por producto en Inventario → Productos → Precios. Al activarlo, el
            cliente puede elegir un plan en la ficha y el pedido genera una solicitud de crédito para aprobar. Apagado, la
            tienda no muestra ningún plan.
          </Typography>
        </SeccionTienda>

        <FormControlLabel
          control={<Switch checked={config.enabled === true} onChange={(e) => set("enabled", e.target.checked)} />}
          label="Habilitar compra en cuotas en la tienda"
        />

        {config.enabled && (
          <>
            <Alert severity="info" sx={{ py: 0.5 }}>
              Cada pedido financiado crea una <strong>solicitud de crédito</strong> con las líneas y el plan
              elegido. Se aprueba desde Ventas → Solicitudes de crédito, igual que una venta a crédito del mostrador.
            </Alert>

            <Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
              <TextField
                select
                size="small"
                label="Si el plan y el medio de pago tienen descuento"
                SelectProps={{ native: true }}
                value={config.precedenciaDescuento}
                onChange={(e) => set("precedenciaDescuento", e.target.value)}
                helperText="Qué descuento se aplica cuando hay dos"
                sx={{ flex: 1, minWidth: 260 }}
              >
                <option value="plan">Gana el del plan de cuotas</option>
                <option value="medio_pago">Gana el del medio de pago</option>
                <option value="acumular">Se acumulan los dos</option>
              </TextField>

              <TextField
                select
                size="small"
                label="Estado inicial de la solicitud"
                SelectProps={{ native: true }}
                value={config.estadoInicial}
                onChange={(e) => set("estadoInicial", e.target.value)}
                helperText="Borrador no entra al circuito de aprobación"
                sx={{ flex: 1, minWidth: 240 }}
              >
                <option value="pendiente_aprobacion">Pendiente de aprobación</option>
                <option value="borrador">Borrador</option>
              </TextField>

              <TextField
                size="small"
                type="number"
                label="Días entre cuotas"
                value={config.intervaloDias}
                onChange={(e) => set("intervaloDias", Math.max(1, Number(e.target.value) || 30))}
                inputProps={{ min: 1, max: 365, step: 1 }}
                helperText="Ej: 30 (mensual)"
                sx={{ minWidth: 150 }}
              />
            </Stack>

            <FormControlLabel
              control={
                <Switch
                  checked={config.mostrarEnTarjeta !== false}
                  onChange={(e) => set("mostrarEnTarjeta", e.target.checked)}
                />
              }
              label='Mostrar "Hasta N cuotas" en las tarjetas del catálogo'
            />
          </>
        )}
      </Stack>
    </Paper>
  );
}

CreditoConfigEditor.propTypes = {
  config: PropTypes.object.isRequired,
  onChange: PropTypes.func.isRequired,
};

PaymentMethodsEditor.propTypes = {
  methods: PropTypes.arrayOf(PropTypes.object).isRequired,
  onChange: PropTypes.func.isRequired,
};

function ConfiguracionPanel({ draft, opciones, onChange, onDeliveryMethodsChange, onEnvioProductoChange, onPaymentMethodsChange, onCheckoutLayoutChange, onCheckoutReservaChange, onCreditoConfigChange, onWorkflowChange, equipo, preparandoEquipo, onPrepararEquipo, cobertura }) {
  const depositosFiltrados = (opciones.depositos || []).filter((d) => !draft.sucursal_id || d.sucursal_id === draft.sucursal_id);
  const navigate = useNavigate();

  return (
    <Stack spacing={2}>
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Box>
          <Typography variant="subtitle1" fontWeight={700}>
            Configuración del canal
          </Typography>
          <Typography variant="body2" color="text.secondary">
            Identidad y dirección de la tienda, estado de publicación y los valores por defecto que se usan al crear los pedidos de la tienda.
          </Typography>
        </Box>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Identidad del canal" donde="Dirección de la tienda y si está abierta al público" zona="todo" cuando="guardar">
            <Typography variant="body2" color="text.secondary">
              La dirección por la que entran tus clientes y el estado de la tienda. A diferencia del resto, estos cambios rigen apenas guardás.
            </Typography>
          </SeccionTienda>
          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap" alignItems="flex-start">
            <TextField
              size="small"
              label="Subdominio"
              value={draft.subdominio || ""}
              onChange={(e) => onChange("subdominio", e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ""))}
              helperText="Ej: novasis-demo → novasis-demo.novasis.com"
              sx={{ minWidth: 260, flex: 1 }}
            />
            <TextField
              select
              size="small"
              label="Estado"
              SelectProps={{ native: true }}
              value={draft.estado || "borrador"}
              onChange={(e) => onChange("estado", e.target.value)}
              helperText="Rige apenas guardás, sin publicar. Pausada o en borrador, la tienda muestra un aviso en lugar del catálogo."
              sx={{ minWidth: 240, maxWidth: 360 }}
            >
              {ESTADO_OPTIONS.map((o) => (
                <option key={o.value} value={o.value}>
                  {o.label}
                </option>
              ))}
            </TextField>
          </Stack>
          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap" alignItems="flex-start">
            <TextField
              size="small"
              label="Dominio propio (opcional)"
              value={draft.dominio_custom || ""}
              onChange={(e) => onChange("dominio_custom", e.target.value.toLowerCase().trim())}
              placeholder="Ej: tienda.suempresa.com.py"
              helperText="Si el cliente quiere usar su propio dominio: apuntá un CNAME de este dominio hacia nuestro servidor y avisale al equipo para habilitar el certificado SSL — no es autogestionable desde acá."
              sx={{ minWidth: 320, flex: 1 }}
            />
          </Stack>
          {/* Todos los links que reparte el panel (seguimiento, QR, "ver tienda") salen de
              acá. Si el dominio propio está vacío se arma <subdominio>.novasis.com, que no
              tiene DNS ni certificado para la mayoría de los tenants: el link se copia igual
              y falla con error de SSL recién en manos del comprador. Mostrarlo lo hace
              verificable antes de compartirlo. */}
          {buildStoreUrl(draft) && (
            <Alert severity={draft.dominio_custom ? "success" : "warning"} sx={{ py: 0.5 }}>
              <Typography variant="body2">
                Los links que compartas apuntan a <strong>{buildStoreUrl(draft)}</strong>
              </Typography>
              {!draft.dominio_custom && (
                <Typography variant="caption" color="text.secondary">
                  Se arma con el subdominio porque no hay dominio propio cargado. Comprobá que
                  resuelva y tenga certificado; si la tienda se sirve en otro dominio, cargalo
                  arriba o el link de seguimiento no va a abrir.
                </Typography>
              )}
            </Alert>
          )}
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Valores por defecto de los pedidos" donde="Precios y stock del catálogo, y pedidos del ERP" zona="carruseles" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Sucursal y depósito donde se reservará stock, y lista de precios pública usada en el catálogo y checkout.
            </Typography>
          </SeccionTienda>
          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
            <TextField
              select
              size="small"
              label="Sucursal"
              SelectProps={{ native: true }}
              InputLabelProps={{ shrink: true }}
              value={draft.sucursal_id || ""}
              onChange={(e) => {
                onChange("sucursal_id", e.target.value);
                onChange("deposito_id", "");
              }}
              sx={{ minWidth: 240, flex: 1 }}
            >
              <option value="">— Seleccionar —</option>
              {(opciones.sucursales || []).map((s) => (
                <option key={s.id} value={s.id}>
                  {s.descripcion}
                </option>
              ))}
            </TextField>
            <TextField
              select
              size="small"
              label="Depósito (reserva de stock)"
              SelectProps={{ native: true }}
              InputLabelProps={{ shrink: true }}
              value={draft.deposito_id || ""}
              onChange={(e) => onChange("deposito_id", e.target.value)}
              sx={{ minWidth: 240, flex: 1 }}
              helperText={draft.sucursal_id ? undefined : "Elegí una sucursal primero"}
            >
              <option value="">— Stock total (todos los depósitos) —</option>
              {depositosFiltrados.map((d) => (
                <option key={d.id} value={d.id}>
                  {d.descripcion}
                </option>
              ))}
            </TextField>
            <TextField
              select
              size="small"
              label="Lista de precios pública"
              SelectProps={{ native: true }}
              InputLabelProps={{ shrink: true }}
              value={draft.lista_precios_id || ""}
              onChange={(e) => onChange("lista_precios_id", e.target.value)}
              sx={{ minWidth: 240, flex: 1 }}
              helperText="Vacío = precio base del producto"
            >
              <option value="">— Precio base del producto —</option>
              {(opciones.listas || []).map((l) => (
                <option key={l.id} value={l.id}>
                  {l.nombre} ({l.moneda})
                </option>
              ))}
            </TextField>
          </Stack>

          {cobertura && cobertura.publicados === 0 && (
            <Alert severity="info" icon={<Icon icon="lucide:info" width={18} />}>
              No hay productos marcados <strong>“mostrar en ecommerce”</strong>. Marcalos en tu catálogo para que aparezcan en la tienda.
            </Alert>
          )}
          {cobertura && cobertura.publicados > 0 && cobertura.conStock === 0 && (
            <Alert
              severity="warning"
              icon={<Icon icon="lucide:alert-triangle" width={18} />}
              action={
                cobertura.mejorDeposito?.id ? (
                  <Button color="inherit" size="small" onClick={() => onChange("deposito_id", cobertura.mejorDeposito.id)}>
                    Usar {cobertura.mejorDeposito.nombre}
                  </Button>
                ) : undefined
              }
            >
              Este depósito no tiene stock de los <strong>{cobertura.publicados}</strong> productos publicados: la tienda mostraría el <strong>catálogo vacío</strong>.
              {cobertura.mejorDeposito?.id && (
                <> Con más stock: <strong>{cobertura.mejorDeposito.nombre}</strong> ({cobertura.mejorDeposito.conStock} productos).</>
              )}
            </Alert>
          )}
          {cobertura && cobertura.publicados > 0 && cobertura.conStock > 0 && (
            <Alert severity="success" icon={<Icon icon="lucide:check-circle-2" width={18} />}>
              <strong>{cobertura.conStock}</strong> de {cobertura.publicados} productos publicados tienen stock {draft.deposito_id ? "en este depósito" : "en algún depósito"}: se verán en la tienda.
            </Alert>
          )}
        </Stack>
      </Paper>

      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Formato del checkout" donde="Checkout" zona="checkout" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Cómo ve el cliente el formulario de compra. En una pantalla es más rápido para quien ya
              compró antes; por pasos guía mejor a quien compra por primera vez y suele reducir el abandono.
            </Typography>
          </SeccionTienda>
          <FormControl size="small" sx={{ maxWidth: 320 }}>
            <InputLabel>Formato</InputLabel>
            <Select
              label="Formato"
              value={draft.checkout.layout === "steps" ? "steps" : "singlePage"}
              onChange={(e) => onCheckoutLayoutChange(e.target.value)}
            >
              <MenuItem value="singlePage">Una sola pantalla</MenuItem>
              <MenuItem value="steps">Por pasos (datos → entrega y pago → confirmar)</MenuItem>
            </Select>
          </FormControl>
        </Stack>
      </Paper>

      {/* Antes había un único plazo fijo de 15 min para todos los pedidos: el link de
          Novasis Pay seguía vigente 24 h y una transferencia vencía antes de que el
          comercio pudiera confirmarla. */}
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1.5}>
          <SeccionTienda titulo="Plazo para pagar" donde="Pedidos pendientes de pago y su reserva de stock" zona="checkout" cuando="publicar">
            <Typography variant="body2" color="text.secondary">
              Mientras el pedido espera el pago, el stock queda reservado. Si se cumple el plazo
              sin pago, el pedido pasa a Expirado y el stock se libera.
            </Typography>
          </SeccionTienda>
          <Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
            <TextField
              id="reserva-horas-online"
              size="small"
              type="number"
              label="Pago online (horas)"
              value={draft.checkout.reservaHorasOnline ?? ""}
              placeholder="1"
              onChange={(e) => onCheckoutReservaChange("reservaHorasOnline", e.target.value)}
              inputProps={{ min: 1, max: 168, step: 1 }}
              InputLabelProps={{ shrink: true }}
              helperText="Lo que dura el link de Novasis Pay. De 1 a 168. Vacío = 1."
              sx={{ maxWidth: 260 }}
            />
            <TextField
              id="reserva-horas-manual"
              size="small"
              type="number"
              label="Transferencia o contra entrega (horas)"
              value={draft.checkout.reservaHorasManual ?? ""}
              placeholder="24"
              onChange={(e) => onCheckoutReservaChange("reservaHorasManual", e.target.value)}
              inputProps={{ min: 1, max: 720, step: 1 }}
              InputLabelProps={{ shrink: true }}
              helperText="Tiempo para confirmar el pago a mano. De 1 a 720. Vacío = 24."
              sx={{ maxWidth: 300 }}
            />
          </Stack>
        </Stack>
      </Paper>

      <DeliveryMethodsEditor
        methods={draft.checkout.deliveryMethods}
        onChange={onDeliveryMethodsChange}
        envioProductoId={draft.checkout.envioProductoId}
        productosServicio={opciones.productosServicio}
        onEnvioProductoChange={onEnvioProductoChange}
      />
      <PaymentMethodsEditor methods={draft.checkout.paymentMethods} onChange={onPaymentMethodsChange} />
      <CreditoConfigEditor config={draft.checkout.creditoConfig} onChange={onCreditoConfigChange} />
      <WorkflowConfigEditor value={draft.workflow} onChange={onWorkflowChange} />

      {/* Equipo y permisos: atajo a los roles operativos. La asignación de
          usuarios vive en Seguridad (fuente de verdad); acá sólo se preparan. */}
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={2}>
          <SeccionTienda
            titulo="Equipo y permisos"
            donde="Sólo en el panel: quién opera los pedidos"
            zona="panel"
            cuando="instante"
            accion={
              <>
                {equipo?.listos ? (
                  <Chip size="small" color="success" variant="outlined" icon={<Icon icon="lucide:check" width={14} />} label="Roles creados" />
                ) : (
                  <Button
                    size="small"
                    variant="contained"
                    disabled={preparandoEquipo}
                    onClick={onPrepararEquipo}
                    startIcon={<Icon icon={preparandoEquipo ? "lucide:loader" : "lucide:users-round"} width={16} />}
                  >
                    {preparandoEquipo ? "Preparando…" : "Preparar roles de la tienda"}
                  </Button>
                )}
              </>
            }
          >
            <Typography variant="body2" color="text.secondary">
              Roles listos para operar la tienda. Prepararlos los crea en Seguridad; después asignás cada usuario al rol que corresponda.
            </Typography>
          </SeccionTienda>

          <Stack direction="row" spacing={1.5} useFlexGap flexWrap="wrap">
            {(equipo?.perfiles ?? EQUIPO_ROLES_FALLBACK).map((rol) => (
              <Paper key={rol.perfil} variant="outlined" sx={{ p: 1.5, flex: "1 1 240px", minWidth: 220, borderColor: rol.existe ? "success.light" : "divider" }}>
                <Stack direction="row" alignItems="center" spacing={1} sx={{ mb: 0.5 }}>
                  <Icon icon={EQUIPO_ROL_ICON[rol.perfil] || "lucide:user"} width={18} />
                  <Typography variant="body2" fontWeight={700} sx={{ flex: 1 }}>
                    {rol.perfil}
                  </Typography>
                  {rol.existe ? (
                    <Chip size="small" color="success" variant="outlined" label={`${rol.usuarios ?? 0} usuario${(rol.usuarios ?? 0) === 1 ? "" : "s"}`} />
                  ) : (
                    <Chip size="small" variant="outlined" label="Sin crear" />
                  )}
                </Stack>
                <Typography variant="caption" color="text.secondary">
                  {rol.desc}
                </Typography>
              </Paper>
            ))}
          </Stack>

          <Box>
            <Button
              size="small"
              variant="text"
              onClick={() => navigate("/configuracion?seccion=perfiles")}
              endIcon={<Icon icon="lucide:arrow-up-right" width={14} />}
            >
              Asignar usuarios a los roles en Seguridad
            </Button>
          </Box>
        </Stack>
      </Paper>
    </Stack>
  );
}

// Roles operativos de la tienda (coinciden con los perfiles autoProvision del
// backend). El fallback se muestra si el backend aún no devolvió el estado.
const EQUIPO_ROLES_FALLBACK = [
  { perfil: "Gestor de Tienda", desc: "Diseña, publica y mantiene la tienda y las novedades. Ve pedidos y pagos, sin operarlos.", existe: false, usuarios: 0 },
  { perfil: "Operador Ecommerce", desc: "Operación completa de pedidos: prepara, despacha, retiros, incidencias y cancelación.", existe: false, usuarios: 0 },
  { perfil: "Despacho Ecommerce", desc: "Prepara y despacha delivery (en camino / entregado). No atiende mostrador ni cancela.", existe: false, usuarios: 0 },
  { perfil: "Mostrador Ecommerce", desc: "Prepara y confirma retiros en el local. No despacha delivery ni cancela.", existe: false, usuarios: 0 },
];

const EQUIPO_ROL_ICON = {
  "Gestor de Tienda": "lucide:palette",
  "Operador Ecommerce": "lucide:user-cog",
  "Despacho Ecommerce": "lucide:truck",
  "Mostrador Ecommerce": "lucide:store",
};

ConfiguracionPanel.propTypes = {
  draft: PropTypes.object.isRequired,
  opciones: PropTypes.object.isRequired,
  onChange: PropTypes.func.isRequired,
  onDeliveryMethodsChange: PropTypes.func.isRequired,
  onEnvioProductoChange: PropTypes.func,
  onCheckoutLayoutChange: PropTypes.func.isRequired,
  onCheckoutReservaChange: PropTypes.func.isRequired,
  onPaymentMethodsChange: PropTypes.func.isRequired,
  onCreditoConfigChange: PropTypes.func.isRequired,
  onWorkflowChange: PropTypes.func.isRequired,
  equipo: PropTypes.object,
  preparandoEquipo: PropTypes.bool,
  onPrepararEquipo: PropTypes.func,
  cobertura: PropTypes.object,
};

const ESTADO_PEDIDO_META = {
  pendiente_pago: {
    label: "Pendiente pago",
    description: "Checkout recibido, pendiente de aprobación o verificación del pago.",
    tone: "#f59e0b",
    bg: "#fff7ed",
    icon: "lucide:clock-3",
  },
  pago_confirmado: {
    label: "A verificar",
    description: "Pago confirmado. El operador valida datos y libera a preparación.",
    tone: "#2563eb",
    bg: "#eff6ff",
    icon: "lucide:clipboard-check",
  },
  en_verificacion: {
    label: "En verificación",
    description: "Pedido tomado por un verificador. Otro operador ya no puede tomarlo.",
    tone: "#2563eb",
    bg: "#eff6ff",
    icon: "lucide:user-check",
  },
  verificado: {
    label: "Verificado",
    description: "Datos, pago y reserva validados; listo para preparación.",
    tone: "#0f766e",
    bg: "#ecfdf5",
    icon: "lucide:badge-check",
  },
  pedido_creado: {
    label: "Pedido ERP",
    description: "Pedido ERP creado con stock reservado, listo para preparación.",
    tone: "#0f766e",
    bg: "#ecfdf5",
    icon: "lucide:file-check-2",
  },
  preparando: {
    label: "En preparación",
    description: "Picking, empaque y control de productos.",
    tone: "#7c3aed",
    bg: "#f5f3ff",
    icon: "lucide:package-open",
  },
  empaquetado: {
    label: "Empaquetado",
    description: "Productos controlados y embalados.",
    tone: "#7c3aed",
    bg: "#f5f3ff",
    icon: "lucide:package-check",
  },
  listo: {
    label: "Listo despacho",
    description: "Pedido preparado para retiro o entrega.",
    tone: "#0891b2",
    bg: "#ecfeff",
    icon: "lucide:package-check",
  },
  listo_retiro: {
    label: "Listo para retiro",
    description: "El cliente puede retirar el pedido en el local.",
    tone: "#0891b2",
    bg: "#ecfeff",
    icon: "lucide:store",
  },
  listo_despacho: {
    label: "Listo para despacho",
    description: "Pedido preparado para entregar al repartidor o transportista.",
    tone: "#0891b2",
    bg: "#ecfeff",
    icon: "lucide:package-check",
  },
  en_camino: {
    label: "En camino",
    description: "Pedido despachado y en traslado al cliente.",
    tone: "#0284c7",
    bg: "#f0f9ff",
    icon: "lucide:truck",
  },
  retirado: {
    label: "Retirado",
    description: "Pedido retirado en el local y cerrado.",
    tone: "#16a34a",
    bg: "#f0fdf4",
    icon: "lucide:shopping-bag-check",
  },
  entregado: {
    label: "Entregado",
    description: "Pedido cerrado y entregado al cliente.",
    tone: "#16a34a",
    bg: "#f0fdf4",
    icon: "lucide:check-circle-2",
  },
  incidencia: {
    label: "Con incidencia",
    description: "Pedido congelado fuera del flujo. Un supervisor lo reencauza a preparación o lo cancela.",
    tone: "#ea580c",
    bg: "#fff7ed",
    icon: "lucide:triangle-alert",
  },
  cancelado: {
    label: "Cancelado",
    description: "Cancelado por incidencia, pago rechazado o decisión operativa.",
    tone: "#dc2626",
    bg: "#fef2f2",
    icon: "lucide:x-circle",
  },
  expirado: {
    label: "Expirado",
    description: "Checkout vencido sin completar pago.",
    tone: "#64748b",
    bg: "#f8fafc",
    icon: "lucide:timer-off",
  },
};

// `always`: se muestra siempre. `needs`: solo si el flujo usa esa etapa/rama.
// `terminalNoise`: estado final que solo se muestra si tiene pedidos. En todos
// los casos, un estado con pedidos (count>0) se muestra igual, para no ocultar
// nada operativo.
const PEDIDO_ESTADO_TABS = [
  { value: "", label: "Todos", icon: "lucide:list", tone: "#475569", always: true },
  { value: "pendiente_pago", label: "Pendiente pago", icon: "lucide:clock-3", tone: "#f59e0b", always: true },
  { value: "pago_confirmado", label: "A verificar", icon: "lucide:clipboard-check", tone: "#2563eb", always: true },
  { value: "en_verificacion", label: "Verificando", icon: "lucide:user-check", tone: "#2563eb", needs: "verificacion" },
  { value: "verificado", label: "Verificado", icon: "lucide:badge-check", tone: "#0f766e", needs: "verificacion" },
  { value: "preparando", label: "Preparación", icon: "lucide:package-open", tone: "#7c3aed", always: true },
  { value: "empaquetado", label: "Empaquetado", icon: "lucide:package-check", tone: "#7c3aed", needs: "empaque" },
  { value: "listo_despacho", label: "Listo despacho", icon: "lucide:package-check", tone: "#0891b2", needs: "delivery" },
  { value: "en_camino", label: "En camino", icon: "lucide:truck", tone: "#0284c7", needs: "transito" },
  { value: "entregado", label: "Entregado", icon: "lucide:check-circle-2", tone: "#16a34a", needs: "delivery" },
  { value: "listo_retiro", label: "Listo retiro", icon: "lucide:store", tone: "#0891b2", needs: "pickup" },
  { value: "retirado", label: "Retirado", icon: "lucide:shopping-bag-check", tone: "#16a34a", needs: "pickup" },
  { value: "incidencia", label: "Incidencias", icon: "lucide:triangle-alert", tone: "#ea580c", always: true },
  { value: "cancelado", label: "Cancelados", icon: "lucide:x-circle", tone: "#dc2626", always: true },
  { value: "expirado", label: "Expirados", icon: "lucide:timer-off", tone: "#64748b", terminalNoise: true },
];

// Deriva qué etapas/ramas usa la tienda según su workflow y métodos de entrega.
// Sin config (aún cargando) asume todo activo para no ocultar de más.
function derivarFlujoPedidos(workflow, checkout) {
  return {
    verificacion: workflow?.verificationEnabled ?? true,
    empaque: workflow?.packingEnabled ?? true,
    transito: workflow?.transitEnabled ?? true,
    delivery: checkout?.allowDelivery ?? true,
    pickup: checkout?.allowPickup ?? true,
  };
}

function tabsPedidosVisibles(flujo, resumen) {
  return PEDIDO_ESTADO_TABS.map((tab) =>
    tab.value === "pago_confirmado" && !flujo.verificacion ? { ...tab, label: PAGO_CONFIRMADO_SIN_VERIFICACION.label } : tab,
  ).filter((tab) => {
    if (tab.always) return true;
    if ((resumen?.[tab.value] || 0) > 0) return true; // nunca ocultar un estado con pedidos
    if (tab.terminalNoise) return false;
    if (tab.needs === "verificacion") return flujo.verificacion;
    if (tab.needs === "empaque") return flujo.empaque;
    if (tab.needs === "transito") return flujo.delivery && flujo.transito;
    if (tab.needs === "delivery") return flujo.delivery;
    if (tab.needs === "pickup") return flujo.pickup;
    return true;
  });
}

const ACCION_ESTADO_LABEL = {
  pago_confirmado: "Confirmar pago",
  en_verificacion: "Tomar para verificar",
  verificado: "Finalizar verificación",
  preparando: "Iniciar preparación",
  empaquetado: "Confirmar empaque",
  listo: "Marcar listo",
  listo_retiro: "Dejar listo para retiro",
  listo_despacho: "Dejar listo para despacho",
  en_camino: "Marcar en camino",
  retirado: "Confirmar retiro",
  entregado: "Marcar entregado",
  cancelado: "Cancelar pedido",
};

/**
 * Qué pasa con el pedido en el ERP. Antes era «Reservado» o «Pendiente»: un pedido
 * expirado decía «Pendiente» como si faltara crearlo, y uno entregado seguía
 * «Reservado» aunque no estuviera facturado.
 */
function estadoErp(p) {
  if (p.factura) {
    return { label: "Facturado", title: `Factura ${p.factura.numero}`, icon: "lucide:receipt", tone: "#16a34a", bg: "#f0fdf4" };
  }
  // Compra en cuotas: no lleva pedido ERP, se factura desde la solicitud de crédito.
  if (p.credito && !["rechazada", "cancelada"].includes(p.credito.estado)) {
    return {
      label: p.credito.estado === "aprobada" ? "Crédito aprobado" : "Crédito en trámite",
      title: "Compra en cuotas: se factura desde la solicitud de crédito (POS → Solicitudes Crédito)",
      icon: "lucide:calendar-clock",
      tone: "#2563eb",
      bg: "#eff6ff",
    };
  }
  if (["expirado", "cancelado"].includes(p.estado)) {
    return { label: "No aplica", title: "El pedido no llegó a generar pedido ERP", icon: "lucide:minus", tone: "#64748b", bg: "#f1f5f9" };
  }
  if (p.pedido_id && ["entregado", "retirado"].includes(p.estado)) {
    return { label: "Sin facturar", title: "Entregado pero todavía sin factura", icon: "lucide:alert-circle", tone: "#c2410c", bg: "#fff7ed" };
  }
  if (p.pedido_id) {
    return { label: "Reservado", title: "Pedido ERP creado con stock reservado", icon: "lucide:check-circle-2", tone: "#16a34a", bg: "#f0fdf4" };
  }
  return { label: "Pendiente", title: "El pedido ERP se crea al confirmar el pago", icon: "lucide:circle-dashed", tone: "#f59e0b", bg: "#fff7ed" };
}

/**
 * Medio de pago que eligió el comprador ("Novasis Pay (tarjetas)", "Transferencia
 * bancaria"). Antes se mostraba el identificador crudo ("novasis_pay", "manual").
 * Los pedidos viejos no guardaban el nombre: se cae a una etiqueta genérica.
 */
function nombreMedioPago(metodo, nombre) {
  if (nombre && String(nombre).trim()) return String(nombre).trim();
  return metodo === "novasis_pay" ? "Novasis Pay" : "Manual";
}

/**
 * Qué hace cada botón, en criollo. Los nombres de estado solos no alcanzaban:
 * "Tomar para verificar" y "Crear pedido ERP" parecían alternativas cuando en
 * realidad son pasos distintos del mismo circuito.
 */
const ACCION_ESTADO_AYUDA = {
  pago_confirmado: "Marca el pago como recibido y habilita la preparación.",
  en_verificacion: "Te asigna el pedido para revisarlo. Nadie más lo va a tomar mientras tanto.",
  verificado: "Confirma que los datos y el stock están bien y deja el pedido listo para preparar.",
  preparando: "Arranca la preparación física del pedido.",
  empaquetado: "El pedido ya está armado y embalado.",
  listo: "El pedido queda listo para su entrega.",
  listo_retiro: "Queda a la espera de que el cliente lo pase a buscar.",
  listo_despacho: "Queda a la espera del transporte.",
  en_camino: "El pedido salió a reparto.",
  retirado: "El cliente ya lo retiró del local.",
  entregado: "El pedido llegó a destino. Cierra el circuito.",
  cancelado: "Anula el pedido y libera el stock reservado. No se puede deshacer.",
};

// Alineado con las opciones del selector de TablePaginationBar (10/20/50/100).
const PAGE_SIZE = 10;

/**
 * Pestaña Pagos: cómo está cobrando la tienda (gateway Novasis Pay vs. manual),
 * el detalle de cada cobro y la reconciliación contra el gateway.
 *
 * El botón "Sincronizar" existe porque el webhook puede perderse o el cliente
 * puede pagar y nunca volver a la tienda: el operador destraba el pedido desde
 * acá y, si el pago está aprobado, se crea el pedido ERP y se reserva el stock.
 */
function PagosPanel() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [estado, setEstado] = useState("");
  const [metodo, setMetodo] = useState("");
  const [q, setQ] = useState("");
  const [syncing, setSyncing] = useState(null);
  const [webhooks, setWebhooks] = useState([]);
  const [verWebhooks, setVerWebhooks] = useState(false);
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(PAGE_SIZE);
  const [pagosEstado, setPagosEstado] = useState(null);

  useEffect(() => {
    let active = true;
    ecommerceService
      .getPagosEstado()
      .then((pe) => active && setPagosEstado(pe))
      .catch(() => undefined);
    return () => {
      active = false;
    };
  }, []);

  const cargar = useCallback(async () => {
    setLoading(true);
    try {
      const res = await ecommerceService.listPagos({
        estado: estado || undefined,
        metodo: metodo || undefined,
        q: q.trim() || undefined,
      });
      setData(res);
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudieron cargar los pagos");
    } finally {
      setLoading(false);
    }
  }, [estado, metodo, q]);

  useEffect(() => {
    setPage(1);
    cargar();
  }, [cargar]);

  const sincronizar = async (id) => {
    setSyncing(id);
    try {
      const res = await ecommerceService.sincronizarPago(id);
      if (res.ok) toast.success(res.mensaje);
      else toast.warning(res.mensaje);
      await cargar();
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudo sincronizar el pago");
    } finally {
      setSyncing(null);
    }
  };

  const abrirWebhooks = async () => {
    setVerWebhooks(true);
    try {
      setWebhooks(await ecommerceService.listWebhooksPago());
    } catch {
      toast.error("No se pudieron cargar los webhooks");
    }
  };

  const items = data?.items ?? [];
  const totalPages = Math.max(1, Math.ceil(items.length / pageSize));
  const pageItems = items.slice((page - 1) * pageSize, page * pageSize);
  const gateway = data?.gateway;
  const resumen = data?.resumen;

  return (
    <Stack gap={2}>
      {/* Estado del gateway: define si la tienda cobra online o a mano. */}
      {gateway && (
        <Alert
          severity={gateway.activo ? "success" : "info"}
          icon={<Icon icon={gateway.activo ? "lucide:shield-check" : "lucide:hand-coins"} width={20} />}
          action={
            <Button size="small" onClick={abrirWebhooks} startIcon={<Icon icon="lucide:webhook" />}>
              Ver webhooks
            </Button>
          }
        >
          {gateway.activo ? (
            <>
              <strong>Novasis Pay activo</strong> — los pagos se cobran online
              {pagosEstado?.tieneWebhook
                ? " y se confirman automáticamente por webhook"
                : " y se confirman al consultar el gateway (falta la clave de webhook para que sea automático)"}
              {" "}(merchant {gateway.merchantId ?? "—"}).
            </>
          ) : (
            <>
              <strong>Cobro manual</strong> — Novasis Pay no está configurado. El cliente transfiere y el operador confirma el pago desde la pestaña Pedidos.
            </>
          )}
        </Alert>
      )}

      {pagosEstado?.activo && (pagosEstado.mode !== "live" || !pagosEstado.tieneWebhook) && (
        <Alert severity="warning" icon={<Icon icon="lucide:alert-triangle" width={18} />}>
          {pagosEstado.mode !== "live" && (
            <>Novasis Pay está en <strong>modo pruebas</strong> — no se cobra dinero real. Pasá a producción cuando estés listo. </>
          )}
          {!pagosEstado.tieneWebhook && (
            <>Falta la <strong>clave de notificaciones (webhook)</strong>: los pagos se confirman por consulta manual, no al instante. </>
          )}
          Configuralo en la pantalla <strong>Novasis Pay</strong>.
        </Alert>
      )}

      {/* KPIs */}
      <Box
        sx={{
          display: "grid",
          gridTemplateColumns: { xs: "1fr 1fr", md: "repeat(4, 1fr)" },
          gap: 1.5,
        }}
      >
        <StatCard>
          <Box sx={{ minWidth: 0 }}>
            <Typography variant="caption" color="text.secondary">
              Cobrado
            </Typography>
            <Typography
              variant="h6"
              color="success.main"
              fontWeight={700}
              noWrap
              title={String(fmtGs(resumen?.cobrado))}
            >
              {loading ? <Skeleton width={110} /> : fmtGs(resumen?.cobrado)}
            </Typography>
          </Box>
        </StatCard>
        <StatCard>
          <Box sx={{ minWidth: 0 }}>
            <Typography variant="caption" color="text.secondary">
              Pendiente de pago
            </Typography>
            <Typography
              variant="h6"
              color="warning.main"
              fontWeight={700}
              noWrap
              title={String(fmtGs(resumen?.pendiente))}
            >
              {loading ? <Skeleton width={110} /> : fmtGs(resumen?.pendiente)}
            </Typography>
          </Box>
        </StatCard>
        <StatCard>
          <Box sx={{ minWidth: 0 }}>
            <Typography variant="caption" color="text.secondary">
              Pagos online
            </Typography>
            <Typography
              variant="h6"
              fontWeight={700}
              noWrap
              title={String((resumen?.porMetodo?.novasis_pay ?? 0))}
            >
              {loading ? <Skeleton width={40} /> : (resumen?.porMetodo?.novasis_pay ?? 0)}
            </Typography>
          </Box>
        </StatCard>
        <StatCard>
          <Box sx={{ minWidth: 0 }}>
            <Typography variant="caption" color="text.secondary">
              Cobros manuales
            </Typography>
            <Typography
              variant="h6"
              fontWeight={700}
              noWrap
              title={String((resumen?.porMetodo?.manual ?? 0))}
            >
              {loading ? <Skeleton width={40} /> : (resumen?.porMetodo?.manual ?? 0)}
            </Typography>
          </Box>
        </StatCard>
      </Box>

      {/* Filtros */}
      <Stack direction="row" gap={1.5} flexWrap="wrap" useFlexGap>
        <TextField
          size="small"
          label="Buscar por código"
          placeholder="Ej: ECOM-2026-0001"
          value={q}
          onChange={(e) => setQ(e.target.value)}
          sx={{ minWidth: 220 }}
        />
        <TextField select size="small" label="Estado" value={estado} onChange={(e) => setEstado(e.target.value)} sx={{ minWidth: 180 }}>
          <MenuItem value="">Todos</MenuItem>
          {Object.entries(ESTADO_PEDIDO_META).map(([k, m]) => (
            <MenuItem key={k} value={k}>
              {m.label}
            </MenuItem>
          ))}
        </TextField>
        <TextField select size="small" label="Método" value={metodo} onChange={(e) => setMetodo(e.target.value)} sx={{ minWidth: 160 }}>
          <MenuItem value="">Todos</MenuItem>
          <MenuItem value="novasis_pay">Novasis Pay</MenuItem>
          <MenuItem value="manual">Manual</MenuItem>
        </TextField>
        <Button onClick={cargar} startIcon={<Icon icon="lucide:refresh-cw" />} disabled={loading}>
          Refrescar
        </Button>
      </Stack>

      {/* Tabla */}
      <Paper variant="outlined">
        {loading ? (
          <Box sx={{ p: 4, textAlign: "center" }}>
            <CircularProgress size={26} />
          </Box>
        ) : items.length === 0 ? (
          <EmptyState
            icon="lucide:credit-card"
            title="Sin pagos todavía"
            description="Cuando la tienda reciba su primer pedido vas a ver acá el detalle del cobro y su estado."
          />
        ) : (
          <>
            <TableContainer>
              <Table size="small">
                <TableHead>
                  <TableRow>
                    <TableCell>Código</TableCell>
                    <TableCell>Cliente</TableCell>
                    <TableCell>Método</TableCell>
                    <TableCell>Estado</TableCell>
                    <TableCell align="right">Total</TableCell>
                    <TableCell align="right">Acciones</TableCell>
                  </TableRow>
                </TableHead>
                <TableBody>
                  {pageItems.map((p) => {
                    const meta = ESTADO_PEDIDO_META[p.estado] ?? {};
                    return (
                      <TableRow key={p.id} hover>
                        <TableCell>
                          <Typography variant="body2" fontWeight={600}>
                            {p.codigo}
                          </Typography>
                          {p.notas && (
                            <Typography variant="caption" color="error.main" sx={{ display: "block" }}>
                              {p.notas}
                            </Typography>
                          )}
                        </TableCell>
                        <TableCell>{p.cliente || "—"}</TableCell>
                        <TableCell>
                          <Chip
                            size="small"
                            variant="outlined"
                            label={p.metodoPago === "novasis_pay" ? "Novasis Pay" : "Manual"}
                            icon={<Icon icon={p.metodoPago === "novasis_pay" ? "lucide:credit-card" : "lucide:banknote"} width={14} />}
                          />
                        </TableCell>
                        <TableCell>
                          <Chip
                            size="small"
                            label={meta.label ?? p.estado}
                            sx={{
                              bgcolor: meta.bg,
                              color: meta.tone,
                              fontWeight: 600,
                            }}
                          />
                        </TableCell>
                        <TableCell align="right">{fmtGs(p.total)}</TableCell>
                        <TableCell align="right">
                          <Stack direction="row" gap={0.5} justifyContent="flex-end">
                            {p.hostedUrl && (
                              <Tooltip title="Abrir link de pago" enterDelay={300}>
                                <IconButton size="small" href={p.hostedUrl} target="_blank" rel="noreferrer">
                                  <Icon icon="lucide:external-link" width={16} />
                                </IconButton>
                              </Tooltip>
                            )}
                            {p.sincronizable && (
                              <Tooltip
                                title="Consultar el gateway y aplicar el resultado. Si el pago está aprobado, crea el pedido ERP y reserva stock."
                                enterDelay={300}
                              >
                                <span>
                                  <Button
                                    size="small"
                                    variant="outlined"
                                    disabled={syncing === p.id}
                                    onClick={() => sincronizar(p.id)}
                                    startIcon={syncing === p.id ? <CircularProgress size={12} /> : <Icon icon="lucide:refresh-cw" width={14} />}
                                  >
                                    Sincronizar
                                  </Button>
                                </span>
                              </Tooltip>
                            )}
                          </Stack>
                        </TableCell>
                      </TableRow>
                    );
                  })}
                </TableBody>
              </Table>
            </TableContainer>

            <Box sx={{ px: 1.5, pb: 1.5 }}>
              <TablePaginationBar
                page={page}
                totalPages={totalPages}
                onPageChange={setPage}
                total={items.length}
                shown={pageItems.length}
                noun="pagos"
                rowsPerPage={pageSize}
                onRowsPerPageChange={(n) => {
                  setPageSize(n);
                  setPage(1);
                }}
              />
            </Box>
          </>
        )}
      </Paper>

      {/* Webhooks: diagnóstico de cobros que no impactaron */}
      <Dialog open={verWebhooks} onClose={() => setVerWebhooks(false)} maxWidth="md" fullWidth>
        <DialogTitle>Últimos webhooks del gateway</DialogTitle>
        <DialogContent dividers>
          <Typography variant="caption" color="text.secondary" sx={{ display: "block", mb: 1.5 }}>
            Si un pago no impactó, buscá acá su referencia del pago (la misma que muestra el
            panel del gateway). &quot;Pago sin pedido&quot; significa que el aviso llegó pero no
            coincidió con ningún pedido.
          </Typography>
          {webhooks.length === 0 ? (
            <Typography variant="body2" color="text.secondary">
              Sin webhooks recibidos.
            </Typography>
          ) : (
            <Table size="small">
              <TableHead>
                <TableRow>
                  <TableCell>Fecha</TableCell>
                  <TableCell>Evento</TableCell>
                  <TableCell>Referencia del pago</TableCell>
                  <TableCell>Resultado</TableCell>
                </TableRow>
              </TableHead>
              <TableBody>
                {webhooks.map((w) => (
                  <TableRow key={w.id}>
                    <TableCell>{new Date(w.fecha).toLocaleString("es-PY")}</TableCell>
                    <TableCell>{w.tipo}</TableCell>
                    <TableCell>
                      <code>{w.intentId ?? "—"}</code>
                    </TableCell>
                    <TableCell>
                      <Chip size="small" color={w.resultado?.startsWith("aplicado") ? "success" : "warning"} label={RESULTADO_WEBHOOK_LABEL[w.resultado] ?? "Otro"} />
                      {w.error && (
                        <Typography variant="caption" color="text.secondary" sx={{ display: "block" }}>
                          {w.error}
                        </Typography>
                      )}
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          )}
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setVerWebhooks(false)}>Cerrar</Button>
        </DialogActions>
      </Dialog>
    </Stack>
  );
}

function fmtGs(n) {
  return `Gs. ${Number(n || 0).toLocaleString("es-PY")}`;
}

// En el flujo sin verificación (Simple) el pago confirmado pasa directo a preparación:
// llamarlo "A verificar" mandaba al operador a buscar un paso que no existe.
const PAGO_CONFIRMADO_SIN_VERIFICACION = {
  label: "Pago confirmado",
  description: "Pago confirmado. Listo para iniciar la preparación.",
};

function getEstadoMeta(estado, flujo) {
  const meta = ESTADO_PEDIDO_META[estado] || {
    label: estado || "Sin estado",
    description: "Estado no catalogado.",
    tone: "#64748b",
    bg: "#f8fafc",
    icon: "lucide:circle",
  };
  if (estado === "pago_confirmado" && flujo && !flujo.verificacion) {
    return { ...meta, ...PAGO_CONFIRMADO_SIN_VERIFICACION };
  }
  return meta;
}

function getPedidoCount(resumen, estado, items) {
  if (!estado) return Object.values(resumen || {}).reduce((acc, n) => acc + Number(n || 0), 0) || items.length;
  return resumen?.[estado] || 0;
}

function PedidoEstadoBadge({ estado, flujo }) {
  const meta = getEstadoMeta(estado, flujo);
  return (
    <PedidoStatusPill $tone={meta.tone} $bg={meta.bg}>
      <Icon icon={meta.icon} />
      <span>{meta.label}</span>
    </PedidoStatusPill>
  );
}

PedidoEstadoBadge.propTypes = {
  estado: PropTypes.string.isRequired,
  flujo: PropTypes.object,
};

function PedidosPanel({ workflow, checkout, storeConfig }) {
  const navigate = useNavigate();
  const [estado, setEstado] = useState("");
  const [query, setQuery] = useState("");
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(PAGE_SIZE);
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState({ items: [], resumen: {} });
  const [selected, setSelected] = useState(null);
  const [detalle, setDetalle] = useState(null);
  const [acting, setActing] = useState(false);
  const [exporting, setExporting] = useState(false);
  const { confirm, prompt, dialogs } = useConfirmDialog();

  const exportarExcel = async () => {
    setExporting(true);
    try {
      const params = {};
      if (estado) params.estado = estado;
      if (query.trim()) params.q = query.trim();
      const rows = await ecommerceService.exportarPedidos(params);
      if (!rows.length) {
        toast.info("No hay pedidos para exportar con los filtros actuales");
        return;
      }
      const sheetRows = rows.map((r) => ({
        "Código": r.codigo,
        "Fecha": fmtFechaHora(r.fecha),
        "Estado": getEstadoMeta(r.estado)?.label || r.estado,
        "Entrega": r.tipo_entrega === "pickup" ? "Retiro en tienda" : r.tipo_entrega ? "Envío a domicilio" : "",
        "Medio de pago": nombreMedioPago(r.metodo_pago, r.medio_pago_nombre),
        "Cliente": r.cliente,
        "Teléfono": r.telefono,
        "Email": r.email,
        "Subtotal": r.subtotal,
        "Envío": r.envio,
        "Total": r.total,
      }));
      const ws = XLSX.utils.json_to_sheet(sheetRows);
      ws["!cols"] = [14, 18, 16, 10, 16, 24, 14, 22, 12, 12, 12].map((w) => ({ wch: w }));
      const wb = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(wb, ws, "Pedidos");
      XLSX.writeFile(wb, `pedidos_ecommerce${estado ? `_${estado}` : ""}.xlsx`);
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudo exportar los pedidos");
    } finally {
      setExporting(false);
    }
  };

  // Paginación server-side: el backend devuelve la página pedida + total.
  // `cargar` ignora argumentos (las llamadas viejas pasan estado/query, ya
  // cubiertos por las deps del useCallback).
  const cargar = useCallback(async () => {
    setLoading(true);
    try {
      const params = { page, limit: pageSize };
      if (estado) params.estado = estado;
      if (query.trim()) params.q = query.trim();
      const res = await ecommerceService.listPedidos(params);
      setData(res);
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudieron cargar los pedidos");
    } finally {
      setLoading(false);
    }
  }, [estado, query, page, pageSize]);

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

  // Volver a la página 1 cuando cambia el filtro o el tamaño de página.
  useEffect(() => {
    setPage(1);
  }, [estado, query, pageSize]);

  const items = data.items || [];
  const totalRegistros = data.total ?? items.length;
  const totalPages = Math.max(1, Math.ceil(totalRegistros / pageSize));
  const pageItems = items;
  const flujo = useMemo(() => derivarFlujoPedidos(workflow, checkout), [workflow, checkout]);
  const selectedMeta = estado ? getEstadoMeta(estado, flujo) : null;
  const tabsVisibles = useMemo(() => tabsPedidosVisibles(flujo, data.resumen), [flujo, data.resumen]);

  const abrirDetalle = async (id) => {
    setSelected(id);
    setDetalle(null);
    try {
      setDetalle(await ecommerceService.getPedido(id));
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudo abrir el pedido");
      setSelected(null);
    }
  };

  const avanzar = async (nextEstado) => {
    if (!detalle) return;
    if (nextEstado === "cancelado") {
      const ok = await confirm({
        title: "¿Cancelar pedido?",
        message: `Esta acción cancelará el pedido ${detalle.codigo}. El stock reservado será liberado y no podrá revertirse fácilmente.`,
        confirmText: "Sí, cancelar pedido",
        confirmColor: "error",
      });
      if (!ok) return;
    }
    setActing(true);
    try {
      const upd = await ecommerceService.cambiarEstadoPedido(detalle.id, nextEstado);
      setDetalle(upd);
      if (upd.aviso) toast.info(upd.aviso, { duration: 6000 });
      else toast.success(`Pedido ${upd.codigo}: ${getEstadoMeta(nextEstado, flujo).label}`);
      cargar(estado, query);
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudo cambiar el estado", { duration: 12000 });
    } finally {
      setActing(false);
    }
  };

  const reportarIncidencia = async () => {
    if (!detalle) return;
    const motivo = await prompt({
      title: "Reportar incidencia",
      message: `El pedido ${detalle.codigo} quedará congelado fuera del flujo. Un supervisor podrá reencauzarlo a preparación o cancelarlo.`,
      label: "Motivo de la incidencia",
      required: true,
    });
    if (motivo === null) return;
    setActing(true);
    try {
      const upd = await ecommerceService.reportarIncidencia(detalle.id, motivo);
      setDetalle(upd);
      toast.success(`Pedido ${upd.codigo}: incidencia registrada`);
      cargar(estado, query);
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudo registrar la incidencia", { duration: 12000 });
    } finally {
      setActing(false);
    }
  };

  const reintentarErp = async () => {
    if (!detalle) return;
    setActing(true);
    try {
      await ecommerceService.crearPedidoErp(detalle.id);
      toast.success("Pedido ERP creado y stock reservado");
      setDetalle(await ecommerceService.getPedido(detalle.id));
      cargar(estado, query);
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudo crear el pedido ERP", { duration: 12000 });
    } finally {
      setActing(false);
    }
  };

  return (
    <Stack spacing={2}>
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack direction="row" alignItems="flex-start" justifyContent="space-between" gap={2} useFlexGap flexWrap="wrap">
          <Box sx={{ minWidth: 0 }}>
            <Typography variant="subtitle1" fontWeight={700}>
              Pedidos ecommerce
            </Typography>
            <Typography variant="body2" color="text.secondary">
              Bandeja única para revisar pagos, tomar pedidos operativos, preparar, despachar y cerrar entregas.
            </Typography>
          </Box>
          <Stack direction="row" spacing={1} useFlexGap flexWrap="wrap" alignItems="center">
            <Chip size="small" icon={<Icon icon="lucide:lock-keyhole" width={14} />} label="Asignación segura" variant="outlined" />
            <Chip size="small" icon={<Icon icon="lucide:table-2" width={14} />} label="Vista tabla" color="primary" variant="outlined" />
            <Button
              size="small"
              variant="outlined"
              startIcon={exporting ? <CircularProgress size={14} /> : <Icon icon="lucide:file-down" width={16} />}
              onClick={exportarExcel}
              disabled={exporting}
            >
              Exportar Excel
            </Button>
          </Stack>
        </Stack>
      </Paper>

      {!loading && (
        <StatsGrid>
          {[
            {
              label: "Pendiente pago",
              value: data.resumen?.pendiente_pago || 0,
              icon: "lucide:clock-3",
              tone: "#f59e0b",
            },
            {
              label: "En control",
              value: (data.resumen?.pago_confirmado || 0) + (data.resumen?.en_verificacion || 0) + (data.resumen?.verificado || 0),
              icon: "lucide:clipboard-check",
              tone: "#2563eb",
            },
            {
              label: "Preparación y salida",
              value:
                (data.resumen?.preparando || 0) +
                (data.resumen?.empaquetado || 0) +
                (data.resumen?.listo_retiro || 0) +
                (data.resumen?.listo_despacho || 0) +
                (data.resumen?.en_camino || 0) +
                (data.resumen?.listo || 0),
              icon: "lucide:package-open",
              tone: "#7c3aed",
            },
            {
              label: "Incidencias",
              value: data.resumen?.incidencia || 0,
              icon: "lucide:triangle-alert",
              tone: "#ea580c",
            },
          ].map((item) => (
            <StatCard key={item.label} $tone={item.tone}>
              <IconBadge $tone={item.tone}>
                <Icon icon={item.icon} />
              </IconBadge>
              <Box>
                <Typography variant="h5" fontWeight={800}>
                  {item.value}
                </Typography>
                <Typography variant="caption" color="text.secondary">
                  {item.label}
                </Typography>
              </Box>
            </StatCard>
          ))}
        </StatsGrid>
      )}

      <OrderStateTabs>
        {tabsVisibles.map((tab) => {
          const active = estado === tab.value;
          const count = getPedidoCount(data.resumen, tab.value, items);
          return (
            <OrderStateButton key={tab.value || "todos"} type="button" $active={active} $tone={tab.tone} onClick={() => setEstado(tab.value)}>
              <Icon icon={tab.icon} />
              <span>{tab.label}</span>
              <strong>{count}</strong>
            </OrderStateButton>
          );
        })}
      </OrderStateTabs>

      <Paper variant="outlined" sx={{ overflow: "hidden" }}>
        <OrdersToolbar>
          <TextField
            size="small"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder="Buscar pedido, cliente o teléfono"
            sx={{ minWidth: 280, flex: 1 }}
          />
          {selectedMeta && (
            <PedidoStatusPill $tone={selectedMeta.tone} $bg={selectedMeta.bg}>
              <Icon icon={selectedMeta.icon} />
              <span>{selectedMeta.description}</span>
            </PedidoStatusPill>
          )}
          <Button size="small" variant="outlined" startIcon={<Icon icon="lucide:refresh-cw" />} onClick={() => cargar(estado, query)} disabled={loading}>
            Actualizar
          </Button>
        </OrdersToolbar>

        {loading ? (
          <Stack alignItems="center" sx={{ py: 7 }}>
            <CircularProgress size={28} />
          </Stack>
        ) : items.length === 0 ? (
          <EmptyState
            icon="lucide:inbox"
            title="Sin pedidos en esta vista"
            description="Ajustá el estado o la búsqueda para revisar otra cola operativa."
            compact
          />
        ) : (
          <>
            <OrdersTable>
              <OrdersTableHeader>
                <span>Pedido</span>
                <span>Fecha</span>
                <span>Cliente</span>
                <span>Total</span>
                <span>Estado</span>
                <span>ERP</span>
                <span>Acción</span>
              </OrdersTableHeader>
              {pageItems.map((p) => {
                const customer = p.customer || {};
                return (
                  // Toda la fila abre el detalle: a 1440 px el botón de la derecha quedaba
                  // cortado y había que hacer scroll horizontal para llegar.
                  <OrdersTableRow key={p.id} onClick={() => abrirDetalle(p.id)}>
                    <OrderCodeCell>
                      <strong>{p.codigo}</strong>
                    </OrderCodeCell>
                    <Typography variant="body2" color="text.secondary" sx={{ fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" }}>
                      {fmtFechaHora(p.created_at)}
                    </Typography>
                    <Box sx={{ minWidth: 0 }}>
                      <Typography
                        variant="body2"
                        fontWeight={700}
                        noWrap
                        title={customer.name || "Consumidor final"}
                      >
                        {customer.name || "Consumidor final"}
                      </Typography>
                      <Typography
                        variant="caption"
                        color="text.secondary"
                        noWrap
                        display="block"
                        title={customer.phone || customer.email || customer.document || "Sin contacto"}
                      >
                        {customer.phone || customer.email || customer.document || "Sin contacto"}
                      </Typography>
                    </Box>
                    <Box>
                      <Typography variant="body2" fontWeight={800}>
                        {fmtGs(p.totals?.total)}
                      </Typography>
                      <PaymentPill>
                        <Icon icon="lucide:credit-card" />
                        {nombreMedioPago(p.metodo_pago, p.totals?.paymentMethodName)}
                      </PaymentPill>
                    </Box>
                    <PedidoEstadoBadge estado={p.estado} flujo={flujo} />
                    {(() => {
                      const erp = estadoErp(p);
                      return (
                        <PedidoStatusPill $tone={erp.tone} $bg={erp.bg} title={erp.title}>
                          <Icon icon={erp.icon} />
                          <span>{erp.label}</span>
                        </PedidoStatusPill>
                      );
                    })()}
                    <OrderActionButton
                      type="button"
                      onClick={(e) => {
                        e.stopPropagation();
                        abrirDetalle(p.id);
                      }}
                    >
                      <Icon icon="lucide:panel-right-open" />
                      <span>Ver</span>
                    </OrderActionButton>
                  </OrdersTableRow>
                );
              })}
            </OrdersTable>

            <Box
              sx={{
                px: 1.5,
                pb: 1.5,
                borderTop: "1px solid",
                borderColor: "divider",
              }}
            >
              <TablePaginationBar
                page={page}
                totalPages={totalPages}
                onPageChange={setPage}
                total={totalRegistros}
                shown={pageItems.length}
                noun="pedidos"
                rowsPerPage={pageSize}
                onRowsPerPageChange={(n) => {
                  setPageSize(n);
                  setPage(1);
                }}
              />
            </Box>
          </>
        )}
      </Paper>

      <Dialog open={!!selected} onClose={() => setSelected(null)} maxWidth="md" fullWidth>
        {!detalle ? (
          <Box sx={{ p: 4, textAlign: "center" }}>
            <CircularProgress size={26} />
          </Box>
        ) : (
          <>
            <DialogTitle>
              <Stack direction="row" alignItems="center" justifyContent="space-between" gap={2}>
                <Stack direction="row" alignItems="center" spacing={1.25}>
                  <Icon icon="lucide:shopping-bag" />
                  <span>{detalle.codigo}</span>
                </Stack>
                <Stack direction="row" alignItems="center" spacing={1}>
                  <PedidoEstadoBadge estado={detalle.estado} flujo={flujo} />
                  {/* El diálogo solo se cerraba con Esc o clickeando afuera. */}
                  <IconButton size="small" aria-label="Cerrar" onClick={() => setSelected(null)}>
                    <Icon icon="lucide:x" width={18} />
                  </IconButton>
                </Stack>
              </Stack>
            </DialogTitle>
            <DialogContent dividers>
              {/* Un pedido congelado por incidencia mostraba los mismos botones que uno
                  normal ("Iniciar preparación"), sin decir que venía de una incidencia ni
                  cuál fue. Parecía que había que rehacer todo desde cero. */}
              {detalle.estado === "incidencia" && (
                <Alert severity="warning" sx={{ mb: 2 }} icon={<Icon icon="lucide:triangle-alert" width={18} />}>
                  <Typography variant="body2" fontWeight={800}>
                    Pedido congelado por una incidencia
                  </Typography>
                  {detalle.notas && (
                    <Typography variant="body2" sx={{ whiteSpace: "pre-wrap", mt: 0.5 }}>
                      {detalle.notas}
                    </Typography>
                  )}
                  <Typography variant="caption" color="text.secondary" display="block" sx={{ mt: 0.5 }}>
                    Está fuera del circuito hasta que lo reencauces. Con &ldquo;Iniciar preparación&rdquo;
                    vuelve al flujo desde donde estaba; con &ldquo;Cancelar pedido&rdquo; se anula y se
                    libera el stock reservado. El historial de abajo tiene el detalle completo.
                  </Typography>
                </Alert>
              )}
              <DetailGrid>
                <DetailSection>
                  <Typography variant="caption" color="text.secondary" fontWeight={800}>
                    CLIENTE
                  </Typography>
                  <Typography variant="body2" fontWeight={700}>
                    {detalle.customer?.name || "Consumidor final"}
                  </Typography>
                  <Typography variant="body2" color="text.secondary">
                    {[detalle.customer?.phone, detalle.customer?.email, detalle.customer?.document].filter(Boolean).join(" · ") || "Sin datos de contacto"}
                  </Typography>
                </DetailSection>

                <DetailSection>
                  <Typography variant="caption" color="text.secondary" fontWeight={800}>
                    ENTREGA
                  </Typography>
                  <Typography variant="body2" fontWeight={700}>
                    {detalle.delivery?.methodName || (detalle.delivery?.type === "pickup" ? "Retiro en tienda" : "Delivery")}
                  </Typography>
                  <Typography variant="body2" color="text.secondary">
                    {/* El checkout guarda nro de casa, distrito y coordenadas; antes se
                        mostraba solo la calle y el operador tenía que adivinar el resto. */}
                    {[
                      detalle.delivery?.address,
                      detalle.delivery?.nroCasa && detalle.delivery.nroCasa !== "0"
                        ? `Nro. ${detalle.delivery.nroCasa}`
                        : null,
                      detalle.delivery?.distrito,
                      detalle.delivery?.ciudad,
                      detalle.delivery?.departamento,
                    ]
                      .filter(Boolean)
                      .join(", ") || "Sin dirección registrada"}
                  </Typography>
                  {detalle.delivery?.lat != null && detalle.delivery?.lng != null && (
                    <Link
                      href={`https://www.google.com/maps/search/?api=1&query=${detalle.delivery.lat},${detalle.delivery.lng}`}
                      target="_blank"
                      rel="noreferrer"
                      variant="body2"
                      sx={{ fontWeight: 700, mt: 0.25, display: "inline-block" }}
                    >
                      Ver ubicación en el mapa
                    </Link>
                  )}
                  <Chip
                    size="small"
                    variant="outlined"
                    icon={<Icon icon={detalle.delivery_type === "pickup" ? "lucide:store" : "lucide:truck"} width={14} />}
                    label={detalle.delivery_type === "pickup" ? "Flujo de retiro" : "Flujo de delivery"}
                    sx={{ mt: 0.75 }}
                  />
                  {/* Indicaciones que escribió el comprador en el checkout. Van acá y no
                      entre las notas internas: es donde el operador busca cómo entregar. */}
                  {detalle.comentarios_cliente && (
                    <Alert severity="info" icon={<Icon icon="lucide:message-square" width={16} />} sx={{ mt: 1, py: 0.5 }}>
                      <Typography variant="caption" fontWeight={800} display="block">
                        INDICACIONES DEL CLIENTE
                      </Typography>
                      <Typography variant="body2" sx={{ whiteSpace: "pre-wrap" }}>
                        {detalle.comentarios_cliente}
                      </Typography>
                    </Alert>
                  )}
                </DetailSection>

                <DetailSection>
                  <Typography variant="caption" color="text.secondary" fontWeight={800}>
                    CONTROL OPERATIVO
                  </Typography>
                  {detalle.credito && (
                    <Alert
                      severity={["rechazada", "cancelada"].includes(detalle.credito.estado) ? "error" : detalle.credito.estado === "facturada" ? "success" : "info"}
                      icon={<Icon icon="lucide:calendar-clock" width={18} />}
                      sx={{ py: 0.5 }}
                      action={
                        !["facturada", "rechazada", "cancelada"].includes(detalle.credito.estado) ? (
                          <Button size="small" color="inherit" onClick={() => navigate("/solicitudes-credito")}>
                            Ver solicitudes
                          </Button>
                        ) : null
                      }
                    >
                      <Typography variant="body2" fontWeight={700}>
                        Compra en cuotas · {detalle.credito.cuotas ? `${detalle.credito.cuotas} cuotas · ` : ""}
                        {fmtGs(detalle.credito.monto)}
                      </Typography>
                      <Typography variant="caption" display="block">
                        {{
                          borrador: "La solicitud de crédito está en borrador: completala y enviala a aprobación en Ventas → Solicitudes de crédito.",
                          pendiente_aprobacion:
                            "Solicitud de crédito pendiente de aprobación (Ventas → Solicitudes de crédito). Mientras tanto la reserva de stock no vence.",
                          aprobada: "Crédito aprobado: facturalo desde el POS → «Solicitudes Crédito». Al facturar, el pedido pasa solo a pago confirmado.",
                          facturada: "Crédito facturado. Seguí con la preparación y la entrega como cualquier pedido.",
                          rechazada: `La solicitud de crédito fue rechazada${detalle.credito.motivo_rechazo ? `: ${detalle.credito.motivo_rechazo}` : ""}. El pedido se canceló y el stock se liberó.`,
                          cancelada: "La solicitud de crédito fue cancelada. El pedido se canceló y el stock se liberó.",
                        }[detalle.credito.estado] || `Solicitud de crédito: ${detalle.credito.estado}.`}
                      </Typography>
                    </Alert>
                  )}
                  {!detalle.credito && (
                  <DetailLine>
                    <span>Pedido ERP</span>
                    <PedidoStatusPill $tone={detalle.pedido_id ? "#16a34a" : "#f59e0b"} $bg={detalle.pedido_id ? "#f0fdf4" : "#fff7ed"}>
                      <Icon icon={detalle.pedido_id ? "lucide:check-circle-2" : "lucide:circle-dashed"} />
                      <span>{detalle.pedido_id ? "Creado y reservado" : "Pendiente"}</span>
                    </PedidoStatusPill>
                  </DetailLine>
                  )}
                  {(detalle.pedido_id || detalle.factura) && (
                    <DetailLine>
                      <span>Factura</span>
                      {(() => {
                        const erp = estadoErp(detalle);
                        return (
                          <PedidoStatusPill $tone={erp.tone} $bg={erp.bg} title={erp.title}>
                            <Icon icon={erp.icon} />
                            <span>{detalle.factura ? detalle.factura.numero : erp.label === "Sin facturar" ? "Sin facturar" : "Todavía no"}</span>
                          </PedidoStatusPill>
                        );
                      })()}
                    </DetailLine>
                  )}
                  <DetailLine>
                    <span>Pago</span>
                    <PaymentPill>
                      <Icon icon="lucide:credit-card" />
                      {nombreMedioPago(detalle.metodo_pago, detalle.totals?.paymentMethodName)}
                    </PaymentPill>
                  </DetailLine>
                  {detalle.notas ? (
                    <Typography variant="caption" color="text.secondary">
                      {detalle.notas}
                    </Typography>
                  ) : null}
                  {buildStoreUrl(storeConfig) && (
                    <Button
                      size="small"
                      variant="text"
                      startIcon={<Icon icon="lucide:link" width={14} />}
                      sx={{ alignSelf: "flex-start", mt: 0.5 }}
                      onClick={async () => {
                        const url = buildStoreUrl(storeConfig, `/?pedido=${detalle.codigo}`);
                        try {
                          await navigator.clipboard.writeText(url);
                          toast.success("Link de seguimiento del pedido copiado");
                        } catch {
                          toast.error("No se pudo copiar el link");
                        }
                      }}
                    >
                      Copiar link de seguimiento
                    </Button>
                  )}
                </DetailSection>

                <DetailSection>
                  <Typography variant="caption" color="text.secondary" fontWeight={800}>
                    TOTALES
                  </Typography>
                  <DetailLine>
                    <span>Subtotal</span>
                    <strong>{fmtGs(detalle.totals?.subtotal)}</strong>
                  </DetailLine>
                  {detalle.totals?.deliveryCost > 0 && (
                    <DetailLine>
                      <span>Envío</span>
                      <strong>{fmtGs(detalle.totals.deliveryCost)}</strong>
                    </DetailLine>
                  )}
                  <DetailLine>
                    <span>Total</span>
                    <strong>{fmtGs(detalle.totals?.total)}</strong>
                  </DetailLine>
                </DetailSection>
              </DetailGrid>

              <Box sx={{ mt: 2 }}>
                <Typography variant="caption" color="text.secondary" fontWeight={800}>
                  PRODUCTOS
                </Typography>
                <TableContainer component={Paper} variant="outlined" sx={{ mt: 1 }}>
                  <Table size="small">
                    <TableHead>
                      <TableRow>
                        <TableCell>Producto</TableCell>
                        <TableCell align="center" sx={{ width: 70 }}>Cant.</TableCell>
                        <TableCell align="right" sx={{ width: 130 }}>Precio unit.</TableCell>
                        <TableCell align="right" sx={{ width: 130 }}>Total</TableCell>
                      </TableRow>
                    </TableHead>
                    <TableBody>
                      {(detalle.items || []).map((it) => (
                        <TableRow key={it.id} hover>
                          <TableCell sx={{ maxWidth: 320 }}>
                            <Typography variant="body2" noWrap title={it.name}>
                              {it.name}
                            </Typography>
                            {it.sku && (
                              <Typography variant="caption" color="text.secondary">
                                Cód. {it.sku}
                              </Typography>
                            )}
                          </TableCell>
                          <TableCell align="center">{it.quantity}</TableCell>
                          <TableCell align="right">{fmtGs(it.unitPrice)}</TableCell>
                          <TableCell align="right">
                            <Typography variant="body2" fontWeight={700}>
                              {fmtGs(it.lineTotal)}
                            </Typography>
                          </TableCell>
                        </TableRow>
                      ))}
                      {(detalle.items || []).length === 0 && (
                        <TableRow>
                          <TableCell colSpan={4}>
                            <Typography variant="body2" color="text.secondary" align="center">
                              Sin productos registrados.
                            </Typography>
                          </TableCell>
                        </TableRow>
                      )}
                    </TableBody>
                  </Table>
                </TableContainer>
              </Box>

              {/* Reserva de stock. Se tomaba desde el checkout pero no se mostraba: el
                  operador leía "Creado y reservado" sin poder comprobar qué quedó tomado,
                  de qué depósito ni hasta cuándo. */}
              {(detalle.reservas || []).length > 0 && (
                <Box sx={{ mt: 2 }}>
                  <Typography variant="caption" color="text.secondary" fontWeight={800}>
                    STOCK RESERVADO
                  </Typography>
                  <TableContainer component={Paper} variant="outlined" sx={{ mt: 1 }}>
                    <Table size="small">
                      <TableHead>
                        <TableRow>
                          <TableCell>Producto</TableCell>
                          <TableCell>Depósito</TableCell>
                          <TableCell align="center" sx={{ width: 70 }}>Cant.</TableCell>
                          <TableCell sx={{ width: 150 }}>Estado</TableCell>
                        </TableRow>
                      </TableHead>
                      <TableBody>
                        {detalle.reservas.map((r, i) => (
                          <TableRow key={i} hover>
                            <TableCell sx={{ maxWidth: 280 }}>
                              <Typography variant="body2" noWrap title={r.producto || ""}>
                                {r.producto || "—"}
                              </Typography>
                              {r.sku && (
                                <Typography variant="caption" color="text.secondary">
                                  Cód. {r.sku}
                                </Typography>
                              )}
                            </TableCell>
                            <TableCell>
                              <Typography variant="body2">{r.deposito || "—"}</Typography>
                            </TableCell>
                            <TableCell align="center">{r.cantidad}</TableCell>
                            <TableCell>
                              <PedidoStatusPill
                                $tone={r.liberada ? "#64748b" : "#16a34a"}
                                $bg={r.liberada ? "#f1f5f9" : "#f0fdf4"}
                              >
                                <Icon icon={r.liberada ? "lucide:unlock" : "lucide:lock"} />
                                <span>
                                  {r.liberada
                                    ? "Liberada"
                                    : `Reservada${
                                        r.expiraAt
                                          ? ` · vence ${new Date(r.expiraAt).toLocaleString("es-PY", {
                                              day: "2-digit",
                                              month: "short",
                                              hour: "2-digit",
                                              minute: "2-digit",
                                            })}`
                                          : ""
                                      }`}
                                </span>
                              </PedidoStatusPill>
                            </TableCell>
                          </TableRow>
                        ))}
                      </TableBody>
                    </Table>
                  </TableContainer>
                </Box>
              )}

              {/* Bitácora del pedido. Cada transición ya se venía registrando en
                  ecommerce_pedido_evento, pero no la devolvía ningún endpoint: el operador
                  no tenía forma de ver quién movió el pedido ni con qué nota. */}
              {(detalle.eventos || []).length > 0 && (
                <Box sx={{ mt: 2 }}>
                  <Typography variant="caption" color="text.secondary" fontWeight={800}>
                    HISTORIAL
                  </Typography>
                  <Stack spacing={1} sx={{ mt: 1 }}>
                    {detalle.eventos.map((ev) => (
                      <Paper key={ev.id} variant="outlined" sx={{ p: 1.25 }}>
                        <Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap">
                          <PedidoEstadoBadge estado={ev.estadoNuevo || detalle.estado} flujo={flujo} />
                          {ev.estadoAnterior && (
                            <Typography variant="caption" color="text.secondary">
                              desde {getEstadoMeta(ev.estadoAnterior, flujo).label}
                            </Typography>
                          )}
                          <Box sx={{ flex: 1 }} />
                          <Typography variant="caption" color="text.secondary">
                            {ev.fecha
                              ? new Date(ev.fecha).toLocaleString("es-PY", {
                                  day: "2-digit",
                                  month: "short",
                                  hour: "2-digit",
                                  minute: "2-digit",
                                })
                              : ""}
                          </Typography>
                        </Stack>
                        {(ev.autor || ev.notas) && (
                          <Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
                            {[ev.autor, ev.notas].filter(Boolean).join(" · ")}
                          </Typography>
                        )}
                      </Paper>
                    ))}
                  </Stack>
                </Box>
              )}
            </DialogContent>
            <DialogActions sx={{ flexWrap: "wrap", gap: 1, px: 2, py: 1.5 }}>
              {detalle.estado === "pago_confirmado" && !detalle.pedido_id && !detalle.credito ? (
                <Tooltip title="El pedido en el ERP se crea solo al confirmarse el pago; este botón reintenta ese paso porque falló. No reemplaza a la verificación.">
                  <span>
                    <Button size="small" variant="outlined" color="warning" disabled={acting} onClick={reintentarErp} startIcon={<Icon icon="lucide:refresh-cw" width={14} />}>
                      Reintentar pedido ERP
                    </Button>
                  </span>
                </Tooltip>
              ) : null}
              {/* Entregar no factura: el botón lleva al POS con el pedido ERP cargado. */}
              {detalle.pedido_id && !detalle.factura && !["cancelado", "incidencia"].includes(detalle.estado) ? (
                <Tooltip title="Abre el POS con el pedido cargado (cliente e ítems) para emitir la factura.">
                  <span>
                    <Button
                      size="small"
                      variant={["entregado", "retirado"].includes(detalle.estado) ? "contained" : "outlined"}
                      color="success"
                      onClick={() => navigate(`/pos?pedido=${detalle.pedido_id}`)}
                      startIcon={<Icon icon="lucide:receipt" width={15} />}
                    >
                      Facturar
                    </Button>
                  </span>
                </Tooltip>
              ) : null}
              {(detalle.proximos_estados || []).map((nextEstado) => {
                // Facturado no se cancela desde acá (el backend lo rechaza): se revierte con
                // nota de crédito o anulación en Ventas. Antes el botón seguía activo y la
                // confirmación prometía liberar el stock.
                const bloqueadoPorFactura = nextEstado === "cancelado" && Boolean(detalle.factura);
                // Compra en cuotas: el pago se confirma solo al facturar la solicitud de crédito.
                const bloqueadoPorCredito = nextEstado === "pago_confirmado" && Boolean(detalle.credito);
                return (
                <Tooltip
                  key={nextEstado}
                  title={
                    bloqueadoPorFactura
                      ? `Ya tiene la factura ${detalle.factura.numero}. Para revertirlo emití una nota de crédito o la anulación desde Ventas.`
                      : bloqueadoPorCredito
                        ? "Compra en cuotas: se confirma sola cuando facturás la solicitud de crédito desde el POS."
                        : ACCION_ESTADO_AYUDA[nextEstado] || ""
                  }
                >
                  <span>
                    <Button
                      size="small"
                      variant={nextEstado === "cancelado" ? "outlined" : "contained"}
                      color={nextEstado === "cancelado" ? "error" : "primary"}
                      disabled={acting || bloqueadoPorFactura || bloqueadoPorCredito}
                      onClick={() => avanzar(nextEstado)}
                      startIcon={<Icon icon={getEstadoMeta(nextEstado).icon || "lucide:arrow-right"} width={15} />}
                    >
                      {ACCION_ESTADO_LABEL[nextEstado] || getEstadoMeta(nextEstado).label}
                    </Button>
                  </span>
                </Tooltip>
                );
              })}
              {!["pendiente_pago", "incidencia", "cancelado", "expirado", "entregado", "retirado"].includes(
                detalle.estado,
              ) && (
                <Button
                  size="small"
                  variant="outlined"
                  color="warning"
                  disabled={acting}
                  onClick={reportarIncidencia}
                  startIcon={<Icon icon="lucide:triangle-alert" width={15} />}
                  sx={{ ml: "auto" }}
                >
                  Reportar incidencia
                </Button>
              )}
              {(detalle.proximos_estados || []).length === 0 && detalle.estado !== "incidencia" && (
                <Typography variant="caption" color="text.secondary">
                  Estado final, sin acciones disponibles.
                </Typography>
              )}
              <Button size="small" onClick={() => setSelected(null)}>
                Cerrar
              </Button>
            </DialogActions>
          </>
        )}
      </Dialog>
      {dialogs}
    </Stack>
  );
}

export function Ecommerce() {
  const [searchParams, setSearchParams] = useSearchParams();
  const activeTab = getActiveTab(searchParams.get("tab"));

  // ── Estado de configuración ecommerce ──────────────────────────
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [publishing, setPublishing] = useState(false);
  const [versionesOpen, setVersionesOpen] = useState(false);
  const [dirty, setDirty] = useState(false);
  const [config, setConfig] = useState(null);
  const [draft, setDraft] = useState(null);
  const [opciones, setOpciones] = useState({
    sucursales: [],
    depositos: [],
    listas: [],
  });
  const [equipo, setEquipo] = useState(null);
  const [preparandoEquipo, setPreparandoEquipo] = useState(false);
  const [pagosEstado, setPagosEstado] = useState(null);
  const [cobertura, setCobertura] = useState(null);
  const navigate = useNavigate();

  useEffect(() => {
    let active = true;
    Promise.all([
      ecommerceService.getConfig(),
      ecommerceService.getOpciones().catch(() => null),
      ecommerceService.getEquipo().catch(() => null),
      ecommerceService.getPagosEstado().catch(() => null),
    ])
      .then(([data, opts, eq, pagos]) => {
        if (!active) return;
        setConfig(data);
        setDraft(buildDraftState(data));
        if (opts) setOpciones(opts);
        if (eq) setEquipo(eq);
        if (pagos) setPagosEstado(pagos);
      })
      .catch((err) => {
        toast.error(err.response?.data?.message || "No se pudo cargar la configuración ecommerce");
      })
      .finally(() => active && setLoading(false));
    return () => {
      active = false;
    };
  }, []);

  const handlePrepararEquipo = async () => {
    setPreparandoEquipo(true);
    try {
      const res = await ecommerceService.prepararEquipo();
      setEquipo(res);
      const nuevos = res.creados ?? 0;
      toast.success(
        nuevos > 0
          ? `Roles de la tienda listos (${nuevos} creado${nuevos === 1 ? "" : "s"})`
          : "Los roles de la tienda ya estaban creados",
      );
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudieron preparar los roles");
    } finally {
      setPreparandoEquipo(false);
    }
  };

  const patchDraft = (updater) => {
    setDraft((prev) => (prev ? updater(prev) : prev));
    setDirty(true);
  };

  const handleFieldChange = (field, value) => patchDraft((prev) => ({ ...prev, [field]: value }));
  const handleIdentityChange = (field, value) =>
    patchDraft((prev) => ({
      ...prev,
      identity: { ...prev.identity, [field]: value },
    }));
  const handleFaviconChange = (value) => patchDraft((prev) => ({ ...prev, favicon_url: value }));
  const handleThemeChange = (field, value) =>
    patchDraft((prev) => ({
      ...prev,
      theme: { ...prev.theme, [field]: value },
    }));
  const handleApplyPreset = (themeColors) =>
    patchDraft((prev) => ({
      ...prev,
      theme: { ...prev.theme, ...themeColors },
    }));
  const handleTypographyChange = (field, value) =>
    patchDraft((prev) => ({
      ...prev,
      typography: { ...prev.typography, [field]: value },
    }));
  const handleBannerEffectChange = (value) => patchDraft((prev) => ({ ...prev, bannerHoverEffect: value }));
  const handleLayoutChange = (field, value) =>
    patchDraft((prev) => ({
      ...prev,
      layout: { ...prev.layout, [field]: value },
    }));
  const handleCatalogChange = (field, value) =>
    patchDraft((prev) => ({
      ...prev,
      catalog: { ...prev.catalog, [field]: value },
    }));
  const handleTextsChange = (field, value) =>
    patchDraft((prev) => ({
      ...prev,
      texts: { ...(prev.texts ?? {}), [field]: value },
    }));
  const handleSeasonsChange = (seasons) => patchDraft((prev) => ({ ...prev, seasons }));
  const handlePagesChange = (contentPages) => patchDraft((prev) => ({ ...prev, contentPages }));
  const handleFooterChange = (footer) => patchDraft((prev) => ({ ...prev, footer }));
  const handlePromotionsChange = (field, value) =>
    patchDraft((prev) => ({
      ...prev,
      promotions: { ...prev.promotions, [field]: value },
    }));
  const handleDeliveryMethodsChange = (deliveryMethods) =>
    patchDraft((prev) => ({
      ...prev,
      checkout: { ...prev.checkout, deliveryMethods },
    }));
  const handleEnvioProductoChange = (envioProductoId) =>
    patchDraft((prev) => ({
      ...prev,
      checkout: { ...prev.checkout, envioProductoId: envioProductoId || undefined },
    }));
  const handleCheckoutLayoutChange = (layout) =>
    patchDraft((prev) => ({
      ...prev,
      checkout: { ...prev.checkout, layout },
    }));
  const handleCheckoutReservaChange = (campo, valor) =>
    patchDraft((prev) => ({
      ...prev,
      checkout: { ...prev.checkout, [campo]: valor === "" ? undefined : Number(valor) },
    }));
  const handlePaymentMethodsChange = (paymentMethods) =>
    patchDraft((prev) => ({
      ...prev,
      checkout: { ...prev.checkout, paymentMethods },
    }));
  const handleCreditoConfigChange = (creditoConfig) =>
    patchDraft((prev) => ({
      ...prev,
      checkout: { ...prev.checkout, creditoConfig },
    }));
  const handleWorkflowChange = (workflow) =>
    patchDraft((prev) => ({
      ...prev,
      workflow: normalizeWorkflowConfig(workflow),
    }));

  const validateDeliveryMethods = () => {
    const methods = draft?.checkout?.deliveryMethods ?? [];
    if (!methods.some((method) => method.enabled !== false)) {
      toast.error("Debe existir al menos un método de entrega activo");
      return false;
    }
    if (methods.some((method) => !method.name?.trim())) {
      toast.error("Todos los métodos de entrega deben tener un nombre");
      return false;
    }
    return true;
  };

  // Cobertura de stock según el depósito elegido (reactiva al dropdown, antes de
  // guardar): detecta el caso "depósito sin stock de productos publicados".
  useEffect(() => {
    let active = true;
    ecommerceService
      .getStockCobertura(draft?.deposito_id)
      .then((c) => active && setCobertura(c))
      .catch(() => undefined);
    return () => {
      active = false;
    };
  }, [draft?.deposito_id]);

  // Aviso al recargar/cerrar la pestaña con cambios sin guardar. La navegación
  // interna a otro módulo no se puede bloquear con este router (BrowserRouter
  // sin data router), por eso el chip de aviso es prominente.
  useEffect(() => {
    if (!dirty) return undefined;
    const handler = (e) => {
      e.preventDefault();
      e.returnValue = "";
    };
    window.addEventListener("beforeunload", handler);
    return () => window.removeEventListener("beforeunload", handler);
  }, [dirty]);

  const handleSaveDraft = async () => {
    if (!draft || !validateDeliveryMethods()) return;
    setSaving(true);
    try {
      const updated = await ecommerceService.saveDraft(serializeDraft(draft));
      setConfig(updated);
      setDirty(false);
      toast.success("Cambios guardados como borrador");
    } catch (err) {
      toast.error(err.response?.data?.message || "Error al guardar el borrador");
    } finally {
      setSaving(false);
    }
  };

  // Vista previa del borrador. Antes los cambios de diseño se editaban a ciegas: para
  // verlos había que publicarlos (en vivo para todos) y esperar la caché de la tienda.
  const [vistaPrevia, setVistaPrevia] = useState(null);
  const [vistaDispositivo, setVistaDispositivo] = useState("escritorio");
  const [abriendoVista, setAbriendoVista] = useState(false);
  const abrirVistaPrevia = async () => {
    if (!draft || !validateDeliveryMethods()) return;
    setAbriendoVista(true);
    try {
      // La tienda muestra lo guardado: si hay cambios, se guardan como borrador (no se publican).
      if (dirty) {
        const updated = await ecommerceService.saveDraft(serializeDraft(draft));
        setConfig(updated);
        setDirty(false);
      }
      const token = await ecommerceService.previewToken();
      setVistaPrevia(`${buildStoreUrl(config)}/?vista_previa=${encodeURIComponent(token)}`);
    } catch (err) {
      toast.error(err.response?.data?.message || "No se pudo abrir la vista previa");
    } finally {
      setAbriendoVista(false);
    }
  };

  const handlePublish = async () => {
    if (!draft || !validateDeliveryMethods()) return;
    setPublishing(true);
    try {
      if (dirty && draft) {
        const updated = await ecommerceService.saveDraft(serializeDraft(draft));
        setConfig(updated);
        setDirty(false);
      }
      const published = await ecommerceService.publish();
      setConfig(published);
      toast.success(`Tienda publicada (versión ${published.published_version})`);
    } catch (err) {
      toast.error(err.response?.data?.message || "Error al publicar");
    } finally {
      setPublishing(false);
    }
  };

  const handleTabChange = (_, value) => {
    setSearchParams(value === "pedidos" ? {} : { tab: value });
  };

  return (
    <Container maxWidth="xl" sx={{ py: 3 }}>
      <Stack spacing={2.5}>
        <HeaderBar>
          <Box sx={{ minWidth: 0 }}>
            <Stack direction="row" alignItems="center" spacing={1} sx={{ mb: 0.5 }}>
              <Typography variant="h5" fontWeight={800}>
                Ecommerce
              </Typography>
              <Chip size="small" label="MVP" color="primary" variant="outlined" />
            </Stack>
            <Typography variant="body2" color="text.secondary">
              Canal online multi tenant para catálogo, checkout, pedidos y operación post-compra.
            </Typography>
          </Box>
          <Stack direction="row" spacing={1} alignItems="center" useFlexGap flexWrap="wrap">
            {!config?.has_published ? (
              <Tooltip title="La tienda todavía no tiene ninguna versión publicada: los clientes ven un aviso de «Estamos preparando la tienda»." arrow>
                <Chip size="small" color="warning" variant="outlined" label="Sin publicar" />
              </Tooltip>
            ) : config?.cambios_sin_publicar && !dirty ? (
              <Tooltip title="Guardaste cambios que la tienda todavía no muestra. Revisalos con Vista previa y tocá Publicar para que los vean tus clientes." arrow>
                <Chip size="small" color="info" variant="outlined" icon={<Icon icon="lucide:file-pen-line" width={13} />} label="Borrador sin publicar" />
              </Tooltip>
            ) : (
              <Tooltip
                title={`Lo que ven tus clientes es la versión ${config?.published_version ?? ""}${config?.published_at ? `, publicada el ${fmtFechaHora(config.published_at)}` : ""}.`}
                arrow
              >
                <Chip
                  size="small"
                  color="success"
                  variant="outlined"
                  icon={<Icon icon="lucide:check-circle-2" width={14} />}
                  label={config?.published_version ? `Publicado · v${config.published_version}` : "Publicado"}
                />
              </Tooltip>
            )}
            {dirty && (
              <Tooltip title="Tenés cambios sin guardar. Guardá el borrador o publicá antes de salir del módulo o vas a perderlos." arrow>
                <Chip
                  size="small"
                  color="warning"
                  variant="outlined"
                  icon={<Icon icon="lucide:alert-triangle" width={13} />}
                  label="Cambios sin guardar"
                />
              </Tooltip>
            )}
            <Button
              variant="outlined"
              size="small"
              startIcon={<Icon icon="lucide:external-link" />}
              href={buildStoreUrl(config) || undefined}
              target="_blank"
              disabled={!buildStoreUrl(config)}
            >
              Ver tienda
            </Button>
            <Tooltip title="Mirá cómo queda la tienda con el borrador, en escritorio o celular, antes de publicar. Los clientes no lo ven." arrow>
              <span>
                <Button
                  variant="outlined"
                  size="small"
                  startIcon={abriendoVista ? <CircularProgress size={14} /> : <Icon icon="lucide:eye" />}
                  onClick={abrirVistaPrevia}
                  disabled={!buildStoreUrl(config) || abriendoVista}
                >
                  Vista previa
                </Button>
              </span>
            </Tooltip>
            <Tooltip title="Guarda tus cambios como borrador, sin publicarlos. La tienda sigue mostrando la última versión publicada." arrow>
              <span>
                <Button
                  variant="outlined"
                  size="small"
                  startIcon={saving ? <CircularProgress size={14} /> : <Icon icon="lucide:save" />}
                  onClick={handleSaveDraft}
                  disabled={loading || saving || !dirty}
                >
                  Guardar borrador
                </Button>
              </span>
            </Tooltip>
            {config?.has_published && (
              <Tooltip title="Historial de publicaciones: si algo salió mal, volvé a una versión anterior." arrow>
                <Button variant="outlined" size="small" startIcon={<Icon icon="lucide:history" />} onClick={() => setVersionesOpen(true)}>
                  Versiones
                </Button>
              </Tooltip>
            )}
            <Tooltip title="Guarda y publica: los cambios salen en vivo en la tienda para tus clientes." arrow>
              <span>
                <Button
                  variant="contained"
                  size="small"
                  startIcon={publishing ? <CircularProgress size={14} color="inherit" /> : <Icon icon="lucide:rocket" />}
                  onClick={handlePublish}
                  disabled={loading || publishing}
                >
                  Publicar
                </Button>
              </span>
            </Tooltip>
          </Stack>
        </HeaderBar>
        <VersionesDialog
          open={versionesOpen}
          onClose={() => setVersionesOpen(false)}
          versionActual={config?.published_version ?? undefined}
          hayCambiosSinGuardar={dirty}
          onRestaurada={(actualizado) => {
            setConfig(actualizado);
            setDraft(buildDraftState(actualizado));
            setDirty(false);
          }}
        />

        {/* Cuándo rige cada cosa. Antes decía que todo es borrador hasta publicar, pero el
            estado de la tienda rige al guardar y las imágenes de categorías, novedades y
            ofertas al instante. Cada sección lleva además su chip. */}
        {!["pedidos", "pagos", "seguimiento"].includes(activeTab) && (
        <Stack direction="row" spacing={1} alignItems="center" useFlexGap flexWrap="wrap" sx={{ px: 0.5, color: "text.secondary" }}>
          <Typography variant="caption" fontWeight={600}>
            Cuándo se aplica lo que cambiás:
          </Typography>
          {[
            ["publicar", "casi todo: diseño, catálogo, promociones, checkout y emails"],
            ["guardar", "estado y dirección de la tienda"],
            ["instante", "imágenes de categorías, novedades, ofertas y equipo"],
          ].map(([k, texto]) => (
            <Tooltip key={k} title={CUANDO_RIGE[k].tip} arrow enterDelay={300}>
              <Stack direction="row" spacing={0.5} alignItems="center" sx={{ cursor: "help" }}>
                <Icon icon={CUANDO_RIGE[k].icon} width={13} color={CUANDO_RIGE[k].color} />
                <Typography variant="caption">
                  <strong style={{ color: CUANDO_RIGE[k].color }}>{CUANDO_RIGE[k].label}:</strong> {texto}
                </Typography>
              </Stack>
            </Tooltip>
          ))}
        </Stack>
        )}

        {/*
          Guía contextual: cambia con la pestaña. El `key` la remonta al cambiar
          de tab, así el colapsable arranca cerrado en cada una en vez de quedar
          abierto mostrando el contenido de la anterior.
        */}
        {ECOMMERCE_GUIAS[activeTab] && <ScreenGuia key={activeTab} moduloPadre="Ecommerce" {...ECOMMERCE_GUIAS[activeTab]} />}

        {!loading && (
          <PrereqChecklist
            title="Primeros pasos para publicar tu tienda"
            items={[
              {
                key: "subdominio",
                label: "1. Subdominio configurado",
                ok: !!config?.subdominio,
                hint: config?.subdominio ? config.subdominio : "Asigná un subdominio en Configuración",
                ctaLabel: "Configuración",
                onGoTo: () => handleTabChange(null, "configuracion"),
              },
              (() => {
                const dep = draft?.deposito_id;
                const cob = cobertura;
                const sinStock = !!dep && cob && cob.publicados > 0 && cob.conStock === 0;
                let hint;
                if (!dep) {
                  hint = "Elegí el depósito desde el que se reserva stock en el checkout. La lista de precios es opcional.";
                } else if (!cob) {
                  hint = "La tienda reserva stock desde este depósito. La lista de precios es opcional (vacío = precio base).";
                } else if (cob.publicados === 0) {
                  hint = "No hay productos marcados 'mostrar en ecommerce'. Marcalos en el catálogo del ERP para que aparezcan en la tienda.";
                } else if (cob.conStock === 0) {
                  const sug = cob.mejorDeposito
                    ? ` Probá con "${cob.mejorDeposito.nombre}" (${cob.mejorDeposito.conStock} con stock).`
                    : "";
                  hint = `Este depósito no tiene stock de los ${cob.publicados} productos publicados: el catálogo sale vacío.${sug}`;
                } else {
                  hint = `${cob.conStock} de ${cob.publicados} productos publicados tienen stock en este depósito.`;
                }
                return {
                  key: "deposito",
                  label: "2. Depósito con stock para la tienda",
                  ok: !!dep && !sinStock,
                  hint,
                  ctaLabel: "Configuración",
                  onGoTo: () => handleTabChange(null, "configuracion"),
                };
              })(),
              {
                key: "roles",
                label: "3. Roles del equipo listos",
                ok: !!equipo?.listos,
                hint: equipo?.listos
                  ? "Operador, Despacho y Mostrador creados. Asigná los usuarios en Seguridad."
                  : "Prepará los roles de la tienda desde Configuración → Equipo y permisos",
                ctaLabel: "Configuración",
                onGoTo: () => handleTabChange(null, "configuracion"),
              },
              (() => {
                const pagoRequerido = !!draft?.checkout?.paymentRequired;
                const pe = pagosEstado;
                const listo = !!(pe?.activo && pe?.configurado);
                let hint;
                if (!pagoRequerido) {
                  hint = "Pago online desactivado en el checkout: cobrás contra entrega o transferencia. Opcional.";
                } else if (!pe) {
                  hint = "Verificando el estado de Novasis Pay…";
                } else if (listo) {
                  const partes = [`Cuenta activa (modo ${pe.mode === "live" ? "producción" : "pruebas"})`];
                  if (pe.mode !== "live") partes.push("pasá a producción para cobrar real");
                  if (!pe.tieneWebhook) partes.push("falta la clave de notificaciones (webhook)");
                  hint = partes.join(" · ");
                } else {
                  hint = "Configurá y activá tu cuenta de pagos en la pantalla Novasis Pay.";
                }
                return {
                  key: "novasis_pay",
                  label: pagoRequerido ? "4. Novasis Pay (pago online)" : "4. Novasis Pay (pago online, opcional)",
                  ok: !pagoRequerido || listo,
                  hint,
                  ctaLabel: "Novasis Pay",
                  onGoTo: () => navigate("/novasis-pay"),
                };
              })(),
              {
                key: "publicado",
                label: "5. Tienda publicada",
                ok: !!config?.has_published,
                hint: "Guardá el borrador y usá el botón Publicar",
              },
              {
                key: "logo",
                label: "6. Logo de la tienda",
                ok: !!draft?.identity?.logoUrl,
                hint: draft?.identity?.logoUrl
                  ? "Cargado"
                  : "Sin logo la tienda muestra un cuadrado con la inicial y la pestaña del navegador un ícono genérico",
                ctaLabel: "Diseño",
                onGoTo: () => handleTabChange(null, "diseno"),
              },
              {
                key: "admin_email",
                label: "7. Email para avisos de pedidos",
                ok: !!draft?.notifications?.adminEmail?.trim(),
                hint: draft?.notifications?.adminEmail?.trim()
                  ? draft.notifications.adminEmail
                  : "Sin este email no te llegan avisos de pedidos nuevos",
                ctaLabel: "Notificaciones",
                onGoTo: () => handleTabChange(null, "notificaciones"),
              },
              (() => {
                const ejemplo = camposDeEjemplo(draft?.footer);
                return {
                  key: "datos_ejemplo",
                  label: "8. Datos de contacto propios",
                  ok: ejemplo.length === 0,
                  hint: ejemplo.length ? `Siguen los de ejemplo: ${ejemplo.join(", ")}` : "Contacto, local y redes cargados",
                  ctaLabel: "Páginas",
                  onGoTo: () => handleTabChange(null, "paginas"),
                };
              })(),
            ]}
            collapseWhenDone
            compact={activeTab !== "configuracion"}
          />
        )}

        <Paper variant="outlined" sx={{ overflow: "hidden" }}>
          <Tabs value={activeTab} onChange={handleTabChange} variant="scrollable" scrollButtons="auto" sx={{ px: 1, borderBottom: 1, borderColor: "divider" }}>
            {TABS.map((tab) => (
              <Tab key={tab.value} value={tab.value} label={tab.label} icon={<Icon icon={tab.icon} width={16} />} iconPosition="start" />
            ))}
          </Tabs>
        </Paper>

        {activeTab === "novedades" && <NovedadesPanel />}
        {activeTab === "seguimiento" && <SeguimientoPanel storeConfig={config} />}
        {activeTab === "paginas" &&
          (loading || !draft ? (
            <Stack alignItems="center" sx={{ py: 6 }}>
              <CircularProgress size={28} />
            </Stack>
          ) : (
            <Stack spacing={2}>
              <FooterConfigPanel
                footer={draft.footer}
                pages={draft.contentPages}
                onFooterChange={handleFooterChange}
                identity={draft.identity}
                onIdentityChange={handleIdentityChange}
              />
              <Paper variant="outlined" sx={{ p: 2 }}>
                <SeccionTienda titulo="Páginas de contenido" donde="Páginas que se abren desde los links del pie" zona="footer" cuando="publicar">
                  <Typography variant="body2" color="text.secondary">
                    Términos, privacidad, cambios y devoluciones, preguntas frecuentes y las que agregues. Cada una se abre desde un
                    link del pie de página (columnas «Comprar», «Soporte» y «Legal»).
                  </Typography>
                </SeccionTienda>
              </Paper>
              <PaginasPanel
                pages={draft.contentPages}
                onPagesChange={handlePagesChange}
                accent={draft.theme?.accent}
                primary={draft.theme?.primary}
              />
            </Stack>
          ))}
        {activeTab === "promociones" && (
          <Stack spacing={2}>
            {!loading && draft && (
              <PromotionsContentPanel promotions={draft.promotions} onPromotionChange={handlePromotionsChange} seasons={draft.seasons} />
            )}
            <Paper variant="outlined" sx={{ p: 2 }}>
              <SeccionTienda titulo="Ofertas de productos" donde="Precio tachado en tarjetas y fichas, y descuento al cobrar" zona="tarjetas" cuando="instante">
                <Typography variant="body2" color="text.secondary">
                  Descuentos por producto, categoría o marca, con vigencia. Son las mismas ofertas de Inventario → Ofertas: lo que
                  cambies acá también vale para los pedidos del ERP. Rigen apenas las guardás, sin publicar la tienda.
                </Typography>
              </SeccionTienda>
            </Paper>
            <OfertasTab />
          </Stack>
        )}

        {activeTab === "configuracion" &&
          (loading || !draft ? (
            <Stack alignItems="center" sx={{ py: 6 }}>
              <CircularProgress size={28} />
            </Stack>
          ) : (
            <ConfiguracionPanel
              draft={draft}
              opciones={opciones}
              onChange={handleFieldChange}
              onDeliveryMethodsChange={handleDeliveryMethodsChange}
              onEnvioProductoChange={handleEnvioProductoChange}
              onPaymentMethodsChange={handlePaymentMethodsChange}
              onCheckoutLayoutChange={handleCheckoutLayoutChange}
              onCheckoutReservaChange={handleCheckoutReservaChange}
              onCreditoConfigChange={handleCreditoConfigChange}
              onWorkflowChange={handleWorkflowChange}
              equipo={equipo}
              preparandoEquipo={preparandoEquipo}
              onPrepararEquipo={handlePrepararEquipo}
              cobertura={cobertura}
            />
          ))}
        {activeTab === "diseno" &&
          (loading || !draft ? (
            <Stack alignItems="center" sx={{ py: 6 }}>
              <CircularProgress size={28} />
            </Stack>
          ) : (
            <Stack spacing={2}>
              <DesignPanel
                identity={draft.identity}
                faviconUrl={draft.favicon_url}
                theme={draft.theme}
                typography={draft.typography}
                layout={draft.layout}
                selectedBannerEffect={draft.bannerHoverEffect}
                seasons={draft.seasons}
                onIdentityChange={handleIdentityChange}
                onFaviconChange={handleFaviconChange}
                onThemeChange={handleThemeChange}
                onTypographyChange={handleTypographyChange}
                onLayoutChange={handleLayoutChange}
                onBannerEffectChange={handleBannerEffectChange}
                onApplyPreset={handleApplyPreset}
              />
              <SeasonsPanel seasons={draft.seasons} onSeasonsChange={handleSeasonsChange} />
            </Stack>
          ))}
        {activeTab === "catalogo" &&
          (loading || !draft ? (
            <Stack alignItems="center" sx={{ py: 6 }}>
              <CircularProgress size={28} />
            </Stack>
          ) : (
            <Stack spacing={2}>
              <CatalogPanel catalog={draft.catalog} onCatalogChange={handleCatalogChange} />
              <StoreTextsPanel texts={draft.texts} onTextsChange={handleTextsChange} />
            </Stack>
          ))}
        {activeTab === "pedidos" && <PedidosPanel workflow={draft?.workflow} checkout={draft?.checkout} storeConfig={config} />}
        {activeTab === "pagos" && <PagosPanel />}
        {activeTab === "notificaciones" && draft && (
          <NotificacionesPanel
            notifications={draft.notifications}
            onChange={(notifications) => patchDraft((prev) => ({ ...prev, notifications }))}
            storeName={draft.identity?.name}
            primaryColor={draft.theme?.primary}
          />
        )}
      </Stack>
      <Dialog open={!!vistaPrevia} onClose={() => setVistaPrevia(null)} fullWidth maxWidth="xl">
        <DialogTitle>
          <Stack direction="row" alignItems="center" spacing={1.5} flexWrap="wrap" useFlexGap>
            <Typography variant="subtitle1" fontWeight={700} sx={{ flex: 1 }}>
              Vista previa del borrador
            </Typography>
            <ToggleButtonGroup
              size="small"
              exclusive
              value={vistaDispositivo}
              onChange={(_, v) => v && setVistaDispositivo(v)}
            >
              <ToggleButton value="escritorio">
                <Icon icon="lucide:monitor" width={16} style={{ marginRight: 6 }} /> Escritorio
              </ToggleButton>
              <ToggleButton value="celular">
                <Icon icon="lucide:smartphone" width={16} style={{ marginRight: 6 }} /> Celular
              </ToggleButton>
            </ToggleButtonGroup>
            <Button size="small" href={vistaPrevia || undefined} target="_blank" startIcon={<Icon icon="lucide:external-link" />}>
              Abrir en otra pestaña
            </Button>
            <IconButton aria-label="Cerrar" onClick={() => setVistaPrevia(null)}>
              <Icon icon="lucide:x" width={18} />
            </IconButton>
          </Stack>
        </DialogTitle>
        <DialogContent dividers sx={{ bgcolor: "action.hover", display: "flex", justifyContent: "center", p: 2 }}>
          {vistaPrevia && (
            <iframe
              key={vistaDispositivo}
              title="Vista previa de la tienda"
              src={vistaPrevia}
              style={{
                width: vistaDispositivo === "celular" ? 390 : "100%",
                height: "75vh",
                border: "1px solid rgba(148,163,184,.4)",
                borderRadius: vistaDispositivo === "celular" ? 24 : 8,
                background: "#fff",
              }}
            />
          )}
        </DialogContent>
      </Dialog>
    </Container>
  );
}

const HeaderBar = styled.div`
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;

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

const StatsGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;

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

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

const StatCard = styled(Paper).attrs({ variant: "outlined" })`
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px;
  border-left: 3px solid ${({ $tone }) => $tone};
`;

const IconBadge = styled.div`
  width: 42px;
  height: 42px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  background: ${({ $tone }) => `${$tone}18`};
  color: ${({ $tone }) => $tone};
  flex: 0 0 auto;

  svg {
    width: 21px;
    height: 21px;
  }
`;

const ContentGrid = styled.div`
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(300px, 0.7fr);
  gap: 14px;

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




const TimelineRow = styled.div`
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};

  &:last-child {
    border-bottom: 0;
  }
`;

// Botones de ancho automático que se acomodan en filas. En una grilla de columnas
// iguales las etiquetas se cortaban («Pendie…», «Listo d…», «Cancel…»).
const OrderStateTabs = styled.div`
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
`;

const OrderStateButton = styled.button`
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 8px 12px;
  border: 1px solid ${({ $active, $tone, theme }) => ($active ? $tone : theme.bg4 || "rgba(148, 163, 184, 0.35)")};
  border-radius: 8px;
  background: ${({ $active, $tone, theme }) => ($active ? `${$tone}18` : theme.bg2 || "#fff")};
  color: ${({ $active, $tone, theme }) => ($active ? $tone : theme.textSecondary || theme.color1 || "#334155")};
  cursor: pointer;
  text-align: left;
  transition:
    border-color 0.16s ease,
    background 0.16s ease,
    transform 0.16s ease;

  &:hover {
    border-color: ${({ $tone }) => $tone};
    transform: translateY(-1px);
  }

  svg {
    width: 18px;
    height: 18px;
  }

  span {
    color: ${({ theme }) => theme.text || "#0f172a"};
    font-size: 12px;
    font-weight: 800;
    white-space: nowrap;
  }

  strong {
    min-width: 28px;
    padding: 3px 7px;
    border-radius: 999px;
    background: ${({ $tone }) => `${$tone}16`};
    color: ${({ $tone }) => $tone};
    font-size: 11px;
    text-align: center;
  }
`;

const OrdersToolbar = styled.div`
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  border-bottom: 1px solid ${({ theme }) => theme.bg4 || "rgba(148, 163, 184, 0.24)"};

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

const OrdersTable = styled.div`
  overflow-x: auto;
`;

const OrdersTableHeader = styled.div`
  display: grid;
  grid-template-columns: 124px 112px minmax(170px, 1fr) 128px 150px 132px 96px;
  gap: 12px;
  min-width: 960px;
  padding: 10px 16px;
  border-bottom: 1px solid ${({ theme }) => theme.bg4 || "rgba(148, 163, 184, 0.24)"};
  background: ${({ theme }) => theme.bg3 || "#f8fafc"};
  color: ${({ theme }) => theme.textSecondary || theme.color1 || "#64748b"};
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
`;

const OrdersTableRow = styled.div`
  display: grid;
  grid-template-columns: 124px 112px minmax(170px, 1fr) 128px 150px 132px 96px;
  gap: 12px;
  align-items: center;
  min-width: 960px;
  padding: 14px 16px;
  border-bottom: 1px solid ${({ theme }) => theme.bg4 || "rgba(148, 163, 184, 0.18)"};

  &:last-child {
    border-bottom: 0;
  }

  cursor: pointer;

  &:hover {
    background: ${({ theme }) => theme.bg3 || "#f8fafc"};
  }
`;

const OrderCodeCell = styled.div`
  display: grid;
  gap: 2px;

  strong {
    color: ${({ theme }) => theme.text || "#0f172a"};
    font-size: 13px;
  }

  small {
    color: ${({ theme }) => theme.textSecondary || theme.color1 || "#64748b"};
    font-size: 11px;
  }
`;

const PedidoStatusPill = styled.span`
  display: inline-flex;
  align-items: center;
  width: fit-content;
  max-width: 100%;
  gap: 6px;
  padding: 5px 9px;
  border: 1px solid ${({ $tone }) => `${$tone}40`};
  border-radius: 999px;
  background: ${({ $bg }) => $bg};
  color: ${({ $tone }) => $tone};
  font-size: 11px;
  font-weight: 800;
  line-height: 1.2;

  svg {
    width: 14px;
    height: 14px;
    flex: 0 0 auto;
  }

  span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
`;

const PaymentPill = styled.span`
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 3px;
  color: ${({ theme }) => theme.textSecondary || theme.color1 || "#64748b"};
  font-size: 11px;
  font-weight: 700;
  text-transform: capitalize;

  svg {
    width: 13px;
    height: 13px;
  }
`;

const OrderActionButton = styled.button`
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 34px;
  padding: 0 11px;
  border: 1px solid ${({ theme }) => `${theme.primary || "#2563eb"}55`};
  border-radius: 999px;
  background: ${({ theme }) => `${theme.primary || "#2563eb"}16`};
  color: ${({ theme }) => theme.primary || "#1d4ed8"};
  cursor: pointer;
  font-size: 12px;
  font-weight: 800;

  &:hover {
    border-color: ${({ theme }) => theme.primary || "#2563eb"};
    background: ${({ theme }) => `${theme.primary || "#2563eb"}24`};
  }

  svg {
    width: 15px;
    height: 15px;
  }
`;

const DetailGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;

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

const DetailSection = styled.div`
  display: grid;
  align-content: start;
  gap: 6px;
  min-height: 94px;
  padding: 12px;
  border: 1px solid ${({ theme }) => theme.bg4 || "rgba(148, 163, 184, 0.24)"};
  border-radius: 8px;
  background: ${({ theme }) => theme.bg2 || "#fff"};
`;

const DetailLine = styled.div`
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  color: ${({ theme }) => theme.textSecondary || theme.color1 || "#475569"};
  font-size: 13px;

  strong {
    color: ${({ theme }) => theme.text || "#0f172a"};
  }
`;

const EffectOptionsGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 10px;
`;

const EffectOption = styled.button`
  display: grid;
  grid-template-columns: 54px minmax(0, 1fr);
  grid-template-rows: auto auto;
  gap: 4px 10px;
  align-items: center;
  min-height: 82px;
  padding: 10px;
  border: 1px solid ${({ $active, theme }) => ($active ? theme.primary || "#2563eb" : theme.bg4 || "#e5e7eb")};
  border-radius: 8px;
  background: ${({ $active, theme }) => ($active ? `${theme.primary || "#2563eb"}16` : theme.bg2 || "#fff")};
  color: ${({ theme }) => theme.text || "inherit"};
  text-align: left;
  cursor: pointer;

  span {
    font-size: 13px;
    font-weight: 700;
  }

  small {
    grid-column: 2;
    color: ${({ theme }) => theme.textSecondary || theme.color1 || "#64748b"};
    font-size: 11px;
    line-height: 1.35;
  }

  &:hover {
    border-color: ${({ theme }) => theme.primary || "#2563eb"};
  }
`;

const PresetSwatch = styled.button`
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 104px;
  padding: 10px 12px;
  border: 1.5px solid ${(p) => (p.$active ? p.theme.primary || "#1976d2" : p.theme.bg4 || "rgba(0,0,0,0.12)")};
  border-radius: 10px;
  background: ${(p) => (p.$active ? `${p.theme.primary || "#1976d2"}16` : p.theme.bg2 || "#fff")};
  color: ${({ theme }) => theme.text || "#111827"};
  cursor: pointer;
  transition:
    border-color 0.15s ease,
    transform 0.15s ease;

  &:hover {
    transform: translateY(-1px);
  }

  .swatches {
    display: grid;
    grid-template-columns: repeat(3, 16px);
    gap: 3px;
  }

  .swatches > span {
    width: 16px;
    height: 16px;
    border-radius: 4px;
  }

  small {
    font-size: 11px;
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    color: ${({ theme }) => theme.text || "#374151"};
    white-space: normal;
    overflow-wrap: anywhere;
  }
`;

const ColorGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 10px;
`;

const ColorField = styled.label`
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid ${({ theme }) => theme.bg4 || "rgba(0, 0, 0, 0.12)"};
  border-radius: 10px;
  background: ${({ theme }) => theme.bg2 || "transparent"};
  color: ${({ theme }) => theme.text || "inherit"};
  cursor: pointer;

  input[type="color"] {
    width: 36px;
    height: 36px;
    padding: 0;
    border: none;
    border-radius: 8px;
    background: none;
    cursor: pointer;
  }

  div {
    display: grid;
    min-width: 0;
  }

  strong {
    font-size: 13px;
    font-weight: 700;
  }

  small {
    font-size: 11px;
    color: ${({ theme }) => theme.textSecondary || theme.color1 || "#6b7280"};
    text-transform: uppercase;
  }
`;

const FontPreview = styled.div`
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border: 1px dashed ${({ theme }) => theme.bg4 || "rgba(0, 0, 0, 0.18)"};
  border-radius: 10px;
  color: ${({ theme }) => theme.text || "inherit"};
  font-size: 14px;

  strong {
    font-size: 22px;
    font-weight: 800;
  }
`;

const EffectPreview = styled.div`
  position: relative;
  grid-row: 1 / span 2;
  width: 54px;
  height: 54px;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: 8px;
  background: linear-gradient(135deg, #0f766e, #38bdf8);
  color: #fff;

  svg {
    position: relative;
    z-index: 3;
    width: 22px;
    height: 22px;
  }

  &::before,
  &::after {
    content: "";
    position: absolute;
    inset: 8px;
    z-index: 2;
    border: 1px solid rgba(255, 255, 255, 0.72);
    opacity: 0;
    transition: all 0.25s ease;
  }

  ${EffectOption}:hover &[data-effect="effect-1"] {
    opacity: 0.78;
  }

  ${EffectOption}:hover &[data-effect="effect-2"]::before {
    inset: 0;
    background: rgba(255, 255, 255, 0.28);
    opacity: 1;
  }

  ${EffectOption}:hover &[data-effect="effect-3"]::before {
    inset: -16px;
    border-radius: 999px;
    opacity: 1;
  }

  ${EffectOption}:hover &[data-effect="effect-4"]::before {
    inset: -18px;
    border-width: 14px 0;
    transform: rotate(45deg);
    opacity: 1;
  }

  ${EffectOption}:hover &[data-effect="effect-5"]::before {
    inset: -12px;
    border: 14px double rgba(255, 255, 255, 0.58);
    opacity: 1;
  }

  ${EffectOption}:hover &[data-effect="effect-6"] {
    transform: scale(1.03);
    filter: brightness(0.86);
  }

  ${EffectOption}:hover &[data-effect="effect-7"]::before {
    inset: 0;
    background: rgba(0, 0, 0, 0.24);
    opacity: 1;
  }

  ${EffectOption}:hover &[data-effect="effect-8"]::before,
  ${EffectOption}:hover &[data-effect="effect-8"]::after {
    opacity: 1;
    transform: scale(1);
  }

  &[data-effect="effect-8"]::before {
    transform: scale(0, 1);
  }

  &[data-effect="effect-8"]::after {
    transform: scale(1, 0);
  }

  ${EffectOption}:hover &[data-effect="effect-9"]::before {
    inset: 0;
    background: rgba(0, 0, 0, 0.28);
    opacity: 1;
  }

  ${EffectOption}:hover &[data-effect="effect-10"]::before {
    inset: 0;
    background: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.34));
    opacity: 1;
  }

  &[data-effect="effect-11"]::before,
  &[data-effect="effect-11"]::after {
    inset: auto;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    border: 0;
    background: rgba(0, 0, 0, 0.24);
    opacity: 1;
    transform-origin: top left;
  }

  &[data-effect="effect-11"]::after {
    top: auto;
    right: 0;
    bottom: 0;
    left: auto;
    transform-origin: bottom right;
  }

  ${EffectOption}:hover &[data-effect="effect-11"]::before,
  ${EffectOption}:hover &[data-effect="effect-11"]::after {
    width: 100%;
    height: 100%;
  }
`;

const BannerPreview = styled.div`
  position: relative;
  min-height: 220px;
  overflow: hidden;
  border-radius: 10px;
  background: #000;

  img {
    width: 100%;
    height: 220px;
    object-fit: cover;
    display: block;
    opacity: 0.82;
    transition:
      opacity 0.35s ease,
      transform 0.35s ease;
  }

  div {
    position: absolute;
    right: 18px;
    bottom: 16px;
    z-index: 4;
    display: grid;
    justify-items: end;
    color: #fff;
    text-align: right;
    text-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
  }

  span {
    color: #38bdf8;
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
  }

  strong {
    font-size: 24px;
    font-weight: 800;
  }

  &::before,
  &::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
  }

  &:hover[data-effect="effect-1"] img {
    opacity: 0.64;
  }

  &[data-effect="effect-2"]::before,
  &[data-effect="effect-2"]::after {
    left: -100%;
    background: rgba(255, 255, 255, 0.32);
    opacity: 0;
    transition: all 0.3s ease-in;
  }

  &:hover[data-effect="effect-2"]::before,
  &:hover[data-effect="effect-2"]::after {
    left: 0;
    opacity: 1;
  }

  &[data-effect="effect-5"]::before,
  &[data-effect="effect-5"]::after {
    border: 200px double transparent;
    opacity: 1;
    visibility: visible;
    transition: all 0.4s cubic-bezier(0.94, 0.85, 0.1, 0.62);
  }

  &:hover[data-effect="effect-5"]::before,
  &:hover[data-effect="effect-5"]::after {
    border: 0 double rgba(255, 255, 255, 0.7);
    opacity: 0;
    visibility: hidden;
  }

  /* effect-3: Anillo central — colapsa hacia adentro al hacer hover */
  &[data-effect="effect-3"]::before,
  &[data-effect="effect-3"]::after {
    width: 100px;
    height: 100px;
    margin: auto;
    box-sizing: border-box;
    border: 50px solid transparent;
    border-radius: 50%;
    opacity: 1;
    transform: scale(4);
    transition:
      border 0.4s ease-in-out,
      opacity 0.4s ease-in-out;
  }

  &:hover[data-effect="effect-3"]::before,
  &:hover[data-effect="effect-3"]::after {
    border: 0 solid rgba(0, 0, 0, 0.6);
    opacity: 0;
  }

  /* effect-4: Corte diagonal — bandas diagonales que aparecen */
  &[data-effect="effect-4"]::before,
  &[data-effect="effect-4"]::after {
    border-top: 50px solid rgba(0, 0, 0, 0.25);
    border-bottom: 50px solid rgba(0, 0, 0, 0.25);
    opacity: 0;
    transform: rotate(45deg) scale(1, 0);
    transition:
      opacity 0.4s ease,
      transform 0.35s ease;
  }

  &:hover[data-effect="effect-4"]::before,
  &:hover[data-effect="effect-4"]::after {
    opacity: 1;
    transform: rotate(45deg) scale(1, 4);
  }

  /* effect-6: Zoom oscuro */
  &:hover[data-effect="effect-6"] img {
    transform: scale(1.08);
  }

  /* effect-7: Cortina central — overlay que se expande desde el centro */
  &[data-effect="effect-7"]::before {
    inset: auto;
    top: 50%;
    left: 50%;
    width: 0;
    height: 0;
    background: rgba(0, 0, 0, 0.3);
    transform: translate(-50%, -50%);
    transition:
      width 0.35s ease-in-out,
      height 0.35s ease-in-out;
  }

  &:hover[data-effect="effect-7"]::before {
    width: 200%;
    height: 200%;
  }

  /* effect-9: Profundidad — zoom + capa oscura */
  &:hover[data-effect="effect-9"] img {
    transform: scale(1.08);
  }

  &[data-effect="effect-9"]::before {
    background: rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: opacity 0.35s ease;
  }

  &:hover[data-effect="effect-9"]::before {
    opacity: 1;
  }

  /* effect-10: Gradiente inferior — sube desde abajo */
  &[data-effect="effect-10"]::before {
    background: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.55) 100%);
    opacity: 0;
    transform: translateY(50%);
    transition:
      opacity 0.35s ease,
      transform 0.35s ease;
  }

  &:hover[data-effect="effect-10"]::before {
    opacity: 1;
    transform: translateY(0);
  }

  &[data-effect="effect-11"]::before,
  &[data-effect="effect-11"]::after {
    inset: auto;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    background: rgba(0, 0, 0, 0.18);
    transition: all 0.3s ease-out;
    transform-origin: top left;
  }

  &[data-effect="effect-11"]::after {
    top: auto;
    right: 0;
    bottom: 0;
    left: auto;
    transform-origin: bottom right;
  }

  &:hover[data-effect="effect-11"]::before,
  &:hover[data-effect="effect-11"]::after {
    width: 100%;
    height: 100%;
  }

  &[data-effect="effect-8"]::before,
  &[data-effect="effect-8"]::after {
    inset: 18px;
    opacity: 0;
    transition:
      opacity 0.35s ease,
      transform 0.35s ease;
  }

  &[data-effect="effect-8"]::before {
    border-top: 1px solid #fff;
    border-bottom: 1px solid #fff;
    transform: scale(0, 1);
  }

  &[data-effect="effect-8"]::after {
    border-left: 1px solid #fff;
    border-right: 1px solid #fff;
    transform: scale(1, 0);
  }

  &:hover[data-effect="effect-8"]::before,
  &:hover[data-effect="effect-8"]::after {
    opacity: 1;
    transform: scale(1);
  }
`;

const CategoryImageGrid = styled.div`
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 400px;
  overflow-y: auto;
`;

const CategoryImageCard = styled.div`
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  border: 1px solid ${({ theme }) => theme.bg4 || "#e5e7eb"};
  border-radius: 8px;
`;

const CategoryImageThumb = styled.div`
  width: 56px;
  height: 40px;
  border-radius: 6px;
  overflow: hidden;
  flex-shrink: 0;
  background: ${({ theme }) => theme.bg3 || "#f3f4f6"};
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
`;

const CategoryImageOverlay = styled.div`
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
`;
