'use client';

import { useEffect, useMemo, useRef, useState } from 'react';
import { useStoreAuth } from './useStoreAuth';
import {
	DEFAULT_CATEGORIES,
	DEFAULT_ECOMMERCE_NEWS,
	DEFAULT_FEATURED_CATEGORIES,
	DEFAULT_PRODUCTS,
	DEFAULT_STORE_CONFIG,
	type CategoryTreeNode,
	type InstallmentPlan,
	type Product,
	type SeasonalTemplate,
	type StoreConfig
} from './config/defaultStoreConfig';
import HomeCatalogSection from './sections/catalogo/HomeCatalogSection';
import CartAddedSnackbar from './sections/checkout/CartAddedSnackbar';
import CartPageSection from './sections/checkout/CartPageSection';
import CheckoutSection from './sections/checkout/CheckoutSection';
import type { CartItem, CartNotice } from './sections/checkout/cartTypes';
import AccountSection from './sections/cuenta/AccountSection';
import ContentPageSection from './sections/contenido/ContentPageSection';
import StoreFooterSection from './sections/footer/StoreFooterSection';
import SeasonalDecorations from './sections/layout/SeasonalDecorations';
import StoreHeader from './sections/layout/StoreHeader';
import StoreFloatingActions from './sections/layout/StoreFloatingActions';
import StoreMotionObserver from './sections/layout/StoreMotionObserver';
import ProductDetailSection from './sections/producto/ProductDetailSection';
import QuickViewDialog from './sections/producto/QuickViewDialog';
import PromotionEntryDialog from './sections/promociones/PromotionEntryDialog';
import TrackingSection from './sections/seguimiento/TrackingSection';
import WishlistSection from './sections/wishlist/WishlistSection';
import StoreCurtainLoader from './StoreCurtainLoader';
import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
import { Main, Root } from './sections/styles';
import {
	fetchStoreSearch,
	searchResponseProducts,
	type StoreSearchResponse
} from '@/lib/storeSearch';

// newsItems se resuelve dentro del componente a partir de config.novedades

type StoreCatalog = {
	products: Product[];
	categories: string[];
	categoryTree: CategoryTreeNode[];
	featuredCategories: { name: string; image: string }[];
};

type StorePageProps = {
	config?: StoreConfig;
	catalog?: StoreCatalog;
	/** Ruta /producto/[slug]: ficha a abrir al cargar. */
	initialProductId?: string;
	/** Se está viendo el borrador desde el panel (no lo ven los clientes). */
	vistaPrevia?: boolean;
};

type CatalogSort = 'default' | 'priceAsc' | 'priceDesc' | 'rating' | 'newest';

function formatGs(value: number) {
	return `Gs. ${value.toLocaleString('es-PY')}`;
}

/**
 * Coincidencia de categoría considerando el árbol multinivel: un producto
 * pertenece a la categoría seleccionada si es su categoría hoja o si la
 * categoría aparece en su ruta (cualquier ancestro).
 */
function productMatchesCategory(product: Product, category: string) {
	if (category === 'Todos') return true;

	if (product.category === category) return true;

	return Array.isArray(product.categoryPath) && product.categoryPath.includes(category);
}

function categoryNodeIncludes(node: CategoryTreeNode, name: string): boolean {
	if (node.name === name) return true;

	return node.children.some((child) => categoryNodeIncludes(child, name));
}

function findCategoryNode(nodes: CategoryTreeNode[], name: string): CategoryTreeNode | null {
	for (const node of nodes) {
		if (categoryNodeIncludes(node, name)) {
			return node.name === name ? node : findCategoryNode(node.children, name);
		}
	}
	return null;
}

function clampQuantity(value: number, max: number) {
	return Math.min(Math.max(value, 1), Math.max(max, 1));
}

function getProductDetails(product: Product) {
	return (
		product.detailsJson ?? {
			mainDescription: product.description,
			sections: [
				{
					title: 'Detalles',
					type: 'text' as const,
					content: product.description
				},
				{
					title: 'Más información',
					type: 'specs' as const,
					rows: product.specifications
				}
			]
		}
	);
}

function getProductReviews(product: Product) {
	return product.reviews ?? [];
}

function getActiveSeason(seasons: SeasonalTemplate[], today = new Date()) {
	const todayDate = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();

	return [...seasons]
		.filter((season) => {
			if (!season.enabled) {
				return false;
			}

			const start = new Date(`${season.startDate}T00:00:00`).getTime();
			const end = new Date(`${season.endDate}T23:59:59`).getTime();

			return todayDate >= start && todayDate <= end;
		})
		.sort((a, b) => b.priority - a.priority)[0];
}

function StorePage({ config, catalog, initialProductId, vistaPrevia = false }: StorePageProps) {
	const storeConfig = config ?? DEFAULT_STORE_CONFIG;
	const products = catalog?.products ?? DEFAULT_PRODUCTS;
	const categories = catalog?.categories ?? DEFAULT_CATEGORIES;
	const categoryTree = catalog?.categoryTree ?? [];
	const catalogPriceCeiling = useMemo(
		() => Math.max(180000, ...products.map((product) => Number(product.price) || 0)),
		[products]
	);
	const featuredCategories = catalog?.featuredCategories ?? DEFAULT_FEATURED_CATEGORIES;
	const [query, setQuery] = useState('');
	const [category, setCategory] = useState('Todos');
	const [maxPrice, setMaxPrice] = useState(0);
	const [catalogPage, setCatalogPage] = useState(1);
	const [catalogSort, setCatalogSort] = useState<CatalogSort>('default');
	const [catalogPageSize, setCatalogPageSize] = useState(storeConfig.catalog.pageSize);
	const [selectedBrands, setSelectedBrands] = useState<string[]>([]);
	const [selectedSubcategories, setSelectedSubcategories] = useState<string[]>([]);
	const [cartPageOpen, setCartPageOpen] = useState(false);
	const [detailProduct, setDetailProduct] = useState<Product | null>(null);
	const [quickViewProduct, setQuickViewProduct] = useState<Product | null>(null);
	const [activeContentPageKey, setActiveContentPageKey] = useState<string | null>(null);
	const [checkoutOpen, setCheckoutOpen] = useState(false);
	const [checkoutStage, setCheckoutStage] = useState<'auth' | 'checkout'>('auth');
	const [checkoutAccessMode, setCheckoutAccessMode] = useState<'login' | 'register' | 'guest' | null>(null);
	const [checkoutConfirmed, setCheckoutConfirmed] = useState(false);
	const [checkoutSubmitting, setCheckoutSubmitting] = useState(false);
	const [confirmedOrderCode, setConfirmedOrderCode] = useState<string | null>(null);
	const [checkoutComentarios, setCheckoutComentarios] = useState('');
	const [pagoCancelado, setPagoCancelado] = useState(false);
	const [confirmedWarnings, setConfirmedWarnings] = useState<string[] | null>(null);
	const [checkoutError, setCheckoutError] = useState<string | null>(null);
	const [checkoutForm, setCheckoutForm] = useState({
		name: '',
		email: '',
		phone: '',
		address: '',
		departamento: '',
		distrito: '',
		ciudad: '',
		nroCasa: '0'
	});
	const [checkoutBilling, setCheckoutBilling] = useState<{
		ruc?: string;
		dv?: string;
		razonSocial?: string;
		naturaleza?: number | null;
		tipoContribuyente?: number | null;
		consultadoSIFEN?: boolean;
		consultaMotivo?: 'no_encontrado' | 'sin_servicio' | '';
	}>({});
	const [checkoutGeo, setCheckoutGeo] = useState<{ lat: number; lng: number } | null>(null);
	const [detailQuantity, setDetailQuantity] = useState(1);
	const [detailLoading, setDetailLoading] = useState(false);
	const [quickViewQuantity, setQuickViewQuantity] = useState(1);
	const [deliveryMethodId, setDeliveryMethodId] = useState('delivery-programado');
	const [paymentMethodId, setPaymentMethodId] = useState('');
	const [acceptTerms, setAcceptTerms] = useState(false);
	const [cartNotice, setCartNotice] = useState<CartNotice | null>(null);
	const [stockNotice, setStockNotice] = useState<{ name: string; stock: number } | null>(null);
	const cartStorageKey = `store_cart_${storeConfig.identity.subdomain}`;
	const wishlistStorageKey = `store_wishlist_${storeConfig.identity.subdomain}`;
	const storageKey = `${cartStorageKey}|${wishlistStorageKey}`;
	const [cartItems, setCartItems] = useState<CartItem[]>([]);
	const [forcePaginatedView, setForcePaginatedView] = useState(false);
	const [backendRelated, setBackendRelated] = useState<Product[]>([]);
	const [trackingOpen, setTrackingOpen] = useState(false);
	const [trackingInitialCode, setTrackingInitialCode] = useState('');
	const [accountOpen, setAccountOpen] = useState(false);
	// El ícono de cuenta sin sesión reutiliza el acceso del checkout. Sin recordar de
	// dónde venía, al ingresar el cliente caía en el checkout con el carrito vacío.
	const [accesoDesdeCuenta, setAccesoDesdeCuenta] = useState(false);
	const [wishlistOpen, setWishlistOpen] = useState(false);
	const [wishlistIds, setWishlistIds] = useState<Set<string>>(new Set<string>());
	const [hydratedStorageKey, setHydratedStorageKey] = useState<string | null>(null);
	const [searchResponse, setSearchResponse] = useState<StoreSearchResponse | null>(null);
	const [searchLoading, setSearchLoading] = useState(false);
	const [searchFailed, setSearchFailed] = useState(false);

	const storeAuth = useStoreAuth(storeConfig.identity.subdomain);

	useEffect(() => {
		const term = query.trim();

		if (term.length < 3) {
			setSearchResponse(null);
			setSearchLoading(false);
			setSearchFailed(false);
			return;
		}

		const controller = new AbortController();
		setSearchLoading(true);
		setSearchFailed(false);

		fetchStoreSearch(term, 500, controller.signal)
			.then((response) => setSearchResponse(response))
			.catch((error: Error) => {
				if (error.name !== 'AbortError') {
					setSearchResponse(null);
					setSearchFailed(true);
				}
			})
			.finally(() => {
				if (!controller.signal.aborted) setSearchLoading(false);
			});

		return () => controller.abort();
	}, [query]);

	useEffect(() => {
		let storedCart: CartItem[] = [];
		let storedWishlist: string[] = [];

		try {
			const rawCart = localStorage.getItem(cartStorageKey);
			const parsedCart = rawCart ? JSON.parse(rawCart) : [];

			if (Array.isArray(parsedCart)) storedCart = parsedCart as CartItem[];
		} catch {
			storedCart = [];
		}

		try {
			const rawWishlist = localStorage.getItem(wishlistStorageKey);
			const parsedWishlist = rawWishlist ? JSON.parse(rawWishlist) : [];

			if (Array.isArray(parsedWishlist)) {
				storedWishlist = parsedWishlist.filter((id): id is string => typeof id === 'string');
			}
		} catch {
			storedWishlist = [];
		}

		setCartItems(storedCart);
		setWishlistIds(new Set(storedWishlist));
		setHydratedStorageKey(storageKey);
	}, [cartStorageKey, storageKey, wishlistStorageKey]);

	useEffect(() => {
		if (hydratedStorageKey !== storageKey) return;

		try {
			localStorage.setItem(cartStorageKey, JSON.stringify(cartItems));
		} catch {
			/* storage lleno */
		}
	}, [cartItems, cartStorageKey, hydratedStorageKey, storageKey]);

	useEffect(() => {
		if (hydratedStorageKey !== storageKey) return;

		try {
			localStorage.setItem(wishlistStorageKey, JSON.stringify([...wishlistIds]));
		} catch {
			/* noop */
		}
	}, [hydratedStorageKey, storageKey, wishlistIds, wishlistStorageKey]);

	// Retorno desde Novasis Pay: ?pedido=TOKEN[&cancelled=1]
	useEffect(() => {
		if (typeof window === 'undefined') return;

		const params = new URLSearchParams(window.location.search);
		const pedidoToken = params.get('pedido');
		// ?seguimiento (sin código) abre el consultor vacío: es el link/QR genérico
		// que el comercio comparte o imprime desde el panel.
		const abrirSeguimiento = params.has('seguimiento');
		// ?q=texto abre el listado con esa búsqueda (lo usa la página 404 y sirve para
		// compartir una búsqueda).
		const busqueda = params.get('q')?.trim();
		if (busqueda) {
			window.history.replaceState({}, '', window.location.pathname);
			commitGlobalSearch(busqueda);
			return;
		}

		if (!pedidoToken && !abrirSeguimiento) return;

		const clean = window.location.pathname;
		window.history.replaceState({}, '', clean);
		setTrackingOpen(true);
		if (pedidoToken) setTrackingInitialCode(pedidoToken);
		// El gateway vuelve con `cancelled=1` cuando el comprador abandona el pago.
		// Se leía el token pero no este flag, así que cancelar aterrizaba en la misma
		// pantalla que pagar bien y nadie entendía qué había pasado.
		if (params.get('cancelled')) setPagoCancelado(true);
	}, []);

	// ── Hash routing ────────────────────────────────────────────────────────────
	// Vistas: #/producto/{id}  |  #/carrito  |  #/checkout
	// (wishlist / cuenta / tracking son overlays, no necesitan URL propia)

	// Se conserva la query (?vista_previa=…): sin ella, recargar en la vista previa del
	// panel volvía a la tienda publicada.
	function pushStoreHash(hash: string) {
		window.history.pushState({ storeView: hash }, '', `${window.location.pathname}${window.location.search}${hash}`);
	}

	function clearStoreHash() {
		window.history.replaceState({ storeView: '' }, '', `${window.location.pathname}${window.location.search}`);
	}

	function applyStoreHash(hash: string) {
		const path = hash.replace(/^#\/?/, '');
		const [seg, ...rest] = path.split('/');
		setCartPageOpen(false);
		setCheckoutOpen(false);
		setDetailProduct(null);
		setBackendRelated([]);
		setActiveContentPageKey(null);

		if (seg === 'carrito') {
			setCartPageOpen(true);
		} else if (seg === 'checkout') {
			if (cartItems.length > 0) setCheckoutOpen(true);
		} else if (seg === 'producto' && rest[0]) {
			const identifier = rest[0];
			const found = products.find((p) => p.id === identifier || p.slug === identifier);

			// Si ya está en el catálogo cargado, se pinta de una (id/nombre/precio) para que
			// no haya salto visual — pero igual se busca el detalle completo contra el backend
			// (galería, cuotas, reseñas, relacionados) porque el objeto de catálogo no los trae.
			if (found) setDetailProduct(found);
			setDetailLoading(true);
			fetch(`/api/store/producto/${identifier}`)
				.then((res) => (res.ok ? res.json() : null))
				.then((detail: (Product & { relatedProducts?: Product[] }) | null) => {
					if (detail) {
						const { relatedProducts: related, ...productFields } = detail;
						setDetailProduct((current) =>
							current && current.id === (detail.id ?? found?.id) ? { ...current, ...productFields } : (productFields as Product)
						);
						if (related?.length) setBackendRelated(related);
					}
				})
				.catch(() => undefined)
				.finally(() => setDetailLoading(false));
		} else if (seg === 'pagina' && rest[0]) {
			setActiveContentPageKey(rest[0]);
		}

		window.scrollTo({ top: 0 });
	}

	// Restaurar la vista desde el hash al cargar. Espera a que el carrito esté
	// hidratado desde localStorage: `#/checkout` sólo abre si hay ítems, y antes
	// esto corría con el carrito todavía vacío, así que recargar parado en el
	// checkout te devolvía al inicio con la URL diciendo /checkout igual.
	// Corre una sola vez; después la navegación la maneja el popstate.
	const hashRestauradoRef = useRef(false);
	useEffect(() => {
		if (hashRestauradoRef.current) return;
		if (hydratedStorageKey !== storageKey) return;
		hashRestauradoRef.current = true;

		let hash = window.location.hash;
		// Llegada por /producto/[slug] (link compartido o buscador): se abre esa ficha y
		// la URL pasa a la navegación interna por hash, así el resto de las vistas y el
		// Atrás/Adelante funcionan igual que siempre.
		if (!hash && initialProductId) {
			hash = `#/producto/${initialProductId}`;
			window.history.replaceState({ storeView: hash }, '', `/${hash}`);
		}
		if (!hash) return;

		// Checkout sin carrito no se puede abrir: se limpia el hash para que la URL
		// no siga anunciando una vista que no está en pantalla.
		if (hash.replace(/^#\/?/, '').split('/')[0] === 'checkout' && cartItems.length === 0) {
			clearStoreHash();
			return;
		}
		applyStoreHash(hash);
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [hydratedStorageKey, storageKey]);

	// El listener se registra una vez, pero applyStoreHash cambia en cada render
	// (lee cartItems y products). Antes quedaba atado a un render viejo con el
	// carrito vacío: Atrás → Adelante hacia #/checkout mostraba la portada.
	const applyStoreHashRef = useRef(applyStoreHash);
	applyStoreHashRef.current = applyStoreHash;
	useEffect(() => {
		const handlePop = (e: PopStateEvent) => {
			const state = e.state as { storeView?: string } | null;
			const hash = state?.storeView ?? window.location.hash ?? '';
			applyStoreHashRef.current(hash);
		};
		window.addEventListener('popstate', handlePop);
		return () => window.removeEventListener('popstate', handlePop);
	}, []);

	const activeSeason = useMemo(() => getActiveSeason(storeConfig.seasons), [storeConfig.seasons]);
	// Con una ficha abierta, la pestaña del navegador dice qué producto es (antes siempre
	// el nombre de la tienda).
	useEffect(() => {
		const tienda = storeConfig.identity?.name || '';
		document.title = detailProduct ? `${detailProduct.name} · ${tienda}` : tienda;
	}, [detailProduct, storeConfig.identity?.name]);
	const activeTheme = useMemo(
		() => ({ ...storeConfig.theme, ...(activeSeason?.themeOverrides ?? {}) }),
		[storeConfig.theme, activeSeason]
	);
	const activePromotions = useMemo(
		() => ({ ...storeConfig.promotions, ...(activeSeason?.promotions ?? {}) }),
		[storeConfig.promotions, activeSeason]
	);
	const storeCssVariables = useMemo(() => {
		const fontWeightMap = {
			suave: { label: 600, body: 400 },
			medio: { label: 700, body: 500 },
			intenso: { label: 900, body: 700 }
		};
		const fw = fontWeightMap[storeConfig.typography.fontWeight ?? 'medio'];
		const motionDuration = {
			fast: 140,
			normal: 200,
			relaxed: 280
		}[storeConfig.layout.transitionSpeed ?? 'normal'];
		return {
			'--store-primary': activeTheme.primary,
			'--store-secondary': activeTheme.secondary,
			'--store-accent': activeTheme.accent,
			'--store-background': activeTheme.background,
			'--store-surface': activeTheme.surface,
			'--store-text': activeTheme.text,
			'--store-muted': activeTheme.muted,
			'--store-success': activeTheme.success,
			'--store-danger': activeTheme.danger,
			'--store-price-color': activeTheme.priceColor ?? activeTheme.primary,
			'--store-price-size': `${storeConfig.typography.priceFontSize ?? 18}px`,
			'--catalog-columns-desktop': storeConfig.catalog.columnsDesktop,
			'--catalog-columns-tablet': storeConfig.catalog.columnsTablet,
			'--catalog-columns-mobile': storeConfig.catalog.columnsMobile,
			'--catalog-image-ratio': storeConfig.catalog.imageRatio,
			...({
				compact: {
					'--catalog-card-min-height': '300px',
					'--catalog-card-body-padding': '10px',
					'--catalog-card-body-min-height': '160px',
					'--catalog-card-name-size': '12px',
					'--catalog-card-name-min-h': '34px'
				},
				normal: {
					'--catalog-card-min-height': '438px',
					'--catalog-card-body-padding': '14px',
					'--catalog-card-body-min-height': '210px',
					'--catalog-card-name-size': '14px',
					'--catalog-card-name-min-h': '48px'
				},
				large: {
					'--catalog-card-min-height': '540px',
					'--catalog-card-body-padding': '20px',
					'--catalog-card-body-min-height': '260px',
					'--catalog-card-name-size': '16px',
					'--catalog-card-name-min-h': '60px'
				}
			}[storeConfig.catalog.cardSize] ?? {}),
			'--store-font': `'${storeConfig.typography.fontFamily ?? 'Inter'}', system-ui, sans-serif`,
			'--store-label-weight': fw.label,
			'--store-body-weight': fw.body,
			'--store-motion-fast': `${Math.round(motionDuration * 0.75)}ms`,
			'--store-motion-base': `${motionDuration}ms`,
			'--store-motion-slow': `${Math.round(motionDuration * 2)}ms`
		} as React.CSSProperties;
	}, [activeTheme, storeConfig]);

	// Carga dinámica de la fuente configurada desde Google Fonts
	useEffect(() => {
		const family = storeConfig.typography.fontFamily;

		if (!family) return;

		const id = `store-font-${family.replace(/\s+/g, '-')}`;

		if (document.getElementById(id)) return;

		const link = document.createElement('link');
		link.id = id;
		link.rel = 'stylesheet';
		link.href = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family)}:wght@400;500;600;700;800;900&display=swap`;
		document.head.appendChild(link);
	}, [storeConfig.typography.fontFamily]);

	const activeSearchResponse =
		searchResponse?.query.trim().toLowerCase() === query.trim().toLowerCase() ? searchResponse : null;
	const smartSearchProducts = useMemo(
		() => searchResponseProducts(activeSearchResponse),
		[activeSearchResponse]
	);
	const usesSmartSearch = query.trim().length >= 3 && Boolean(activeSearchResponse) && !searchFailed;
	const searchableProducts = useMemo(
		() => (usesSmartSearch ? smartSearchProducts : searchLoading ? [] : products),
		[products, searchLoading, smartSearchProducts, usesSmartSearch]
	);
	const effectiveCatalogPriceCeiling = useMemo(
		() => Math.max(catalogPriceCeiling, ...searchableProducts.map((product) => Number(product.price) || 0)),
		[catalogPriceCeiling, searchableProducts]
	);
	const effectiveMaxPrice = maxPrice || effectiveCatalogPriceCeiling;

	const filteredProducts = useMemo(() => {
		const normalizedQuery = query.trim().toLowerCase();
		const byFilters = searchableProducts.filter((product) => {
			const matchesCategory = productMatchesCategory(product, category);
			const matchesQuery =
				usesSmartSearch || !normalizedQuery || `${product.name} ${product.brand}`.toLowerCase().includes(normalizedQuery);
			const matchesPrice = product.price <= effectiveMaxPrice;
			const matchesBrand = selectedBrands.length === 0 || selectedBrands.includes(product.brand);
			const matchesSubcategory =
				selectedSubcategories.length === 0 ||
				selectedSubcategories.some((subcategory) => productMatchesCategory(product, subcategory));
			return matchesCategory && matchesQuery && matchesPrice && matchesBrand && matchesSubcategory;
		});

		const bySort = [...byFilters];

		if (catalogSort === 'priceAsc') {
			bySort.sort((a, b) => a.price - b.price);
		} else if (catalogSort === 'priceDesc') {
			bySort.sort((a, b) => b.price - a.price);
		} else if (catalogSort === 'rating') {
			bySort.sort((a, b) => b.rating - a.rating);
		} else if (catalogSort === 'newest') {
			bySort.sort((a, b) => Number(b.badges.includes('Nuevo')) - Number(a.badges.includes('Nuevo')));
		}

		return bySort;
	}, [
		catalogSort,
		category,
		effectiveMaxPrice,
		query,
		searchableProducts,
		selectedBrands,
		selectedSubcategories,
		usesSmartSearch
	]);

	const categoryScopedProducts = useMemo(() => {
		const normalizedQuery = query.trim().toLowerCase();
		return searchableProducts.filter((product) => {
			const matchesCategory = productMatchesCategory(product, category);
			const matchesQuery =
				usesSmartSearch || !normalizedQuery || `${product.name} ${product.brand}`.toLowerCase().includes(normalizedQuery);
			return matchesCategory && matchesQuery;
		});
	}, [category, query, searchableProducts, usesSmartSearch]);

	const hasGlobalSearch = query.trim().length > 0;
	const showHomeIntro = !hasGlobalSearch && !forcePaginatedView && category === 'Todos';
	const showCategoryCarousels =
		storeConfig.catalog.displayMode === 'categoryCarousels' && !hasGlobalSearch && !forcePaginatedView;
	const sortedNews = useMemo(
		() => [...(config?.novedades ?? DEFAULT_ECOMMERCE_NEWS)].sort((a, b) => b.priority - a.priority),
		[config?.novedades]
	);
	const navigationCategories = useMemo(() => categories.filter((item) => item !== 'Todos').slice(0, 9), []);
	const menuPromoCategories = useMemo(() => featuredCategories.slice(0, 4), [featuredCategories]);
	// Mega menú jerárquico desde el árbol real: categorías raíz con sus hijos.
	// Fallback a categorías planas si no hay árbol (catálogo de ejemplo).
	const categoryPreviewGroups = useMemo(() => {
		if (categoryTree.length > 0) {
			return categoryTree.slice(0, 5).map((root) => ({
				name: root.name,
				items: root.children.slice(0, 6).map((child) => child.name)
			}));
		}

		return navigationCategories.slice(0, 4).map((item) => ({
			name: item,
			items: products
				.filter((product) => product.category === item)
				.slice(0, 4)
				.map((product) => product.name)
		}));
	}, [categoryTree, navigationCategories, products]);
	const catalogSections = useMemo(() => {
		const categoryNames = categories.filter((item) => item !== 'Todos');
		const visibleCategories =
			category === 'Todos' ? categoryNames : categoryNames.filter((item) => item === category);

		const sections = visibleCategories
			.map((item) => ({
				name: item,
				products: products
					.filter((product) => product.category === item)
					.slice(0, storeConfig.catalog.maxProductsPerCategory)
			}))
			.filter((section) => section.products.length > 0);

		if (category !== 'Todos') return sections;

		// Portada: las categorías que eligió el comercio, en su orden. Sin elección, las
		// más surtidas primero. Antes salían en orden alfabético (Abdominal, Aire
		// acondicionado, Alacenas, Alambre…), que no dice nada de lo que se vende.
		const elegidas = (storeConfig.catalog.homeCategories ?? []).filter(Boolean);
		const conteo = new Map<string, number>();
		products.forEach((p) => conteo.set(p.category, (conteo.get(p.category) ?? 0) + 1));
		const ordenadas = elegidas.length
			? elegidas
					.map((nombre) => sections.find((section) => section.name === nombre))
					.filter((section): section is (typeof sections)[number] => Boolean(section))
			: [...sections].sort((a, b) => (conteo.get(b.name) ?? 0) - (conteo.get(a.name) ?? 0));

		const categoryLimit = Math.max(1, storeConfig.catalog.maxCategoriesOnHome || 6);
		return ordenadas.slice(0, categoryLimit);
	}, [
		categories,
		category,
		products,
		storeConfig.catalog.homeCategories,
		storeConfig.catalog.maxCategoriesOnHome,
		storeConfig.catalog.maxProductsPerCategory
	]);
	const pageSize = catalogPageSize;
	const totalPages = Math.max(1, Math.ceil(filteredProducts.length / pageSize));
	const paginatedProducts = useMemo(() => {
		const startIndex = (catalogPage - 1) * pageSize;
		return filteredProducts.slice(startIndex, startIndex + pageSize);
	}, [catalogPage, filteredProducts, pageSize]);
	const relatedProducts = useMemo(() => {
		if (!detailProduct) return [];

		// Preferir los relacionados que ya vienen del backend (con stock/precio real).
		// Completar con los del catálogo local si son menos de 4.
		const byId = new Set(backendRelated.map((p) => p.id));
		const localFill = products
			.filter((p) => p.id !== detailProduct.id && p.category === detailProduct.category && !byId.has(p.id))
			.slice(0, Math.max(0, storeConfig.catalog.maxProductsPerCategory - backendRelated.length));
		return [...backendRelated, ...localFill].slice(0, storeConfig.catalog.maxProductsPerCategory);
	}, [detailProduct, backendRelated]);

	const detailSections = detailProduct ? getProductDetails(detailProduct).sections : [];
	const detailMainDescription = detailProduct ? getProductDetails(detailProduct).mainDescription : '';
	const detailReviews = detailProduct ? getProductReviews(detailProduct) : [];
	const activeContentPage = useMemo(
		() => storeConfig.contentPages.find((page) => page.key === activeContentPageKey),
		[activeContentPageKey]
	);

	useEffect(() => {
		setCatalogPage(1);
	}, [catalogPageSize, catalogSort, category, maxPrice, query, selectedBrands, selectedSubcategories]);

	useEffect(() => {
		setSelectedBrands([]);
		setSelectedSubcategories([]);
		setMaxPrice(0);
	}, [category]);

	const selectedCategoryNode = useMemo(() => findCategoryNode(categoryTree, category), [categoryTree, category]);
	// Imagen cargada en el panel para cada categoría, a cualquier nivel del árbol. Antes
	// sólo se usaban las de las categorías raíz: la de una subcategoría (ej. Alacenas)
	// no aparecía ni en su carrusel de la portada ni en su página.
	const categoryImages = useMemo(() => {
		const map: Record<string, string> = {};
		const walk = (nodes: CategoryTreeNode[]) =>
			nodes.forEach((node) => {
				if (node.image && !map[node.name]) map[node.name] = node.image;
				walk(node.children ?? []);
			});
		walk(categoryTree);
		return map;
	}, [categoryTree]);

	const selectCategory = (categoryName: string) => {
		setCategory(categoryName);
		setForcePaginatedView(categoryName !== 'Todos');
		setCatalogPage(1);
	};

	const commitGlobalSearch = (value: string) => {
		setQuery(value.trim());
		setCategory('Todos');
		setSelectedBrands([]);
		setSelectedSubcategories([]);
		setMaxPrice(0);
		setForcePaginatedView(true);
		setCatalogPage(1);
	};

	const toggleBrandFilter = (brand: string) => {
		setSelectedBrands((current) =>
			current.includes(brand) ? current.filter((item) => item !== brand) : [...current, brand]
		);
	};

	const toggleSubcategoryFilter = (subcategory: string) => {
		setSelectedSubcategories((current) =>
			current.includes(subcategory) ? current.filter((item) => item !== subcategory) : [...current, subcategory]
		);
	};

	const resetCatalogFilters = () => {
		setQuery('');
		setMaxPrice(0);
		setSelectedBrands([]);
		setSelectedSubcategories([]);
		setCatalogSort('default');
		setCatalogPageSize(storeConfig.catalog.pageSize);
	};

	const goHome = () => {
		closeAll();
		setCategory('Todos');
		setForcePaginatedView(false);
		setCatalogPage(1);
		resetCatalogFilters();
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const openProductsCatalog = () => {
		closeAll();
		setCategory('Todos');
		setForcePaginatedView(true);
		setCatalogPage(1);
		resetCatalogFilters();
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const cartCount = cartItems.reduce((acc, item) => acc + item.quantity, 0);
	const cartTotal = cartItems.reduce((acc, item) => acc + item.quantity * item.price, 0);
	const configuredDeliveryMethods = (storeConfig.checkout.deliveryMethods ?? []).filter(
		(method) => method.enabled !== false
	);
	const legacyDeliveryMethods = [
		...(storeConfig.checkout.allowDelivery
			? [
					{
						id: 'delivery-programado',
						type: 'delivery' as const,
						name: 'Delivery programado',
						cost: storeConfig.checkout.deliveryCost,
						enabled: true
					}
				]
			: []),
		...(storeConfig.checkout.allowPickup
			? [{ id: 'retiro-local', type: 'pickup' as const, name: 'Retiro en local', cost: 0, enabled: true }]
			: [])
	];
	const activeDeliveryMethods =
		configuredDeliveryMethods.length > 0 ? configuredDeliveryMethods : legacyDeliveryMethods;
	const effectiveDeliveryMethod =
		activeDeliveryMethods.find((method) => method.id === deliveryMethodId) ?? activeDeliveryMethods[0];
	const effectiveDeliveryType = effectiveDeliveryMethod?.type ?? 'delivery';
	const effectiveDeliveryMethodId = effectiveDeliveryMethod?.id ?? '';
	const deliveryCost = cartTotal > 0 ? Math.max(0, Number(effectiveDeliveryMethod?.cost ?? 0)) : 0;
	const defaultPaymentMethods = [
		{ id: 'novasis-pay', type: 'online' as const, name: storeConfig.checkout.paymentProviderLabel, enabled: true, discountPercent: 0 }
	];
	const configuredPaymentMethods = (storeConfig.checkout.paymentMethods ?? []).filter(
		(method) => method.enabled !== false
	);
	const activePaymentMethods = configuredPaymentMethods.length > 0 ? configuredPaymentMethods : defaultPaymentMethods;
	const effectivePaymentMethod =
		activePaymentMethods.find((method) => method.id === paymentMethodId) ??
		activePaymentMethods.find((method) => method.type === 'online') ??
		activePaymentMethods[0];
	const effectivePaymentMethodId = effectivePaymentMethod?.id ?? '';
	const paymentDiscountPercent = Math.max(0, Number(effectivePaymentMethod?.discountPercent ?? 0));
	const paymentDiscount = cartTotal > 0 ? Math.round(cartTotal * (paymentDiscountPercent / 100)) : 0;
	const checkoutTotal = cartTotal - paymentDiscount + deliveryCost;
	const checkoutOrderCode = `ECOM-${new Date().getFullYear()}-${String(Math.max(cartCount, 1)).padStart(4, '0')}`;

	const addToCart = (product: Product, quantity = 1, financingPlan?: InstallmentPlan) => {
		// Un producto padre de variantes no es vendible: el ERP factura la hija.
		// Cualquier "agregar" desde una tarjeta / quick view abre la ficha para que
		// se elija la combinación (la ficha manda ya la variante, sin hasVariants).
		if (product.hasVariants) {
			openProductDetail(product);
			return;
		}

		// Sin precio cargado el backend rechaza el pedido ("no tiene precio público
		// válido"): se avisa acá en vez de dejarlo descubrir en el checkout.
		if (!(product.price > 0)) {
			setStockNotice({ name: product.name, stock: -1 });
			return;
		}

		const safeQuantity = clampQuantity(quantity, product.stock);

		// clampQuantity topea el incremento, no el total: sin esto, agregar de a uno
		// repetidas veces dejaba el carrito por encima del stock real.
		const yaEnCarrito = cartItems.find((item) => item.id === product.id);
		const totalPedido = (yaEnCarrito?.quantity ?? 0) + safeQuantity;
		const totalFinal = clampQuantity(totalPedido, product.stock);
		if (totalFinal < totalPedido) {
			setStockNotice({ name: product.name, stock: product.stock });
		}

		setCartItems((current) => {
			const existing = current.find((item) => item.id === product.id);

			if (existing) {
				return current.map((item) =>
					item.id === product.id
						? // Volver a agregar con otro plan pisa el anterior: un mismo producto no
							// puede quedar mitad al contado y mitad financiado en el mismo pedido.
							{
								...item,
								quantity: clampQuantity(item.quantity + safeQuantity, product.stock),
								financingPlan: financingPlan ?? item.financingPlan
							}
						: item
				);
			}

			return [...current, { ...product, quantity: safeQuantity, financingPlan }];
		});
		setCartNotice({ product, quantity: totalFinal - (yaEnCarrito?.quantity ?? 0) });
	};

	const updateQuantity = (productId: string, delta: number) => {
		// El tope por stock no puede pasar desapercibido: antes el contador se quedaba
		// quieto y el comprador no sabía por qué.
		if (delta > 0) {
			const actual = cartItems.find((item) => item.id === productId);
			if (actual && actual.quantity + delta > actual.stock) {
				setStockNotice({ name: actual.name, stock: actual.stock });
			}
		}

		setCartItems((current) =>
			current
				.map((item) => {
					if (item.id !== productId) return item;

					const next = item.quantity + delta;
					return { ...item, quantity: Math.min(Math.max(next, 0), item.stock) };
				})
				.filter((item) => item.quantity > 0)
		);
	};

	const removeCartItem = (productId: string) => {
		setCartItems((current) => current.filter((item) => item.id !== productId));
	};

	const openProductDetail = (product: Product) => {
		setDetailProduct(product);
		setQuickViewProduct(null);
		setActiveContentPageKey(null);
		setCheckoutOpen(false);
		setCheckoutConfirmed(false);
		setDetailQuantity(1);
		pushStoreHash(`#/producto/${product.slug || product.id}`);
		window.scrollTo({ top: 0, behavior: 'smooth' });

		// Enriquecer con galería + descripción + relacionados reales desde el backend.
		// Si el id no es UUID (catálogo de ejemplo) o falla, se queda con lo que hay.
		if (/^[0-9a-f-]{36}$/i.test(product.id)) {
			setDetailLoading(true);
			fetch(`/api/store/producto/${product.slug || product.id}`)
				.then((res) => (res.ok ? res.json() : null))
				.then((detail: (Product & { relatedProducts?: Product[] }) | null) => {
					if (detail && detail.id === product.id) {
						const { relatedProducts: backendRelated, ...productFields } = detail;
						setDetailProduct((current) =>
							current?.id === product.id ? { ...current, ...productFields } : current
						);

						if (backendRelated?.length) {
							setBackendRelated(backendRelated);
						}
					}
				})
				.catch(() => undefined)
				.finally(() => setDetailLoading(false));
		}
	};

	const closeProductDetail = () => {
		setDetailProduct(null);
		setBackendRelated([]);
		setDetailLoading(false);
		setCheckoutOpen(false);
		setCheckoutConfirmed(false);
		clearStoreHash();
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const openContentPage = (pageKey: string) => {
		setDetailProduct(null);
		setQuickViewProduct(null);
		setActiveContentPageKey(pageKey);
		setCheckoutOpen(false);
		setCheckoutConfirmed(false);
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const closeContentPage = () => {
		setActiveContentPageKey(null);
		setCheckoutOpen(false);
		setCheckoutConfirmed(false);
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const openCheckout = () => {
		if (cartItems.length === 0) {
			return;
		}

		setCartPageOpen(false);
		setDetailProduct(null);
		setQuickViewProduct(null);
		setActiveContentPageKey(null);
		setCheckoutOpen(true);
		setCheckoutStage(checkoutAccessMode ? 'checkout' : 'auth');
		setCheckoutConfirmed(false);
		setAcceptTerms(false);
		pushStoreHash('#/checkout');
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const closeCheckout = () => {
		setCheckoutOpen(false);
		setCheckoutStage('auth');
		setCheckoutConfirmed(false);
		setCheckoutBilling({});
		clearStoreHash();
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const updateCheckoutForm = (
		field: 'name' | 'email' | 'phone' | 'address' | 'departamento' | 'distrito' | 'ciudad' | 'nroCasa',
		value: string
	) => {
		setCheckoutForm((prev) => ({ ...prev, [field]: value }));
	};

	const confirmCheckout = async () => {
		if (checkoutSubmitting) return;

		setCheckoutSubmitting(true);
		try {
			const callbackBase =
				typeof window !== 'undefined' ? `${window.location.origin}${window.location.pathname}` : '';
			const res = await fetch('/api/store/checkout', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					customer: {
						...checkoutForm,
						ruc: checkoutBilling.ruc || undefined,
						dv: checkoutBilling.dv || undefined,
						razonSocial: checkoutBilling.razonSocial || undefined,
						naturaleza: checkoutBilling.naturaleza ?? undefined,
						tipoContribuyente: checkoutBilling.tipoContribuyente ?? undefined,
						consultadoSIFEN: checkoutBilling.consultadoSIFEN ?? false,
						// El comprador cargó un RUC pero SIFEN no estaba disponible: no es lo
						// mismo que "no existe". Se marca para que el comercio lo valide antes
						// de facturar, en vez de darlo por no contribuyente en silencio.
						sifenNoVerificado: checkoutBilling.consultaMotivo === 'sin_servicio'
					},
					delivery: {
						methodId: effectiveDeliveryMethodId,
						type: effectiveDeliveryType,
						address: checkoutForm.address,
						departamento: checkoutForm.departamento || undefined,
						distrito: checkoutForm.distrito || undefined,
						ciudad: checkoutForm.ciudad || undefined,
						nroCasa: checkoutForm.nroCasa || '0',
						lat: checkoutGeo?.lat,
						lng: checkoutGeo?.lng
					},
					items: cartItems.map((item) => ({ id: item.id, quantity: item.quantity })),
					paymentMethodId: effectivePaymentMethodId,
					// El backend revalida cada plan contra la base; acá sólo se manda la elección.
					financing: cartItems
						.filter((item) => item.financingPlan)
						.map((item) => ({ productoId: item.id, planId: item.financingPlan!.planId })),
					comentarios: checkoutComentarios.trim() || undefined,
					callbackBase
				})
			});
			const data = await res.json();

			if (!res.ok || !data.ok) {
				const msg =
					Array.isArray(data.issues) && data.issues.length
						? data.issues.join(' ')
						: 'No se pudo procesar el pedido.';
				setCheckoutError(msg);
				return;
			}

			setCartItems([]);
			setCheckoutComentarios('');
			try {
				localStorage.removeItem(cartStorageKey);
			} catch {
				/* noop */
			}

			// Si Novasis Pay está configurado, redirigir al gateway
			if (data.hostedUrl) {
				window.location.href = data.hostedUrl;
				return;
			}

			// Flujo manual: mostrar pantalla de confirmación. Si el backend avisó que algo
			// se degradó (p. ej. el pago online no arrancó), se muestra ahí mismo.
			setConfirmedWarnings(Array.isArray(data.warnings) && data.warnings.length ? data.warnings : null);
			setConfirmedOrderCode(data.codigo);
			setCheckoutConfirmed(true);
			window.scrollTo({ top: 0, behavior: 'smooth' });
		} catch {
			setCheckoutError('No se pudo conectar con el servidor. Intentá nuevamente.');
		} finally {
			setCheckoutSubmitting(false);
		}
	};

	const handleSubmitReview = async (rating: number, comment: string) => {
		if (!detailProduct) return;

		const res = await fetch(`/api/store/auth/resenas/${detailProduct.id}`, {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json',
				...(storeAuth.token ? { Authorization: `Bearer ${storeAuth.token}` } : {})
			},
			body: JSON.stringify({ rating, comment })
		});
		const data = await res.json().catch(() => ({}));

		if (!res.ok) throw new Error(data?.message ?? 'No se pudo publicar la reseña');

		// Recargar el detalle para que aparezca la reseña
		if (/^[0-9a-f-]{36}$/i.test(detailProduct.id)) {
			fetch(`/api/store/producto/${detailProduct.id}`)
				.then((r) => (r.ok ? r.json() : null))
				.then((detail: (Product & { relatedProducts?: Product[] }) | null) => {
					if (detail && detail.id === detailProduct.id) {
						const { relatedProducts: updatedRelated, ...productFields } = detail;
						setDetailProduct((current) =>
							current?.id === detailProduct.id ? { ...current, ...productFields } : current
						);

						if (updatedRelated?.length) setBackendRelated(updatedRelated);
					}
				})
				.catch(() => undefined);
		}
	};

	const continueCheckoutAs = (
		mode: 'login' | 'register' | 'guest',
		clienteData?: { nombre: string; email: string; telefono?: string; documento?: string }
	) => {
		if (accesoDesdeCuenta && mode !== 'guest') {
			setAccesoDesdeCuenta(false);
			closeAll();
			setAccountOpen(true);
			window.scrollTo({ top: 0, behavior: 'smooth' });
			return;
		}
		setCheckoutAccessMode(mode);
		// Usar clienteData recibido directamente (evita timing de estado async)
		// o caer en storeAuth.cliente si ya está disponible
		const data =
			clienteData ??
			(storeAuth.cliente
				? {
						nombre: storeAuth.cliente.nombre,
						email: storeAuth.cliente.email,
						telefono: storeAuth.cliente.telefono,
						documento: storeAuth.cliente.documento
					}
				: null);

		if (data?.documento) {
			// El cliente ya dejó su RUC/CI en una compra anterior: no tiene sentido
			// volver a pedírselo. Se precarga y la consulta a SIFEN se dispara sola.
			setCheckoutBilling((prev) => ({ ...prev, ruc: prev.ruc || data.documento }));
		}

		if (data) {
			setCheckoutForm((prev) => ({
				...prev,
				name: data.nombre || prev.name,
				email: data.email || prev.email,
				phone: data.telefono ?? prev.phone
			}));
		}

		setCheckoutStage('checkout');
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const handleViewAll = (categoryName: string) => {
		selectCategory(categoryName);
		setForcePaginatedView(true);
		setCatalogPage(1);
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const handleQuickView = (product: Product) => {
		setQuickViewQuantity(1);
		setQuickViewProduct(product);
	};

	const closeAll = () => {
		setDetailProduct(null);
		setCheckoutOpen(false);
		setCheckoutConfirmed(false);
		setActiveContentPageKey(null);
		setTrackingOpen(false);
		setAccountOpen(false);
		setWishlistOpen(false);
		setCartPageOpen(false);
		setAccesoDesdeCuenta(false);
	};

	const openTracking = (initialCode = '') => {
		closeAll();
		setTrackingInitialCode(initialCode);
		setTrackingOpen(true);
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const openAccount = () => {
		if (!storeAuth.isLoggedIn) {
			closeAll();
			setAccesoDesdeCuenta(true);
			setCheckoutOpen(true);
			return;
		}

		closeAll();
		setAccountOpen(true);
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const openWishlist = () => {
		closeAll();
		setWishlistOpen(true);
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const openPromotionCategory = (categoryName: string) => {
		closeAll();
		resetCatalogFilters();
		selectCategory(categoryName || 'Todos');
		window.scrollTo({ top: 0, behavior: 'smooth' });
	};

	const [wishlistNotice, setWishlistNotice] = useState(false);

	const toggleWishlist = (productId: string) => {
		setWishlistIds((prev) => {
			const next = new Set(prev);

			if (next.has(productId)) next.delete(productId);
			else {
				next.add(productId);

				if (!storeAuth.isLoggedIn) setWishlistNotice(true);
			}

			return next;
		});
	};

	return (
		<Root
			data-season={activeSeason?.id ?? 'base'}
			data-banner-effect={storeConfig.layout.bannerHoverEffect}
			data-motion={storeConfig.layout.motionPreset ?? 'dynamic'}
			data-scroll-animations={storeConfig.layout.showScrollAnimations ?? true}
			data-hero-variant={storeConfig.layout.heroVariant ?? 'immersive'}
			data-section-background={storeConfig.layout.sectionBackgroundMode ?? 'alternating'}
			data-card-hover={storeConfig.layout.cardHoverEffect ?? 'lift'}
			data-header-variant={storeConfig.layout.headerVariant ?? 'compact'}
			data-sticky-header={storeConfig.layout.stickyHeader ?? true}
			style={storeCssVariables}
		>
			<StoreMotionObserver />
			<StoreCurtainLoader
				storeName={storeConfig.identity.name}
				logoText={storeConfig.identity.logoText}
				slogan={storeConfig.identity.slogan}
			/>
			{vistaPrevia && (
				<div
					role="status"
					style={{
						position: 'sticky',
						top: 0,
						zIndex: 1400,
						padding: '8px 16px',
						background: '#1e293b',
						color: '#fff',
						fontSize: 13,
						fontWeight: 600,
						textAlign: 'center'
					}}
				>
					Vista previa del borrador: así se verá la tienda al publicar. Los clientes siguen viendo la versión publicada.
				</div>
			)}
			<SeasonalDecorations season={activeSeason} />
			<PromotionEntryDialog
				promotion={activePromotions}
				tenantKey={storeConfig.identity.subdomain}
				storeCssVariables={storeCssVariables}
				onBuyNow={openPromotionCategory}
			/>
			<StoreHeader
				config={storeConfig}
				activeSeasonName={activeSeason?.name ?? null}
				topBarMessage={activePromotions.topBar}
				query={query}
				cartCount={cartCount}
				cartItems={cartItems}
				cartTotal={cartTotal}
				storeCssVariables={storeCssVariables}
				menuPromoCategories={menuPromoCategories}
				categoryPreviewGroups={categoryPreviewGroups}
				formatPrice={formatGs}
				onQueryChange={commitGlobalSearch}
				onOpenDetail={openProductDetail}
				onCloseContentPage={closeContentPage}
				onSelectCategory={selectCategory}
				onGoHome={goHome}
				onOpenProducts={openProductsCatalog}
				onUpdateCartQuantity={updateQuantity}
				onRemoveCartItem={removeCartItem}
				onViewCart={() => {
					setCartPageOpen(true);
					setDetailProduct(null);
					setCheckoutOpen(false);
					setActiveContentPageKey(null);
					pushStoreHash('#/carrito');
					window.scrollTo({ top: 0, behavior: 'smooth' });
				}}
				onCheckout={openCheckout}
				isLoggedIn={storeAuth.isLoggedIn}
				clienteNombre={storeAuth.cliente?.nombre ?? null}
				wishlistCount={wishlistIds.size}
				onLogout={storeAuth.logout}
				onOpenAccount={openAccount}
				onOpenTracking={() => openTracking()}
				onOpenWishlist={openWishlist}
			/>

			<Main>
				{trackingOpen ? (
					<TrackingSection
						config={storeConfig}
						initialCode={trackingInitialCode}
						onClose={() => {
							setTrackingOpen(false);
							window.scrollTo({ top: 0, behavior: 'smooth' });
						}}
					/>
				) : accountOpen && storeAuth.isLoggedIn ? (
					<AccountSection
						storeAuth={storeAuth}
						token={storeAuth.token}
						onClose={() => {
							setAccountOpen(false);
							window.scrollTo({ top: 0, behavior: 'smooth' });
						}}
						onLogout={() => {
							storeAuth.logout();
							setAccountOpen(false);
						}}
						onOpenTracking={(code) => openTracking(code)}
					/>
				) : wishlistOpen ? (
					<WishlistSection
						products={products}
						wishlistIds={wishlistIds}
						formatPrice={formatGs}
						showBrand={storeConfig.catalog.showBrand}
						onToggleWishlist={toggleWishlist}
						onOpenDetail={(p) => {
							setWishlistOpen(false);
							openProductDetail(p);
						}}
						onAddToCart={addToCart}
						onClose={() => {
							setWishlistOpen(false);
							window.scrollTo({ top: 0, behavior: 'smooth' });
						}}
						onGoToCatalog={() => {
							setWishlistOpen(false);
							window.scrollTo({ top: 0, behavior: 'smooth' });
						}}
					/>
				) : cartPageOpen && !checkoutOpen && !detailProduct ? (
					<CartPageSection
						items={cartItems}
						totalItems={cartCount}
						formatPrice={formatGs}
						onBack={() => {
							setCartPageOpen(false);
							clearStoreHash();
						}}
						onUpdateQuantity={updateQuantity}
						onRemove={removeCartItem}
						onCheckout={() => {
							setCartPageOpen(false);
							openCheckout();
						}}
					/>
				) : detailProduct ? (
					<ProductDetailSection
						product={detailProduct}
						relatedProducts={relatedProducts}
						detailQuantity={detailQuantity}
						detailSections={detailSections}
						detailMainDescription={detailMainDescription}
						detailReviews={detailReviews}
						loading={detailLoading}
						showBrand={storeConfig.catalog.showBrand}
						showStock={storeConfig.catalog.showStock}
						showPromotionBadge={storeConfig.catalog.showPromotionBadge}
						storeAuth={{
							isLoggedIn: storeAuth.isLoggedIn,
							cliente: storeAuth.cliente
								? { nombre: storeAuth.cliente.nombre, email: storeAuth.cliente.email }
								: null,
							token: storeAuth.token ?? null
						}}
						formatPrice={formatGs}
						onClose={closeProductDetail}
						onSetCategory={selectCategory}
						onSetDetailQuantity={setDetailQuantity}
						onOpenDetail={openProductDetail}
						onAddToCart={addToCart}
						onQuickView={handleQuickView}
						isFavorite={detailProduct ? wishlistIds.has(detailProduct.id) : false}
						onToggleWishlist={toggleWishlist}
						onSubmitReview={handleSubmitReview}
						onOpenCheckout={openCheckout}
					/>
				) : checkoutOpen ? (
					<CheckoutSection
						soloAcceso={accesoDesdeCuenta}
						cartItems={cartItems}
						cartTotal={cartTotal}
						checkoutConfirmed={checkoutConfirmed}
						checkoutWarnings={confirmedWarnings}
						rucPrecargado={checkoutBilling.ruc ?? storeAuth.cliente?.documento ?? null}
						comentarios={checkoutComentarios}
						onComentariosChange={setCheckoutComentarios}
						checkoutStage={checkoutStage}
						checkoutAccessMode={checkoutAccessMode}
						effectiveDeliveryType={effectiveDeliveryType}
						effectiveDeliveryMethodId={effectiveDeliveryMethodId}
						deliveryMethods={activeDeliveryMethods}
						deliveryCost={deliveryCost}
						effectivePaymentMethodId={effectivePaymentMethodId}
						paymentMethods={activePaymentMethods}
						paymentDiscount={paymentDiscount}
						checkoutTotal={checkoutTotal}
						checkoutOrderCode={confirmedOrderCode ?? checkoutOrderCode}
						acceptTerms={acceptTerms}
						config={storeConfig}
						customer={checkoutForm}
						checkoutSubmitting={checkoutSubmitting}
						storeAuth={storeAuth}
						formatPrice={formatGs}
						onClose={closeCheckout}
						onConfirm={confirmCheckout}
						onContinueAs={continueCheckoutAs}
						onUpdateQuantity={updateQuantity}
						onSetDeliveryMethod={setDeliveryMethodId}
						onSetPaymentMethod={setPaymentMethodId}
						onSetAcceptTerms={setAcceptTerms}
						onSetCheckoutStage={setCheckoutStage}
						onCustomerChange={updateCheckoutForm}
						onBillingChange={(data) =>
							setCheckoutBilling({
								ruc: data.ruc,
								dv: data.dv,
								razonSocial: data.razonSocial,
								naturaleza: data.naturaleza,
								tipoContribuyente: data.tipoContribuyente,
								consultadoSIFEN: data.consultadoSIFEN,
								consultaMotivo: data.consultaMotivo
							})
						}
						onOpenContentPage={openContentPage}
						onGeoChange={(coords) => setCheckoutGeo(coords)}
						checkoutError={checkoutError}
						onClearCheckoutError={() => setCheckoutError(null)}
					/>
				) : activeContentPage ? (
					<ContentPageSection
						page={activeContentPage}
						config={storeConfig}
						onClose={closeContentPage}
					/>
				) : (
					<HomeCatalogSection
						query={query}
						category={category}
						maxPrice={effectiveMaxPrice}
						priceRangeMax={effectiveCatalogPriceCeiling}
						catalogPage={catalogPage}
						catalogSort={catalogSort}
						catalogPageSize={catalogPageSize}
						showHomeIntro={showHomeIntro}
						showCategoryCarousels={showCategoryCarousels}
						hasGlobalSearch={hasGlobalSearch}
						searchResponse={activeSearchResponse}
						searchLoading={searchLoading}
						catalogSections={catalogSections}
						selectedCategoryNode={selectedCategoryNode}
						categoryImages={categoryImages}
						categoryScopedProducts={categoryScopedProducts}
						filteredProducts={filteredProducts}
						paginatedProducts={paginatedProducts}
						totalPages={totalPages}
						selectedBrands={selectedBrands}
						selectedSubcategories={selectedSubcategories}
						sortedNews={sortedNews}
						categories={categories}
						featuredCategories={featuredCategories}
						activePromotions={activePromotions}
						storeConfig={storeConfig}
						formatPrice={formatGs}
						onSetQuery={commitGlobalSearch}
						onSetCategory={selectCategory}
						onSetMaxPrice={setMaxPrice}
						onSetCatalogPage={setCatalogPage}
						onSetCatalogSort={setCatalogSort}
						onSetCatalogPageSize={setCatalogPageSize}
						onToggleBrand={toggleBrandFilter}
						onToggleSubcategory={toggleSubcategoryFilter}
						onResetCatalogFilters={resetCatalogFilters}
						onGoHome={goHome}
						onOpenDetail={openProductDetail}
						onAddToCart={addToCart}
						onQuickView={handleQuickView}
						onViewAll={handleViewAll}
						wishlistIds={wishlistIds}
						onToggleWishlist={toggleWishlist}
					/>
				)}
			</Main>

			<StoreFooterSection
				config={storeConfig}
				onOpenContentPage={openContentPage}
			/>

			<StoreFloatingActions
				whatsapp={storeConfig.footer.contact.whatsapp}
				storeName={storeConfig.identity.name}
			/>

			<QuickViewDialog
				product={quickViewProduct}
				quickViewQuantity={quickViewQuantity}
				storeCssVariables={storeCssVariables}
				formatPrice={formatGs}
				onClose={() => setQuickViewProduct(null)}
				onSetQuantity={setQuickViewQuantity}
				onAddToCart={addToCart}
				onOpenDetail={openProductDetail}
			/>

			<CartAddedSnackbar
				notice={cartNotice}
				onClose={() => setCartNotice(null)}
				onOpenCart={() => {
					setCartNotice(null);
					setCartPageOpen(true);
				}}
			/>

			<Snackbar
				open={pagoCancelado}
				autoHideDuration={12000}
				onClose={() => setPagoCancelado(false)}
				anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
			>
				<Alert
					severity="warning"
					variant="filled"
					onClose={() => setPagoCancelado(false)}
					sx={{ borderRadius: 2.5, fontWeight: 500, maxWidth: 480 }}
				>
					Cancelaste el pago. Tu pedido quedó registrado como pendiente: podés retomarlo
					desde el seguimiento o comunicarte con el comercio para coordinar otro medio.
				</Alert>
			</Snackbar>

			<Snackbar
				open={!!stockNotice}
				autoHideDuration={5000}
				// Tocar otra vez "+" cuenta como clic afuera y cerraba el aviso justo
				// cuando el comprador seguía chocando contra el tope.
				onClose={(_, reason) => reason !== 'clickaway' && setStockNotice(null)}
				anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
			>
				<Alert
					severity="warning"
					variant="filled"
					onClose={() => setStockNotice(null)}
					sx={{ borderRadius: 2.5, fontWeight: 500 }}
				>
					{stockNotice
						? stockNotice.stock > 0
							? `${stockNotice.stock === 1 ? 'Solo queda 1 unidad' : `Solo quedan ${stockNotice.stock} unidades`} de ${stockNotice.name}. Ajustamos la cantidad al máximo disponible.`
							: stockNotice.stock < 0
								? `${stockNotice.name} no tiene precio publicado. Consultanos por WhatsApp.`
								: `${stockNotice.name} no tiene stock disponible en este momento.`
						: ''}
				</Alert>
			</Snackbar>

			<Snackbar
				open={wishlistNotice}
				autoHideDuration={4000}
				onClose={() => setWishlistNotice(false)}
				anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
			>
				<Alert
					severity="info"
					variant="filled"
					onClose={() => setWishlistNotice(false)}
					action={
						<button
							style={{
								background: 'none',
								border: 'none',
								color: '#fff',
								fontWeight: 700,
								cursor: 'pointer',
								fontSize: 13,
								textDecoration: 'underline'
							}}
							onClick={() => {
								setWishlistNotice(false);
								openAccount();
							}}
						>
							Iniciar sesión
						</button>
					}
					sx={{ borderRadius: 2.5, fontWeight: 500 }}
				>
					Guardado en favoritos. Iniciá sesión para sincronizar entre dispositivos.
				</Alert>
			</Snackbar>
		</Root>
	);
}

export default StorePage;
