// --- START OF FILE shop-core.js ---

import { initializeApp } from "https://www.gstatic.com/firebasejs/11.9.0/firebase-app.js";
import { getDatabase, ref, get, push, remove, set, serverTimestamp, update } from "https://www.gstatic.com/firebasejs/11.9.0/firebase-database.js";
import { DEFAULT_BLOCKED_VISITOR_MESSAGE, looksLikeIpv4 } from './ip-block-utils.js';
import { firebaseConfig } from './firebase-config.js';

export function showBlockedVisitorOverlay(message) {
    if (document.getElementById('sb-ip-blocked-overlay')) return;
    const outer = document.createElement('div');
    outer.id = 'sb-ip-blocked-overlay';
    outer.setAttribute('role', 'alert');
    outer.className = 'fixed inset-0 z-[100000] flex items-center justify-center bg-slate-900/97 p-6';
    const inner = document.createElement('div');
    inner.className = 'max-w-md rounded-2xl bg-slate-800 border border-slate-600 p-8 shadow-2xl text-center text-white';
    const icon = document.createElement('i');
    icon.className = 'fa-solid fa-circle-exclamation text-4xl text-amber-400 mb-4 block';
    icon.setAttribute('aria-hidden', 'true');
    const p = document.createElement('p');
    p.className = 'text-lg font-medium leading-relaxed';
    p.textContent = message && String(message).trim() ? String(message).trim() : DEFAULT_BLOCKED_VISITOR_MESSAGE;
    inner.appendChild(icon);
    inner.appendChild(p);
    outer.appendChild(inner);
    document.body.appendChild(outer);
    document.documentElement.style.overflow = 'hidden';
}

// ============================================================
// 1. CONFIGURATION & STATE
// ============================================================

const app = initializeApp(firebaseConfig);
const db = getDatabase(app);
let CURRENT_CLIENT_ID = null;

/** Session replay logs (`visitors/.../logs`) — off to reduce RTDB writes. Match `SESSION_REPLAY_ENABLED` in dashboard.js to re-enable. */
const SESSION_REPLAY_ENABLED = false;

/** Set store client id (e.g. landing page `view.html` before `initCore`). */
export const setClientId = (id) => {
    CURRENT_CLIENT_ID = (id != null && String(id).trim()) ? String(id).trim() : null;
};

// Global State
export const state = {
    /** 'idle' | 'ok' | 'loading' | 'error' — used by themes to avoid infinite skeleton when Firebase fetch fails. */
    storeCatalogFetchStatus: 'idle',
    allProducts: [],
    allCategories: [],
    cartItems: [],
    siteSettings: {}, 
    visitorId: localStorage.getItem('sb_visitor_id') || null,
    deviceHash: localStorage.getItem('sb_device_hash') || null,
    incompleteOrderId: null,
    sessionStartTime: Date.now(),
    tabHiddenTime: 0,
    rageClickCount: 0,
    lastClickTime: 0,
    wealthProfile: { score: 0, class: 'Standard', isRich: false },
    userInfo: {
        deviceType: /Android|iPhone|iPad/i.test(navigator.userAgent) ? 'Mobile' : 'Desktop',
        userAgent: navigator.userAgent,
        screen: `${window.screen.width}x${window.screen.height}`,
        language: navigator.language,
        pixelRatio: window.devicePixelRatio || 1
    }
};

/**
 * Dismiss #loading-overlay (hidden placeholder in HTML) when catalog is ready, or when force (init failure, IP block).
 */
export function tryDismissBootLoadingOverlay(options = {}) {
    const force = !!(options && options.force);
    if (!force) {
        const st = state.storeCatalogFetchStatus || 'idle';
        const n = Array.isArray(state.allProducts) ? state.allProducts.length : 0;
        if (st === 'loading' && n === 0) return;
    }
    const loader = document.getElementById('loading-overlay');
    if (!loader || loader.classList.contains('hidden')) return;
    loader.setAttribute('aria-busy', 'false');
    loader.style.transition = 'opacity 0.15s ease-out';
    loader.style.opacity = '0';
    setTimeout(() => {
        loader.classList.add('hidden');
        loader.style.opacity = '';
        loader.style.transition = '';
    }, 0);
}

/** Shared DOM refs object; populated by theme scripts (e.g. script-default.js initDOM). */
export const doc = {};

/** Categories used in header/menu surfaces; falls back to all categories when not configured. */
export const getMenuCategories = () => {
    const all = Array.isArray(state.allCategories) ? state.allCategories : [];
    const configured = state.siteSettings?.layout?.categoryConfig?.headerCategories;
    const iconMap = state.siteSettings?.layout?.categoryConfig?.headerCategoryIcons;
    const icons = (iconMap && typeof iconMap === 'object') ? iconMap : {};
    const selected = Array.isArray(configured)
        ? configured.map((name) => String(name || '').trim()).filter(Boolean)
        : [];
    if (selected.length === 0) {
        return all.map((cat) => {
            const name = String(cat?.name || '').trim();
            const iconUrl = String(icons[name] || '').trim();
            return iconUrl ? { ...cat, iconUrl } : cat;
        });
    }
    const selectedSet = new Set(selected);
    const filtered = all.filter((cat) => selectedSet.has(String(cat?.name || '').trim()));
    const base = filtered.length > 0 ? filtered : all;
    return base.map((cat) => {
        const name = String(cat?.name || '').trim();
        const iconUrl = String(icons[name] || '').trim();
        return iconUrl ? { ...cat, iconUrl } : cat;
    });
};
if (typeof window !== 'undefined') {
    window.getMenuCategories = getMenuCategories;
}

// ============================================================
// 2. UTILITIES & TRACKING
// ============================================================

export const getEl = (id) => document.getElementById(id);

/** When `?p=` / `?product=` is present but the hash is missing (some in-app browsers strip `#/product/...`), set the product hash. */
export const applyProductIdQueryParamAsHash = () => {
    try {
        const hash = window.location.hash || '';
        if (hash.startsWith('#/') && hash.length > 2) return;
        const q = new URLSearchParams(window.location.search);
        const id = (q.get('p') || q.get('product') || '').trim();
        if (!id) return;
        window.location.hash = '#/product/' + encodeURIComponent(id);
    } catch (e) { /* ignore */ }
};

export const formatPrice = (price) => {
    return new Intl.NumberFormat('bn-BD', { style: 'currency', currency: 'BDT', minimumFractionDigits: 0 }).format(price || 0);
};

/** Current shop subdomain from URL (?site= or hash), for scoped landing links. */
function getShopSubdomainFromBrowser() {
    try {
        const sp = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : '');
        let s = sp.get('site');
        if (!s && typeof window !== 'undefined' && window.location.hash.includes('?site=')) {
            s = window.location.hash.split('?site=')[1].split('&')[0];
            try { s = decodeURIComponent(s); } catch (e) { /* ignore */ }
        }
        return s && String(s).trim() ? String(s).trim() : '';
    } catch (e) { return ''; }
}

/** URL to open when a product is configured to use a landing page; null for normal product-detail routing. */
export const getLandingPageUrlForProduct = (p) => {
    if (!p || p.clickTarget !== 'landing_page' || !p.landingPageSlug) return null;
    const raw = p.landingPageCustomDomain;
    const d = raw != null && String(raw).trim() ? String(raw).trim().replace(/^https?:\/\//i, '').replace(/\/+$/, '') : '';
    if (d) return `https://${d}/`;
    const site = getShopSubdomainFromBrowser();
    const q = site ? `?site=${encodeURIComponent(site)}` : '';
    return `view.html${q}#${p.landingPageSlug}`;
};

export const debounce = (func, wait) => {
    let timeout;
    return function(...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, args), wait);
    };
};

export const showToast = (message, type = 'success') => {
    const container = document.getElementById('toast-container');
    if(!container) { const div = document.createElement('div'); div.id = 'toast-container'; document.body.appendChild(div); }
    const toast = document.createElement('div');
    toast.className = `toast-notification ${type}`;
    const icon = type === 'success' ? '<i class="fa-solid fa-circle-check"></i>' : '<i class="fa-solid fa-circle-exclamation"></i>';
    toast.innerHTML = `${icon} <span>${message}</span>`;
    document.getElementById('toast-container').appendChild(toast);
    requestAnimationFrame(() => toast.classList.add('show'));
    setTimeout(() => { toast.classList.remove('show'); setTimeout(() => toast.remove(), 300); }, 3000);
};

let duplicateOrderModalEscapeHandler = null;
let duplicateOrderModalSubmitGateRelease = null;

function closeDuplicateOrderProtectionModal() {
    const root = document.getElementById('duplicate-order-modal');
    if (root) {
        root.classList.add('hidden');
        root.setAttribute('aria-hidden', 'true');
    }
    document.body.style.overflow = '';
    if (duplicateOrderModalEscapeHandler) {
        document.removeEventListener('keydown', duplicateOrderModalEscapeHandler);
        duplicateOrderModalEscapeHandler = null;
    }
    if (duplicateOrderModalSubmitGateRelease) {
        duplicateOrderModalSubmitGateRelease();
        duplicateOrderModalSubmitGateRelease = null;
    }
}

function digitsOnlyForContact(v) {
    return String(v || '').replace(/\D/g, '');
}

/**
 * Full-screen modal for duplicate-order protection; releases checkout submit gate on dismiss.
 * Uses state.siteSettings.contactInfo.whatsappNumber for WhatsApp; falls back to phone/hotline for tel:.
 */
function showDuplicateOrderProtectionModal(messageText, submitBtns) {
    const root = document.getElementById('duplicate-order-modal');
    const msgEl = document.getElementById('duplicate-order-modal-message');
    const ctaEl = document.getElementById('duplicate-order-modal-cta');
    const waBtn = document.getElementById('duplicate-order-whatsapp-btn');
    const callBtn = document.getElementById('duplicate-order-call-btn');
    const backdrop = document.getElementById('duplicate-order-modal-backdrop');
    const dismiss = document.getElementById('duplicate-order-modal-dismiss');

    if (!root || !msgEl || !ctaEl || !waBtn || !callBtn || !backdrop || !dismiss) {
        showToast(messageText, 'error');
        releaseCheckoutSubmitGate(submitBtns);
        return;
    }

    msgEl.textContent = messageText;

    const ci = (state.siteSettings && state.siteSettings.contactInfo) || {};
    const waDigits = digitsOnlyForContact(ci.whatsappNumber);
    const phoneDigits = digitsOnlyForContact(ci.phoneNumber);
    const hotlineDigits = digitsOnlyForContact(ci.hotlineNumber);

    if (waDigits.length >= 10) {
        const prefilled = encodeURIComponent('আসসালামু আলাইকুম। আমি আরও অর্ডার করতে চাই।');
        waBtn.href = `https://wa.me/${waDigits}?text=${prefilled}`;
        waBtn.classList.remove('hidden');
    } else {
        waBtn.href = '#';
        waBtn.classList.add('hidden');
    }

    let telForCall = '';
    if (phoneDigits.length >= 10) telForCall = phoneDigits;
    else if (hotlineDigits.length >= 10) telForCall = hotlineDigits;
    else if (waDigits.length >= 10) telForCall = waDigits;

    if (!waBtn.classList.contains('hidden')) {
        callBtn.classList.add('hidden');
        callBtn.href = '#';
    } else if (telForCall.length >= 10) {
        callBtn.href = `tel:${telForCall}`;
        callBtn.classList.remove('hidden');
    } else {
        callBtn.href = '#';
        callBtn.classList.add('hidden');
    }

    if (!waBtn.classList.contains('hidden')) {
        ctaEl.textContent = 'আপনি যদি আরও অর্ডার করতে চান, তবে দয়া করে আমাদের হোয়াটসঅ্যাপে যোগাযোগ করুন।';
    } else if (!callBtn.classList.contains('hidden')) {
        ctaEl.textContent = 'আপনি যদি আরও অর্ডার করতে চান, তবে দয়া করে আমাদের ফোন নম্বরে যোগাযোগ করুন।';
    } else {
        ctaEl.textContent = 'সাহায্যের জন্য দোকানের সাথে যোগাযোগ করুন।';
    }

    if (duplicateOrderModalEscapeHandler) {
        document.removeEventListener('keydown', duplicateOrderModalEscapeHandler);
        duplicateOrderModalEscapeHandler = null;
    }

    duplicateOrderModalSubmitGateRelease = () => releaseCheckoutSubmitGate(submitBtns);

    backdrop.onclick = () => closeDuplicateOrderProtectionModal();
    dismiss.onclick = () => closeDuplicateOrderProtectionModal();
    waBtn.onclick = () => {
        if (!waBtn.classList.contains('hidden') && waBtn.getAttribute('href') && waBtn.getAttribute('href') !== '#') {
            setTimeout(() => closeDuplicateOrderProtectionModal(), 0);
        }
    };
    callBtn.onclick = () => {
        if (!callBtn.classList.contains('hidden') && callBtn.getAttribute('href') && callBtn.getAttribute('href') !== '#') {
            setTimeout(() => closeDuplicateOrderProtectionModal(), 0);
        }
    };

    duplicateOrderModalEscapeHandler = (ev) => {
        if (ev.key === 'Escape') closeDuplicateOrderProtectionModal();
    };
    document.addEventListener('keydown', duplicateOrderModalEscapeHandler);

    root.classList.remove('hidden');
    root.setAttribute('aria-hidden', 'false');
    document.body.style.overflow = 'hidden';
}

// --- BROWSER PIXEL TRACKER ---
let tiktokPixelLoaded = false;
let pageViewListenerAttached = false;
let pushPromptRendered = false;
let pushInitStarted = false;
let pushMessageListenerBound = false;
let firebaseMessagingApiPromise = null;

const getFirebaseMessagingApi = async () => {
    if (!firebaseMessagingApiPromise) {
        firebaseMessagingApiPromise = import("https://www.gstatic.com/firebasejs/11.9.0/firebase-messaging.js");
    }
    return firebaseMessagingApiPromise;
};

/**
 * PHP proxies under /api/ — same folder as shop HTML on cPanel (public_html).
 * If your shop lives in a subfolder but api/ is at domain root, set before loading modules:
 *   <script>window.SB_PUBLIC_API_BASE='https://yourdomain.com/api';</script>
 */
function publicApiFileUrl(fileName) {
    const f = String(fileName || '').replace(/^\/+/, '');
    const base =
        typeof window !== 'undefined' && window.SB_PUBLIC_API_BASE != null
            ? String(window.SB_PUBLIC_API_BASE).trim().replace(/\/$/, '')
            : '';
    if (base) return `${base}/${f}`;
    return new URL(`api/${f}`, window.location.href).href;
}

const tiktokProxyUrl = () => publicApiFileUrl('tiktok-events-proxy.php');
const facebookCapiPurchaseProxyUrl = () => publicApiFileUrl('facebook-capi-purchase-proxy.php');

/** Snapshotted when pixels boot or when `storeDataRefreshed` updates settings — avoids stale fbq/ttq after dashboard pixel changes. */
let trackingReconcileBootstrapped = false;
let lastAppliedFbPixelId = null;
let lastAppliedTtPixelKey = '';
let lastAppliedTtEnabled = false;
let storeDataRefreshListenerBound = false;

function resetFacebookPixelRuntime() {
    if (typeof window === 'undefined') return;
    try {
        delete window.fbq;
        delete window._fbq;
    } catch (_) {}
    try {
        document.querySelectorAll('script[src*="connect.facebook.net"][src*="fbevents"]').forEach((n) => {
            try {
                n.parentNode && n.parentNode.removeChild(n);
            } catch (_) {}
        });
    } catch (_) {}
}

/** Re-run after RTDB `siteSettings` refresh so new pixel / TikTok IDs take effect without a full reload. */
function reconcileTrackingConfig() {
    if (typeof window === 'undefined' || typeof document === 'undefined') return;
    const nextFb = String(state.siteSettings?.pixelId || '').trim();
    const tt = getTikTokConfig();
    const nextTtKey = tt.enabled && tt.pixelId ? String(tt.pixelId).trim() : '';
    const nextTtEnabled = Boolean(tt.enabled);

    if (!trackingReconcileBootstrapped) {
        trackingReconcileBootstrapped = true;
        lastAppliedFbPixelId = nextFb;
        lastAppliedTtPixelKey = nextTtKey;
        lastAppliedTtEnabled = nextTtEnabled;
        return;
    }

    const fbChanged = lastAppliedFbPixelId !== nextFb;
    const ttChanged = lastAppliedTtPixelKey !== nextTtKey || lastAppliedTtEnabled !== nextTtEnabled;

    if (!fbChanged && !ttChanged) return;

    if (fbChanged) {
        lastAppliedFbPixelId = nextFb;
        resetFacebookPixelRuntime();
        if (nextFb) initFacebookPixel();
    }

    if (ttChanged) {
        lastAppliedTtPixelKey = nextTtKey;
        lastAppliedTtEnabled = nextTtEnabled;
        if (nextTtEnabled && nextTtKey) {
            if (typeof window.ttq !== 'undefined' && window.ttq && typeof window.ttq.load === 'function') {
                try {
                    window.ttq.load(nextTtKey);
                } catch (_) {}
                tiktokPixelLoaded = true;
            } else {
                initTikTokPixel();
            }
        }
    }

    try {
        trackPixelEvent('PageView');
    } catch (_) {}
}

function bindStoreDataRefreshReconcile() {
    if (storeDataRefreshListenerBound) return;
    storeDataRefreshListenerBound = true;
    document.addEventListener('storeDataRefreshed', () => reconcileTrackingConfig());
}
const visitorIpBlockCheckUrl = () => publicApiFileUrl('visitor-ip-block-check.php');
const visitorPublicIpUrl = () => publicApiFileUrl('visitor-public-ip.php');
const geoIpUrl = () => publicApiFileUrl('geo-ip.php');

/** IP + geo from this origin (real client IP as seen by PHP); null if endpoint fails. */
async function fetchGeoFromOriginServer() {
    try {
        const ctrl = new AbortController();
        const t = setTimeout(() => ctrl.abort(), 10000);
        const res = await fetch(geoIpUrl(), {
            method: 'GET',
            credentials: 'omit',
            signal: ctrl.signal
        });
        clearTimeout(t);
        const data = await res.json().catch(() => null);
        if (!res.ok || !data || data.status !== 'ok') return null;
        const ip = data.ip != null ? String(data.ip).trim() : '';
        if (!ip) return null;
        const rawCity = data.city != null ? String(data.city).trim() : '';
        const city =
            rawCity && rawCity.toLowerCase() !== 'unknown' ? rawCity : 'Unknown';
        const rawRegion = data.region != null ? String(data.region).trim() : '';
        const region =
            rawRegion && rawRegion.toLowerCase() !== 'unknown' ? rawRegion : '';
        const rawCountry = data.country != null ? String(data.country).trim() : '';
        const country =
            rawCountry && rawCountry.toLowerCase() !== 'unknown' ? rawCountry : '';
        const ispRaw = data.isp != null ? String(data.isp).trim() : '';
        const isp = ispRaw && ispRaw.toLowerCase() !== 'unknown' ? ispRaw : 'Unknown';
        return { ip, city, country, isp, region };
    } catch (_) {
        return null;
    }
}

async function fetchVisitorIpFromOriginServer() {
    try {
        const ctrl = new AbortController();
        const t = setTimeout(() => ctrl.abort(), 8000);
        const res = await fetch(visitorPublicIpUrl(), {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            credentials: 'omit',
            body: '{}',
            signal: ctrl.signal
        });
        clearTimeout(t);
        const data = await res.json().catch(() => null);
        if (!res.ok || !data || data.status !== 'ok') return '';
        const ip = data.ip != null ? String(data.ip).trim() : '';
        if (!ip || !looksLikeIpv4(ip)) return '';
        return ip;
    } catch (e) {
        return '';
    }
}
const pushSwUrl = () => new URL('push-sw.js', window.location.href).href;

function readBrowserCookie(name) {
    try {
        const v = `; ${document.cookie}`;
        const parts = v.split(`; ${name}=`);
        if (parts.length === 2) return parts.pop().split(';').shift() || '';
    } catch (_) {}
    return '';
}

/** Meta `fbc` from cookie, or synthetic `fb.1.{unix}.{fbclid}` when landing from ads. */
function resolveMetaFbc(fbclidFromSession) {
    const fromCookie = readBrowserCookie('_fbc').trim();
    if (fromCookie) return fromCookie;
    const cl = fbclidFromSession && String(fbclidFromSession).trim();
    if (!cl) return '';
    return `fb.1.${Math.floor(Date.now() / 1000)}.${cl}`;
}

async function sendFacebookPurchaseCapiProxy(payload) {
    if (!CURRENT_CLIENT_ID || !payload || !payload.orderKey) return false;
    if (state.siteSettings?.facebookConfig?.capiEnabled === false) return false;
    const pixelId = String(state.siteSettings?.pixelId || '').trim();
    if (!pixelId) return false;
    try {
        const ctrl = new AbortController();
        const t = setTimeout(() => ctrl.abort(), 12000);
        const res = await fetch(facebookCapiPurchaseProxyUrl(), {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            credentials: 'same-origin',
            body: JSON.stringify({ clientId: CURRENT_CLIENT_ID, ...payload }),
            signal: ctrl.signal
        });
        clearTimeout(t);
        const data = await res.json().catch(() => null);
        const ok = !!(res.ok && data && data.status === 'success');
        if (!ok) {
            const hint =
                res.status === 404
                    ? 'api/facebook-capi-purchase-proxy.php missing (404). Deploy it beside your shop HTML.'
                    : (data && data.message) || `HTTP ${res.status}`;
            console.warn('[Meta CAPI Purchase]', hint);
        }
        return ok;
    } catch (err) {
        console.warn('[Meta CAPI Purchase] request failed:', err);
        return false;
    }
}

const getTikTokConfig = () => {
    const cfg = state.siteSettings?.tiktokConfig || {};
    return {
        enabled: Boolean(cfg.enabled),
        pixelId: String(cfg.pixelId || '').trim(),
        accessToken: String(cfg.accessToken || '').trim(),
        testEventCode: String(cfg.testEventCode || '').trim()
    };
};

const getPushConfig = () => {
    const cfg = state.siteSettings?.pushConfig || {};
    return {
        enabled: Boolean(cfg.enabled),
        vapidPublicKey: String(cfg.vapidPublicKey || '').trim(),
        defaultIconUrl: String(cfg.defaultIconUrl || '').trim()
    };
};

const sanitizeSubscriptionId = (token) => String(token || '').replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 180) || ('sub_' + Date.now());

const shouldShowPushPrompt = () => {
    if (localStorage.getItem('sb_push_opt_out') === '1') return false;
    if (Notification.permission !== 'default') return false;
    const laterUntil = Number(localStorage.getItem('sb_push_later_until') || 0);
    if (laterUntil && Date.now() < laterUntil) return false;
    return true;
};

const removePushPrompt = () => {
    const el = document.getElementById('push-optin-banner');
    if (el) el.remove();
};

const savePushSubscriber = async (token) => {
    if (!CURRENT_CLIENT_ID || !token) return;
    const id = sanitizeSubscriptionId(token);
    const payload = {
        token,
        active: true,
        platform: /Android|iPhone|iPad|iPod/i.test(navigator.userAgent) ? 'mobile' : 'desktop',
        userAgent: navigator.userAgent || '',
        language: navigator.language || '',
        createdAt: Date.now(),
        updatedAt: Date.now(),
        lastSeenAt: Date.now()
    };
    await update(ref(db, `clients/${CURRENT_CLIENT_ID}/pushSubscribers/${id}`), payload);
};

const requestPushSubscription = async () => {
    const cfg = getPushConfig();
    if (!cfg.enabled || !cfg.vapidPublicKey) {
        showToast('Notifications are not configured yet.', 'error');
        return;
    }
    try {
        const { isSupported, getMessaging, getToken } = await getFirebaseMessagingApi();
        const supported = await isSupported();
        if (!supported) {
            showToast('This browser does not support push notifications.', 'error');
            return;
        }
        const permission = await Notification.requestPermission();
        if (permission !== 'granted') {
            showToast('Notification permission was not granted.', 'error');
            return;
        }
        const registration = await navigator.serviceWorker.register(pushSwUrl());
        const messaging = getMessaging(app);
        const token = await getToken(messaging, { vapidKey: cfg.vapidPublicKey, serviceWorkerRegistration: registration });
        if (!token) {
            showToast('Could not subscribe for notifications.', 'error');
            return;
        }
        await savePushSubscriber(token);
        localStorage.removeItem('sb_push_later_until');
        removePushPrompt();
        showToast('Notifications enabled successfully.');
    } catch (err) {
        console.error('push subscribe', err);
        showToast('Failed to enable notifications.', 'error');
    }
};

const renderPushPrompt = () => {
    if (pushPromptRendered || !shouldShowPushPrompt()) return;
    pushPromptRendered = true;
    const root = document.createElement('div');
    root.id = 'push-optin-banner';
    root.className = 'fixed bottom-4 left-1/2 -translate-x-1/2 z-[70] w-[min(92vw,460px)] rounded-xl border border-gray-200 bg-white shadow-xl p-4';
    root.innerHTML = `
        <p class="text-sm font-semibold text-gray-800 mb-1">Get offers instantly</p>
        <p class="text-xs text-gray-600 mb-3">Allow notifications to receive discounts and order updates.</p>
        <div class="flex gap-2 justify-end">
            <button type="button" id="push-never-btn" class="px-3 py-1.5 rounded-md text-xs border border-gray-300 text-gray-600 hover:bg-gray-50">Never</button>
            <button type="button" id="push-later-btn" class="px-3 py-1.5 rounded-md text-xs border border-gray-300 text-gray-700 hover:bg-gray-50">Later</button>
            <button type="button" id="push-allow-btn" class="px-3 py-1.5 rounded-md text-xs bg-indigo-600 text-white hover:bg-indigo-700">Allow</button>
        </div>
    `;
    document.body.appendChild(root);
    root.querySelector('#push-never-btn')?.addEventListener('click', () => {
        localStorage.setItem('sb_push_opt_out', '1');
        removePushPrompt();
    });
    root.querySelector('#push-later-btn')?.addEventListener('click', () => {
        localStorage.setItem('sb_push_later_until', String(Date.now() + (2 * 24 * 60 * 60 * 1000)));
        removePushPrompt();
    });
    root.querySelector('#push-allow-btn')?.addEventListener('click', () => {
        requestPushSubscription().catch(() => {});
    });
};

const initPushNotifications = async () => {
    if (pushInitStarted) return;
    pushInitStarted = true;
    const cfg = getPushConfig();
    if (!cfg.enabled) return;
    try {
        const { isSupported, getMessaging, getToken, onMessage } = await getFirebaseMessagingApi();
        const supported = await isSupported();
        if (!supported) return;
        const registration = await navigator.serviceWorker.register(pushSwUrl());
        if (Notification.permission === 'granted' && cfg.vapidPublicKey) {
            const messaging = getMessaging(app);
            const token = await getToken(messaging, { vapidKey: cfg.vapidPublicKey, serviceWorkerRegistration: registration });
            if (token) await savePushSubscriber(token);
            if (!pushMessageListenerBound) {
                pushMessageListenerBound = true;
                onMessage(messaging, (payload) => {
                    const title = payload?.notification?.title || 'New notification';
                    const body = payload?.notification?.body || '';
                    showToast(`${title}${body ? `: ${body}` : ''}`);
                });
            }
            return;
        }
        setTimeout(() => renderPushPrompt(), 6500);
    } catch (err) {
        console.error('init push', err);
    }
};

const mapTikTokEventName = (eventName) => {
    if (eventName === 'Purchase') return 'CompletePayment';
    return eventName;
};

/** Meta Pixel standard events — others use fbq('trackCustom', ...) */
const META_PIXEL_STANDARD_EVENTS = new Set([
    'PageView', 'ViewContent', 'AddToCart', 'InitiateCheckout', 'Purchase', 'Search',
    'AddToWishlist', 'AddPaymentInfo', 'CompleteRegistration', 'Contact', 'Lead',
    'Subscribe', 'StartTrial', 'Schedule', 'SubmitApplication', 'FindLocation', 'Donate'
]);

const makeEventId = (eventName) => {
    const rnd = Math.random().toString(36).slice(2, 9);
    return `sb_${String(eventName || 'event').toLowerCase()}_${Date.now()}_${rnd}`;
};

export const initFacebookPixel = () => {
    const pixelId = String(state.siteSettings?.pixelId || '').trim();
    if (!pixelId) return;
    !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js');
    fbq('init', pixelId);
};

export const initTikTokPixel = () => {
    const cfg = getTikTokConfig();
    if (!cfg.enabled || !cfg.pixelId) return;
    if (typeof window.ttq !== 'undefined') {
        try { window.ttq.load(cfg.pixelId); } catch (_) {}
        tiktokPixelLoaded = true;
        return;
    }
    !function (w, d, t) {
        w.TiktokAnalyticsObject = t;
        const ttq = w[t] = w[t] || [];
        ttq.methods = ['page', 'track', 'identify', 'instances', 'debug', 'on', 'off', 'once', 'ready', 'alias', 'group', 'enableCookie', 'disableCookie'];
        ttq.setAndDefer = function (obj, method) { obj[method] = function () { obj.push([method].concat(Array.prototype.slice.call(arguments, 0))); }; };
        for (let i = 0; i < ttq.methods.length; i++) ttq.setAndDefer(ttq, ttq.methods[i]);
        ttq.instance = function (pixel) {
            const inst = ttq._i[pixel] || [];
            for (let i = 0; i < ttq.methods.length; i++) ttq.setAndDefer(inst, ttq.methods[i]);
            return inst;
        };
        ttq.load = function (pixel) {
            const src = 'https://analytics.tiktok.com/i18n/pixel/events.js';
            ttq._i = ttq._i || {};
            ttq._i[pixel] = [];
            ttq._i[pixel]._u = src;
            ttq._t = ttq._t || {};
            ttq._t[pixel] = +new Date();
            ttq._o = ttq._o || {};
            ttq._o[pixel] = {};
            const s = d.createElement('script');
            s.type = 'text/javascript';
            s.async = true;
            s.src = src + '?sdkid=' + pixel + '&lib=' + t;
            const x = d.getElementsByTagName('script')[0];
            x.parentNode.insertBefore(s, x);
        };
        ttq.load(cfg.pixelId);
        ttq.page();
    }(window, document, 'ttq');
    tiktokPixelLoaded = true;
};

const sendTikTokServerEvent = async (eventName, data = {}, eventId = '') => {
    const cfg = getTikTokConfig();
    if (!cfg.enabled || !cfg.pixelId || !cfg.accessToken || !CURRENT_CLIENT_ID) return;
    const body = {
        clientId: CURRENT_CLIENT_ID,
        event_name: mapTikTokEventName(eventName),
        event_id: eventId || makeEventId(eventName),
        event_time: Math.floor(Date.now() / 1000),
        event_source_url: window.location.href,
        custom_data: data,
        user_data: {
            external_id: state.visitorId || '',
            client_user_agent: navigator.userAgent || '',
            client_ip_address: ''
        }
    };
    if (cfg.testEventCode) body.test_event_code = cfg.testEventCode;
    try {
        await fetch(tiktokProxyUrl(), {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            credentials: 'same-origin',
            body: JSON.stringify(body)
        });
    } catch (_) {}
};

export const trackPixelEvent = (eventName, data = {}) => {
    const eventId = data?.event_id || makeEventId(eventName);
    const pixelPayload = { ...data };
    delete pixelPayload.event_id;
    const useStandard = META_PIXEL_STANDARD_EVENTS.has(eventName);
    if (typeof fbq === 'function') {
        try {
            if (useStandard) {
                fbq('track', eventName, pixelPayload, { eventID: eventId });
            } else {
                fbq('trackCustom', eventName, pixelPayload, { eventID: eventId });
            }
        } catch (_) {
            try {
                if (useStandard) fbq('track', eventName, pixelPayload);
                else fbq('trackCustom', eventName, pixelPayload);
            } catch (_) { /* noop */ }
        }
    }
    const cfg = getTikTokConfig();
    if (cfg.enabled && cfg.pixelId && typeof window.ttq === 'object' && typeof window.ttq.track === 'function') {
        try { window.ttq.track(mapTikTokEventName(eventName), { ...data, event_id: eventId }); } catch (_) {}
    }
    sendTikTokServerEvent(eventName, data, eventId).catch(() => {});
};

let phase1PixelEngagementInitialized = false;

/** Phase 1: TimeOnSite60s, Scroll50, WhatsApp Contact (once per tab where noted). */
export function initPhase1PixelEngagement() {
    if (phase1PixelEngagementInitialized || typeof window === 'undefined') return;
    phase1PixelEngagementInitialized = true;

    try {
        if (!sessionStorage.getItem('sb_px_time60')) {
            window.setTimeout(() => {
                try {
                    if (sessionStorage.getItem('sb_px_time60')) return;
                    sessionStorage.setItem('sb_px_time60', '1');
                    trackPixelEvent('TimeOnSite60s', {});
                } catch (_) { /* noop */ }
            }, 60000);
        }
    } catch (_) { /* private mode */ }

    let scroll50Fired = false;
    const onScroll = () => {
        if (scroll50Fired) return;
        try {
            if (sessionStorage.getItem('sb_px_scroll50')) {
                scroll50Fired = true;
                window.removeEventListener('scroll', onScroll, { capture: true });
                return;
            }
            const docEl = document.documentElement;
            const body = document.body;
            const scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop || 0;
            const scrollHeight = Math.max(body.scrollHeight, docEl.scrollHeight, 1);
            const winH = window.innerHeight || 1;
            const maxScroll = Math.max(1, scrollHeight - winH);
            if (scrollTop / maxScroll >= 0.499) {
                scroll50Fired = true;
                sessionStorage.setItem('sb_px_scroll50', '1');
                trackPixelEvent('Scroll50', {});
                window.removeEventListener('scroll', onScroll, { capture: true });
            }
        } catch (_) { /* noop */ }
    };
    window.addEventListener('scroll', onScroll, { passive: true, capture: true });
    window.requestAnimationFrame(onScroll);

    document.addEventListener('click', (e) => {
        const a = e.target && e.target.closest && e.target.closest('a[href]');
        if (!a) return;
        const href = String(a.getAttribute('href') || '').toLowerCase();
        if (!href) return;
        if (href.includes('wa.me') || href.includes('whatsapp.com') || href.includes('api.whatsapp.com')) {
            try {
                trackPixelEvent('Contact', { content_name: 'WhatsApp', content_category: 'messaging' });
            } catch (_) { /* noop */ }
        }
    }, true);
}

let phase2PixelIntentInitialized = false;

/** Phase 2: checkout form start (name/phone first input), once per tab session. */
export function initPhase2PixelIntent() {
    if (phase2PixelIntentInitialized || typeof window === 'undefined') return;
    phase2PixelIntentInitialized = true;

    document.addEventListener(
        'input',
        (e) => {
            try {
                const t = e.target;
                if (!t || !t.closest) return;
                if (!t.closest('#checkout-form')) return;
                if (t.id !== 'customer-name' && t.id !== 'customer-phone') return;
                if (sessionStorage.getItem('sb_px_order_form_started')) return;
                const v = String(t.value || '').trim();
                if (!v) return;
                sessionStorage.setItem('sb_px_order_form_started', '1');
                trackPixelEvent('OrderFormStarted', {});
            } catch (_) { /* noop */ }
        },
        true
    );
}

let phase3PixelDropoffInitialized = false;

const RAGE_CLICK_WINDOW_MS = 2500;
const RAGE_CLICK_THRESHOLD = 5;
const ERROR_PIXEL_MAX_PER_SESSION = 10;

/** Phase 3: checkout/cart abandon on leave, rage clicks, global JS errors. */
export function initPhase3PixelDropoff() {
    if (phase3PixelDropoffInitialized || typeof window === 'undefined') return;
    phase3PixelDropoffInitialized = true;

    const fireDropoffIfNeeded = () => {
        try {
            if (document.visibilityState !== 'hidden') return;

            const purchaseDone = sessionStorage.getItem('sb_px_purchase_success');
            if (purchaseDone) return;

            if (sessionStorage.getItem('sb_px_order_form_started') && !sessionStorage.getItem('sb_px_checkout_abandoned_sent')) {
                sessionStorage.setItem('sb_px_checkout_abandoned_sent', '1');
                trackPixelEvent('CheckoutAbandoned', {});
            }

            const items = state.cartItems || [];
            if (items.length > 0 && !sessionStorage.getItem('sb_px_cart_abandoned_sent')) {
                const total = items.reduce((sum, i) => sum + (Number(i.price) || 0) * (Number(i.quantity) || 0), 0);
                sessionStorage.setItem('sb_px_cart_abandoned_sent', '1');
                trackPixelEvent('CartAbandoned', { value: total, currency: 'BDT' });
            }
        } catch (_) { /* noop */ }
    };

    document.addEventListener('visibilitychange', fireDropoffIfNeeded);
    window.addEventListener('pagehide', fireDropoffIfNeeded);

    /** Same-target burst clicks (friction signal). */
    const rageTimestampsByTarget = new WeakMap();
    document.addEventListener(
        'click',
        (e) => {
            try {
                const t = e.target;
                if (!t || typeof Node === 'undefined' || !(t instanceof Node) || t.nodeType !== 1) return;
                const now = Date.now();
                let arr = rageTimestampsByTarget.get(t);
                if (!arr) {
                    arr = [];
                    rageTimestampsByTarget.set(t, arr);
                }
                arr.push(now);
                const cutoff = now - RAGE_CLICK_WINDOW_MS;
                while (arr.length && arr[0] < cutoff) arr.shift();
                if (arr.length >= RAGE_CLICK_THRESHOLD) {
                    arr.length = 0;
                    state.rageClickCount = (state.rageClickCount || 0) + 1;
                    trackPixelEvent('RageClick', {});
                }
            } catch (_) { /* noop */ }
        },
        true
    );

    let errorPixelCount = 0;
    const fireErrorPixel = (msg) => {
        if (errorPixelCount >= ERROR_PIXEL_MAX_PER_SESSION) return;
        const s = String(msg || 'unknown').slice(0, 500);
        if (!s.trim()) return;
        errorPixelCount++;
        try {
            trackPixelEvent('ErrorEncountered', { error_message: s });
        } catch (_) { /* noop */ }
    };

    window.addEventListener('error', (ev) => {
        const parts = [ev.message, ev.filename, ev.lineno, ev.colno, ev.error && ev.error.stack].filter(Boolean);
        fireErrorPixel(parts.join(' | '));
    });

    window.addEventListener('unhandledrejection', (ev) => {
        const r = ev.reason;
        const msg = r && typeof r === 'object' && 'message' in r ? r.message : String(r);
        fireErrorPixel(`unhandledrejection: ${msg}`);
    });
}

// ============================================================
// 3. ULTIMATE SPY ENGINE (The "Wealth Detector")
// ============================================================

const generateVisitorId = () => 'v_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5);

const getCanvasFingerprint = () => {
    try {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        ctx.textBaseline = "top";
        ctx.font = "14px 'Arial'";
        ctx.fillStyle = "#f60";
        ctx.fillRect(125, 1, 62, 20);
        ctx.fillStyle = "#069";
        ctx.fillText("StoreBuilder_Spy_v1", 2, 15);
        const dataURL = canvas.toDataURL();
        let hash = 0;
        for (let i = 0; i < dataURL.length; i++) {
            const char = dataURL.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash;
        }
        return 'dev_' + Math.abs(hash).toString(16);
    } catch (e) { return 'blocked'; }
};

const checkNetworkSpeed = async () => {
    const start = Date.now();
    try {
        await fetch('https://www.google.com/favicon.ico', { mode: 'no-cors', cache: 'force-cache' });
        const latency = Date.now() - start;
        return latency + 'ms';
    } catch (e) { return 'Unknown'; }
};

const getAdvancedGPUInfo = () => {
    try {
        const canvas = document.createElement('canvas');
        const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
        if (gl) {
            const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
            if (debugInfo) {
                return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
            }
        }
    } catch (e) {}
    return "Generic GPU";
};

const calculateWealthScore = (gpu, deviceModel, networkType, isDataSaver) => {
    let score = 50; 
    let signals = [];
    const g = (gpu || '').toLowerCase();
    const d = (deviceModel || '').toLowerCase();

    if (g.includes('apple') || d.includes('iphone')) {
        if (g.includes('a17') || g.includes('a16') || d.includes('pro')) { score += 35; signals.push('Flagship iPhone'); } 
        else if (g.includes('a15') || g.includes('a14')) { score += 25; signals.push('Modern iPhone'); } 
        else { score += 10; signals.push('Older iPhone'); }
    } 
    else if (g.includes('adreno')) {
        if (g.includes('750') || g.includes('740') || g.includes('730')) { score += 30; signals.push('Flagship Android'); } 
        else if (g.includes('660') || g.includes('650') || g.includes('642')) { score += 15; signals.push('Mid-Range Android'); } 
        else { score -= 10; signals.push('Budget Android'); }
    }

    if (window.devicePixelRatio >= 3) { score += 10; signals.push('Super Retina'); }
    else if (window.devicePixelRatio < 2) { score -= 10; }

    if (!isDataSaver && networkType === '4g') { score += 10; signals.push('Premium 4G User'); } 
    else if (isDataSaver) { score -= 15; signals.push('Data Saver Mode'); }

    let wealthClass = 'Standard';
    if (score >= 80) wealthClass = 'Elite';
    else if (score >= 60) wealthClass = 'Aspiring';

    score = Math.min(100, Math.max(0, score));
    return { score, class: wealthClass, signals: signals.join(', ') };
};

const parseBrowserName = (ua) => {
    if (!ua || typeof ua !== 'string') return '';
    const s = ua.slice(0, 600);
    if (/Edg\//.test(s)) return 'Microsoft Edge';
    if (/OPR\/|Opera\//i.test(s)) return 'Opera';
    if (/Chrome\//.test(s)) return 'Google Chrome';
    if (/Firefox\//i.test(s)) return 'Mozilla Firefox';
    if (/SamsungBrowser\//i.test(s)) return 'Samsung Internet';
    if (/Safari\//.test(s) && !/Chrome|Chromium|Edg/i.test(s)) return 'Safari';
    return 'Web browser';
};

const getDeviceInfo = async () => {
    let model = "Generic Device";
    let platform = navigator.platform;
    if (navigator.userAgentData && navigator.userAgentData.getHighEntropyValues) {
        try {
            const ua = await navigator.userAgentData.getHighEntropyValues(['model', 'platform']);
            if (ua.model) model = ua.model;
            if (ua.platform) platform = ua.platform;
        } catch (e) {}
    } else if (/iPhone|iPad/.test(navigator.userAgent)) {
        model = 'iPhone (Masked)';
    }
    return { model, platform };
};

const emptyGeo = () => ({ ip: 'Unknown', city: 'Unknown', country: '', region: '', isp: 'Unknown' });

/** Single round-trip for IP + geo (checkout intel + IP block gate). */
const fetchGeoFromProviders = async () => {
    const fallback = emptyGeo();
    const fromOrigin = await fetchGeoFromOriginServer();
    if (fromOrigin && fromOrigin.ip) return fromOrigin;
    try {
        const res = await fetch('https://ipapi.co/json/');
        if (res.ok) {
            const d = await res.json();
            if (!d || d.error || d.reason) throw new Error('ipapi error');
            const geo = {
                ip: d.ip,
                city: d.city,
                country: d.country_name || '',
                isp: d.org || 'Unknown',
                region: d.region || ''
            };
            if (geo.ip) return geo;
        }
    } catch (e) {}
    try {
        const res = await fetch('https://ipwho.is/json/');
        if (res.ok) {
            const d = await res.json();
            if (!d || d.success === false) return null;
            return {
                ip: d.ip || fallback.ip,
                city: d.city || '',
                country: d.country || '',
                isp: (d.connection && d.connection.isp) || d.isp || 'Unknown',
                region: d.region || ''
            };
        }
    } catch (e) {}
    return null;
};

const getGeolocation = async () => {
    const g = await fetchGeoFromProviders();
    return g || emptyGeo();
};

/** Public IP only (for block list check); empty string if unknown. */
export const fetchPublicIpQuick = async () => {
    const g = await fetchGeoFromProviders();
    if (!g || !g.ip || g.ip === 'Unknown') return '';
    return String(g.ip).trim();
};

const getEnvData = () => {
    const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
    return {
        darkMode: window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'Yes' : 'No',
        connectionType: conn ? conn.effectiveType : 'unknown',
        isDataSaver: conn ? conn.saveData : false
    };
};

// 3.6 MASTER DATA COLLECTOR
export const gatherAllIntel = async () => {
    try {
        const [device, geo, ping] = await Promise.all([
            getDeviceInfo(),
            getGeolocation(),
            checkNetworkSpeed()
        ]);
        
        const env = getEnvData();
        const gpuRenderer = getAdvancedGPUInfo();
        
        let batteryInfo = { level: 'N/A', charging: 'N/A' };
        if (navigator.getBattery) {
            try {
                const b = await navigator.getBattery();
                batteryInfo = { level: Math.round(b.level * 100) + '%', charging: b.charging ? 'Yes' : 'No' };
            } catch(e) {}
        }

        const wealthData = calculateWealthScore(gpuRenderer, device.model, env.connectionType, env.isDataSaver);
        state.wealthProfile = wealthData;

        const locParts = [geo.city, geo.region, geo.country].filter(
            (p) => p && String(p).trim() && String(p) !== 'Unknown'
        );
        const location = locParts.join(', ');

        return {
            deviceModel: device.model,
            os: device.platform,
            gpu: gpuRenderer,
            wealthScore: wealthData.score,
            wealthClass: wealthData.class, 
            wealthSignals: wealthData.signals,
            ip: geo.ip,
            city: geo.city,
            region: geo.region,
            country: geo.country,
            isp: geo.isp,
            browser: parseBrowserName(navigator.userAgent),
            location,
            networkType: env.connectionType,
            batteryLevel: batteryInfo.level,
            isCharging: batteryInfo.charging
        };
    } catch (error) {
        console.warn("Intel gathering failed", error);
        return {
            deviceModel: 'Unknown',
            ip: 'Unknown',
            browser: parseBrowserName(typeof navigator !== 'undefined' ? navigator.userAgent : ''),
            location: '',
            gpu: 'Generic GPU',
            wealthScore: 0,
            wealthClass: 'Standard',
            networkType: 'unknown'
        };
    }
};

// ============================================================
// 4. SIGNAL BROADCASTER
// ============================================================

export const broadcastWealthSignal = async (intel) => {
    const wc = intel.wealthClass || 'Standard';
    /** Canonical custom event names for Meta / TikTok / CAPI (matches dashboard Elite / Aspiring / Standard). */
    const pixelWealthEvent = wc === 'Elite' || wc === 'Aspiring' || wc === 'Standard' ? wc : 'Standard';

    if (typeof fbq === 'function') {
        fbq('trackCustom', pixelWealthEvent, {
            wealth_score: intel.wealthScore,
            device_gpu: intel.gpu,
            network_speed: intel.networkType
        });
    }

    const cfg = getTikTokConfig();
    if (cfg.enabled && cfg.pixelId && typeof window.ttq === 'object' && typeof window.ttq.track === 'function') {
        try { window.ttq.track(pixelWealthEvent, { wealth_score: intel.wealthScore, gpu_model: intel.gpu }); } catch (_) {}
    }
    sendTikTokServerEvent(pixelWealthEvent, { wealth_score: intel.wealthScore, gpu_model: intel.gpu }).catch(() => {});

    const token = state.siteSettings?.facebookConfig?.accessToken;
    const pixelId = state.siteSettings?.pixelId;

    if (token && pixelId) {
        try {
            const payload = {
                data: [{
                    event_name: pixelWealthEvent,
                    event_time: Math.floor(Date.now() / 1000),
                    event_source_url: window.location.href,
                    action_source: "website",
                    user_data: { client_ip_address: intel.ip, client_user_agent: navigator.userAgent },
                    custom_data: { wealth_score: intel.wealthScore, gpu_model: intel.gpu }
                }],
                access_token: token
            };
            await fetch(`https://graph.facebook.com/v19.0/${pixelId}/events`, {
                method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload)
            });
        } catch (e) {}
    }
};

// ============================================================
// 5. SESSION & ANALYTICS INIT
// ============================================================

let visitorWealthRescoreTimer = null;

/** Debounced RTDB merge after meaningful interaction — updates wealth fields without re-firing pixel custom events. */
function scheduleVisitorWealthRescore() {
    if (!CURRENT_CLIENT_ID || !state.visitorId) return;
    if (visitorWealthRescoreTimer) clearTimeout(visitorWealthRescoreTimer);
    visitorWealthRescoreTimer = setTimeout(async () => {
        visitorWealthRescoreTimer = null;
        try {
            const intel = await gatherAllIntel();
            await update(ref(db, `clients/${CURRENT_CLIENT_ID}/visitors/${state.visitorId}`), {
                lastActive: serverTimestamp(),
                wealthScore: intel.wealthScore,
                wealthClass: intel.wealthClass,
                intelPending: false,
                meta: { ...state.userInfo, ...intel }
            });
        } catch (e) {
            console.warn('visitor wealth rescore', e);
        }
    }, 4500);
}

const initVisitorSession = async () => {
    if (!CURRENT_CLIENT_ID) return;

    if (!state.visitorId) {
        state.visitorId = generateVisitorId();
        localStorage.setItem('sb_visitor_id', state.visitorId);
    }

    if (!state.deviceHash) {
        state.deviceHash = getCanvasFingerprint();
        localStorage.setItem('sb_device_hash', state.deviceHash);
    }

    document.addEventListener('click', () => {
        update(ref(db, `clients/${CURRENT_CLIENT_ID}/visitors/${state.visitorId}`), { lastActive: serverTimestamp() }).catch(()=>{});
    });

    // Early stub so dashboard / Detective sees the visitor immediately (wealth refined shortly after).
    const visitorStub = {
        id: state.visitorId,
        deviceId: state.deviceHash,
        meta: { ...state.userInfo },
        source: document.referrer || 'Direct',
        lastActive: serverTimestamp(),
        status: 'Online',
        wealthScore: 0,
        wealthClass: 'Standard',
        intelPending: true
    };
    update(ref(db, `clients/${CURRENT_CLIENT_ID}/visitors/${state.visitorId}`), visitorStub).catch((e) => console.warn('visitor stub', e));

    // Defer full intel + replay until after first storefront paint (reduces first-load contention).
    const INTEL_DELAY_MS = 2800;
    setTimeout(async () => {
        const intel = await gatherAllIntel();
        broadcastWealthSignal(intel);

        const sessionData = {
            id: state.visitorId,
            deviceId: state.deviceHash,
            meta: { ...state.userInfo, ...intel },
            source: document.referrer || 'Direct',
            lastActive: serverTimestamp(),
            status: 'Online',
            wealthScore: intel.wealthScore,
            wealthClass: intel.wealthClass,
            intelPending: false
        };
        update(ref(db, `clients/${CURRENT_CLIENT_ID}/visitors/${state.visitorId}`), sessionData).catch((e) => console.warn('visitor intel write', e));
        if (SESSION_REPLAY_ENABLED) initSessionReplay();
    }, INTEL_DELAY_MS);
};

// ============================================================
// SESSION REPLAY DATA (for later video-like replay)
// ============================================================
function getSafeSelector(el) {
    if (!el || !el.tagName) return '';
    const tag = (el.tagName || '').toLowerCase();
    if (el.id && !el.id.match(/^\d/)) return '#' + el.id;
    let s = tag;
    if (el.className && typeof el.className === 'string') {
        const c = el.className.trim().split(/\s+/).filter(c => c && !c.match(/^(swiper|fa-)/)).slice(0, 2).join('.');
        if (c) s += '.' + c;
    }
    if (el.parentElement) {
        const idx = Array.from(el.parentElement.children).indexOf(el) + 1;
        if (idx > 0) s += ':nth-child(' + idx + ')';
    }
    return s.substring(0, 120);
}

function pushSessionLog(payload) {
    if (!SESSION_REPLAY_ENABLED) return;
    if (!CURRENT_CLIENT_ID || !state.visitorId) return;
    const logRef = push(ref(db, `clients/${CURRENT_CLIENT_ID}/visitors/${state.visitorId}/logs`));
    set(logRef, { ...payload, timestamp: serverTimestamp() }).catch(() => {});
}

let lastClickLogTime = 0;
const CLICK_LOG_THROTTLE_MS = 600;
let lastScrollLogTime = 0;
const SCROLL_LOG_THROTTLE_MS = 150;

function logScroll() {
    const now = Date.now();
    if (now - lastScrollLogTime < SCROLL_LOG_THROTTLE_MS) return;
    lastScrollLogTime = now;
    const path = window.location.hash || window.location.pathname || '/';
    pushSessionLog({
        type: 'scroll',
        action: 'Scroll',
        scrollX: window.scrollX || 0,
        scrollY: window.scrollY || 0,
        path: path.substring(0, 200)
    });
}

function logPageView() {
    const path = window.location.hash || window.location.pathname || '/';
    const url = window.location.href || '';
    const displayPath = path === '' || path === '#' ? 'Home' : path.replace('#/', '');
    pushSessionLog({ type: 'pageview', action: 'Page View', details: displayPath, path: path.substring(0, 200), url: url.substring(0, 300) });
    setTimeout(logScroll, 50);
}

function logClick(e) {
    const now = Date.now();
    if (now - lastClickLogTime < CLICK_LOG_THROTTLE_MS) return;
    const target = e.target;
    if (target && (target.type === 'password' || target.getAttribute('type') === 'password')) return;
    if (target && (target.isContentEditable || (target.tagName === 'INPUT' && target.type === 'text') || target.tagName === 'TEXTAREA')) {
        lastClickLogTime = now;
        return;
    }
    lastClickLogTime = now;
    const path = window.location.hash || window.location.pathname || '/';
    const rect = target ? target.getBoundingClientRect() : { left: 0, top: 0 };
    const x = Math.round((rect.left + rect.width / 2));
    const y = Math.round((rect.top + rect.height / 2));
    const selector = getSafeSelector(target);
    pushSessionLog({ type: 'click', action: 'Click', details: selector || 'element', selector: selector || 'unknown', x, y, path: path.substring(0, 200), viewportW: window.innerWidth || 0, viewportH: window.innerHeight || 0 });
}

function initSessionReplay() {
    if (!SESSION_REPLAY_ENABLED) return;
    if (!CURRENT_CLIENT_ID || !state.visitorId) return;
    logPageView();
    window.addEventListener('hashchange', logPageView);
    document.addEventListener('click', logClick, true);
    window.addEventListener('scroll', logScroll, { passive: true });
}

// ============================================================
// 6. CART & CHECKOUT LOGIC
// ============================================================

/** Get quantity of a product in cart (for simple product pass variantId as null). */
export const getCartQuantity = (productId, variantId = null) => {
    const cartKey = variantId ? `${productId}_${variantId}` : String(productId);
    const item = state.cartItems.find(i => (i.variantId ? `${i.id}_${i.variantId}` : String(i.id)) === cartKey);
    return item ? item.quantity : 0;
};

/** Decrease quantity by 1; remove item if becomes 0. */
export const decreaseProductFromCart = (productId, variantId = null) => {
    const cartKey = variantId ? `${productId}_${variantId}` : String(productId);
    const item = state.cartItems.find(i => (i.variantId ? `${i.id}_${i.variantId}` : String(i.id)) === cartKey);
    if (!item) return;
    item.quantity--;
    if (item.quantity <= 0) state.cartItems = state.cartItems.filter(i => (i.variantId ? `${i.id}_${i.variantId}` : String(i.id)) !== cartKey);
    saveCart(); dispatchCartUpdate();
};

export const addProductToCart = (productId, variantId = null, showFeedback = true, options = {}) => {
    const priceOverride = options.priceOverride != null && options.priceOverride !== '' ? Number(options.priceOverride) : null;
    const useOverride = priceOverride != null && !Number.isNaN(priceOverride) && priceOverride > 0;

    const product = state.allProducts.find(p => String(p.id) === String(productId));
    if (!product) return;

    let itemToAdd, cartKey;

    if (product.hasVariants && variantId && product.variants && product.variants[variantId]) {
        const variant = product.variants[variantId];
        cartKey = `${product.id}_${variantId}`;
        const unitPrice = useOverride ? priceOverride : variant.salePrice;
        itemToAdd = { id: product.id, variantId: variantId, name: product.name, variantName: variant.name, price: unitPrice, image: product.imageUrl, quantity: 1 };
    } else if (product.hasVariants && !variantId) {
        const keys = Object.keys(product.variants || {});
        const firstKey = keys.find((k) => {
            const v = product.variants[k];
            if (!v || typeof v !== 'object') return false;
            const sp = Number(v.salePrice);
            return Number.isFinite(sp) && sp > 0;
        });
        if (firstKey) return addProductToCart(productId, firstKey, showFeedback, options);
        cartKey = String(product.id);
        const fallbackPrice = Number(product.salePrice);
        const unitPrice = useOverride ? priceOverride : fallbackPrice;
        if (!Number.isFinite(Number(unitPrice)) || Number(unitPrice) <= 0) return;
        itemToAdd = { id: product.id, variantId: null, name: product.name, price: Number(unitPrice), image: product.imageUrl, quantity: 1 };
    } else {
        cartKey = product.id;
        const unitPrice = useOverride ? priceOverride : product.salePrice;
        itemToAdd = { id: product.id, variantId: null, name: product.name, price: unitPrice, image: product.imageUrl, quantity: 1 };
    }

    const existing = state.cartItems.find(i => (i.variantId ? `${i.id}_${i.variantId}` : i.id) === cartKey);
    if (existing) existing.quantity++; else state.cartItems.push(itemToAdd);

    saveCart(); dispatchCartUpdate();

    if (showFeedback) {
        showToast(`${itemToAdd.name} কার্টে যুক্ত হয়েছে!`);
        trackPixelEvent('AddToCart', { content_name: itemToAdd.name, content_ids: [itemToAdd.id], value: itemToAdd.price, currency: 'BDT' });
        scheduleVisitorWealthRescore();

        if (SESSION_REPLAY_ENABLED) {
            const logRef = push(ref(db, `clients/${CURRENT_CLIENT_ID}/visitors/${state.visitorId}/logs`));
            set(logRef, { type: 'cart', action: 'Add to Cart', details: `Added ${itemToAdd.name}`, timestamp: serverTimestamp() }).catch(()=>{});
        }
    }
};

/**
 * Add multiple products in one action with optional bundle discount (% off each unit price).
 * @param {{ id: string, variantId?: string|null }[]} entries
 * @param {number} discountPercent 0–50
 * @param {boolean} showFeedback toast on last item only
 */
export const addBundleProductsToCart = (entries, discountPercent = 0, showFeedback = true) => {
    if (!entries || !entries.length) return;
    const pct = Math.min(50, Math.max(0, Number(discountPercent) || 0));
    const factor = pct > 0 ? (100 - pct) / 100 : 1;
    entries.forEach((entry, idx) => {
        const p = state.allProducts.find(x => String(x.id) === String(entry.id));
        if (!p) return;
        let vid = entry.variantId || null;
        let basePrice;
        if (p.hasVariants && p.variants) {
            if (!vid) vid = Object.keys(p.variants)[0];
            if (!vid) return;
            basePrice = Number(p.variants[vid]?.salePrice);
        } else {
            basePrice = Number(p.salePrice);
        }
        if (Number.isNaN(basePrice) || basePrice <= 0) return;
        const discounted = Math.max(0.01, Math.round(basePrice * factor * 100) / 100);
        const last = idx === entries.length - 1;
        addProductToCart(p.id, vid, showFeedback && last, { priceOverride: discounted });
    });
};

export const updateCartUI = () => { dispatchCartUpdate(); };
const saveCart = () => { localStorage.setItem('sb_cart_items', JSON.stringify(state.cartItems)); };
const HIGH_VALUE_CART_THRESHOLD = 3000;

const dispatchCartUpdate = () => {
    const total = state.cartItems.reduce((sum, i) => sum + (i.price * i.quantity), 0);
    document.dispatchEvent(new CustomEvent('cartUpdated', { detail: { items: state.cartItems, total: total } }));
    try {
        if (!state.cartItems.length) {
            sessionStorage.removeItem('sb_px_cart_abandoned_sent');
        } else {
            sessionStorage.removeItem('sb_px_purchase_success');
        }
        if (total >= HIGH_VALUE_CART_THRESHOLD && !sessionStorage.getItem('sb_px_high_value_cart')) {
            sessionStorage.setItem('sb_px_high_value_cart', '1');
            trackPixelEvent('HighValueCartReached', { value: total, currency: 'BDT' });
        }
    } catch (_) { /* noop */ }
};

document.addEventListener('click', (e) => {
    const incBtn = e.target.closest('.increase-qty-btn');
    if (incBtn) { const id = incBtn.dataset.id; const item = state.cartItems.find(i => (i.variantId ? `${i.id}_${i.variantId}` : i.id) === id); if(item) { item.quantity++; saveCart(); dispatchCartUpdate(); } }
    const decBtn = e.target.closest('.decrease-qty-btn');
    if (decBtn) { const id = decBtn.dataset.id; const item = state.cartItems.find(i => (i.variantId ? `${i.id}_${i.variantId}` : i.id) === id); if(item) { item.quantity--; if(item.quantity <= 0) state.cartItems = state.cartItems.filter(i => (i.variantId ? `${i.id}_${i.variantId}` : i.id) !== id); saveCart(); dispatchCartUpdate(); } }
    const removeBtn = e.target.closest('.remove-from-cart-btn');
    if (removeBtn) { const id = removeBtn.dataset.id; state.cartItems = state.cartItems.filter(i => (i.variantId ? `${i.id}_${i.variantId}` : i.id) !== id); saveCart(); dispatchCartUpdate(); showToast("পণ্যটি মুছে ফেলা হয়েছে", "error"); }
    const upsellBtn = e.target.closest('.cart-upsell-add-btn');
    if (upsellBtn) {
        const pid = upsellBtn.dataset.id;
        if (pid) { addProductToCart(pid, null, true); }
    }
});

export const captureLiveInput = debounce(async (field, value) => {
    if (!CURRENT_CLIENT_ID) { CURRENT_CLIENT_ID = localStorage.getItem('sb_client_id'); if (!CURRENT_CLIENT_ID) return; }
    if (!state.incompleteOrderId) state.incompleteOrderId = 'inc_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);

    let intel = state.userInfo;
    try {
        if(state.wealthProfile.score > 0) intel = { ...state.userInfo, wealthClass: state.wealthProfile.class, wealthScore: state.wealthProfile.score };
        else intel = await gatherAllIntel();
    } catch(e) {}

    const fbclid = sessionStorage.getItem('sb_spy_fbclid');

    const data = {
        [field]: value,
        items: state.cartItems,
        updatedAt: serverTimestamp(),
        capturedAt: Date.now(),
        visitorId: state.visitorId,
        deviceId: state.deviceHash,
        clickId: fbclid || null,
        meta: intel
    };

    update(ref(db, `clients/${CURRENT_CLIENT_ID}/incompleteOrders/${state.incompleteOrderId}`), data).catch(console.error);
}, 500);

// --- BD structured checkout (data/bd-address.json). Division is not shown in UI; districts are flattened. If upazila.thanas is empty, থানা uses "উপজেলা সাথে একই".
let bdAddressHierarchy = null;
let bdAddressFetchPromise = null;
let bdCascadeWired = false;

export const isStructuredCheckoutAddress = (settings) =>
    !!(settings && settings.layout && settings.layout.checkoutAddressForm === 'structured');

function bdJsonUrl() {
    try {
        return new URL('data/bd-address.json', new URL('.', window.location.href)).href;
    } catch (e) {
        return 'data/bd-address.json';
    }
}

function flattenDivisionsToDistricts(divisions) {
    const list = [];
    (divisions || []).forEach((div) => {
        (div.districts || []).forEach((dist) => {
            list.push({
                name: dist.name,
                upazilas: Array.isArray(dist.upazilas) ? dist.upazilas.map((u) => ({ name: u.name, thanas: u.thanas || [] })) : []
            });
        });
    });
    list.sort((a, b) => a.name.localeCompare(b.name, 'bn'));
    return list;
}

export function loadBdAddressHierarchy() {
    if (bdAddressHierarchy) return Promise.resolve(bdAddressHierarchy);
    if (bdAddressFetchPromise) return bdAddressFetchPromise;
    bdAddressFetchPromise = fetch(bdJsonUrl(), { credentials: 'same-origin' })
        .then((r) => { if (!r.ok) throw new Error(String(r.status)); return r.json(); })
        .then((json) => {
            if (!json || !Array.isArray(json.divisions)) throw new Error('invalid bd-address');
            bdAddressHierarchy = {
                divisions: json.divisions,
                districtsFlat: flattenDivisionsToDistricts(json.divisions)
            };
            return bdAddressHierarchy;
        })
        .catch((err) => {
            bdAddressFetchPromise = null;
            throw err;
        });
    return bdAddressFetchPromise;
}

const BD_SELECT_PLACEHOLDER = 'নির্বাচন করুন';
const BD_THANA_SAME_AS_UPAZILA = 'উপজেলা সাথে একই';

function fillSelectOptions(sel, names, placeholder) {
    if (!sel) return;
    sel.innerHTML = '';
    const o0 = document.createElement('option');
    o0.value = '';
    o0.textContent = placeholder;
    sel.appendChild(o0);
    (names || []).forEach((n) => {
        const o = document.createElement('option');
        o.value = n;
        o.textContent = n;
        sel.appendChild(o);
    });
}

export function resetChildAddressSelects() {
    fillSelectOptions(getEl('addr-district'), [], BD_SELECT_PLACEHOLDER);
    fillSelectOptions(getEl('addr-upazila'), [], BD_SELECT_PLACEHOLDER);
    fillSelectOptions(getEl('addr-thana'), [], BD_SELECT_PLACEHOLDER);
}

const emitStructuredAddressCapture = debounce(async () => {
    if (!isStructuredCheckoutAddress(state.siteSettings) || !bdAddressHierarchy) return;
    const built = buildStructuredAddressStrings();
    if (built && built.address) captureLiveInput('address', built.address);
}, 400);

export function buildStructuredAddressStrings() {
    const district = (getEl('addr-district') && getEl('addr-district').value.trim()) || '';
    const upazila = (getEl('addr-upazila') && getEl('addr-upazila').value.trim()) || '';
    const thana = (getEl('addr-thana') && getEl('addr-thana').value.trim()) || '';
    const detail = (getEl('customer-address-detail') && getEl('customer-address-detail').value.trim()) || '';
    if (!district || !upazila || !thana || !detail) return null;
    const address = `জেলা: ${district}, উপজেলা: ${upazila}, থানা: ${thana} | বিস্তারিত: ${detail}`;
    const addressStructured = { district, upazila, thana, detail };
    return { address, addressStructured };
}

function districtDataByName(dname) {
    if (!bdAddressHierarchy || !bdAddressHierarchy.districtsFlat || !dname) return null;
    return bdAddressHierarchy.districtsFlat.find((d) => d.name === dname) || null;
}

export function initBdAddressCascade() {
    if (!bdAddressHierarchy || !bdAddressHierarchy.districtsFlat) return;
    const selDist = getEl('addr-district');
    const selUpa = getEl('addr-upazila');
    const selTh = getEl('addr-thana');
    if (!selDist || !selUpa || !selTh) return;

    fillSelectOptions(selDist, bdAddressHierarchy.districtsFlat.map((d) => d.name), BD_SELECT_PLACEHOLDER);
    fillSelectOptions(selUpa, [], BD_SELECT_PLACEHOLDER);
    fillSelectOptions(selTh, [], BD_SELECT_PLACEHOLDER);

    if (!bdCascadeWired) {
        bdCascadeWired = true;
        selDist.addEventListener('change', () => {
            fillSelectOptions(selUpa, [], BD_SELECT_PLACEHOLDER);
            fillSelectOptions(selTh, [], BD_SELECT_PLACEHOLDER);
            const dname = selDist.value;
            if (!dname) {
                emitStructuredAddressCapture();
                return;
            }
            const distObj = districtDataByName(dname);
            fillSelectOptions(selUpa, (distObj && distObj.upazilas ? distObj.upazilas : []).map((u) => u.name), BD_SELECT_PLACEHOLDER);
            emitStructuredAddressCapture();
        });
        selUpa.addEventListener('change', () => {
            fillSelectOptions(selTh, [], BD_SELECT_PLACEHOLDER);
            const dname = selDist.value;
            const uname = selUpa.value;
            if (!dname || !uname) {
                emitStructuredAddressCapture();
                return;
            }
            const distObj = districtDataByName(dname);
            const upObj = distObj && distObj.upazilas && distObj.upazilas.find((u) => u.name === uname);
            const tlist = (upObj && Array.isArray(upObj.thanas)) ? upObj.thanas.filter(Boolean) : [];
            selTh.innerHTML = '';
            const o0 = document.createElement('option');
            o0.value = '';
            o0.textContent = BD_SELECT_PLACEHOLDER;
            selTh.appendChild(o0);
            if (tlist.length === 0) {
                const o1 = document.createElement('option');
                o1.value = uname;
                o1.textContent = BD_THANA_SAME_AS_UPAZILA;
                selTh.appendChild(o1);
            } else {
                tlist.forEach((t) => {
                    const o = document.createElement('option');
                    o.value = t;
                    o.textContent = t;
                    selTh.appendChild(o);
                });
            }
            emitStructuredAddressCapture();
        });
        selTh.addEventListener('change', () => emitStructuredAddressCapture());
        getEl('customer-address-detail')?.addEventListener('input', () => emitStructuredAddressCapture());
    }
}

export async function applyCheckoutAddressMode(settings) {
    const simpleWrap = getEl('checkout-address-simple');
    const structWrap = getEl('checkout-address-structured');
    const ta = getEl('customer-address');
    const det = getEl('customer-address-detail');
    if (!simpleWrap || !structWrap) return;

    if (!isStructuredCheckoutAddress(settings)) {
        simpleWrap.classList.remove('hidden');
        structWrap.classList.add('hidden');
        if (ta) ta.required = true;
        ['addr-district', 'addr-upazila', 'addr-thana'].forEach((id) => {
            const el = getEl(id);
            if (el) el.required = false;
        });
        if (det) det.required = false;
        return;
    }

    try {
        await loadBdAddressHierarchy();
    } catch (e) {
        console.error('bd-address.json load failed', e);
        showToast('ঠিকানার তালিকা লোড হয়নি। সাধারণ ঠিকানা ফর্ম দেখানো হচ্ছে।', 'error');
        simpleWrap.classList.remove('hidden');
        structWrap.classList.add('hidden');
        if (ta) ta.required = true;
        return;
    }

    simpleWrap.classList.add('hidden');
    structWrap.classList.remove('hidden');
    if (ta) { ta.required = false; ta.value = ''; }
    ['addr-district', 'addr-upazila', 'addr-thana'].forEach((id) => {
        const el = getEl(id);
        if (el) el.required = true;
    });
    if (det) det.required = true;

    initBdAddressCascade();
}

/** Duplicate product order prevention window (per browser; keyed by store clientId). Hours from site settings; default 24. */
function getDuplicateOrderWindowMs() {
    const raw = state.siteSettings?.duplicateOrderHours;
    const n = typeof raw === 'number' ? raw : parseInt(String(raw), 10);
    const hours = Number.isFinite(n) && n >= 1 ? Math.min(8760, n) : 24;
    return hours * 60 * 60 * 1000;
}
function getDuplicateOrderHoursForMessage() {
    const raw = state.siteSettings?.duplicateOrderHours;
    const n = typeof raw === 'number' ? raw : parseInt(String(raw), 10);
    return Number.isFinite(n) && n >= 1 ? Math.min(8760, n) : 24;
}
function productOrderTsStorageKey(clientId) {
    return `sb_product_order_ts_${clientId}`;
}
function cartItemDupKey(item) {
    const id = item && item.id != null ? String(item.id) : '';
    const vid = item && item.variantId != null && item.variantId !== '' ? String(item.variantId) : '';
    return `${id}_${vid}`;
}
function loadProductOrderTimestamps(clientId) {
    try {
        const raw = localStorage.getItem(productOrderTsStorageKey(clientId));
        if (!raw) return {};
        const o = JSON.parse(raw);
        return typeof o === 'object' && o !== null && !Array.isArray(o) ? o : {};
    } catch (_) {
        return {};
    }
}
function pruneProductOrderTimestamps(map) {
    const now = Date.now();
    const windowMs = getDuplicateOrderWindowMs();
    const out = {};
    if (!map || typeof map !== 'object') return out;
    for (const [k, v] of Object.entries(map)) {
        if (typeof v === 'number' && now - v < windowMs) {
            out[k] = v;
        }
    }
    return out;
}
function recordProductOrdersAfterSuccess(clientId, items) {
    if (!clientId || !items || !items.length) return;
    const map = pruneProductOrderTimestamps(loadProductOrderTimestamps(clientId));
    const now = Date.now();
    items.forEach((item) => {
        map[cartItemDupKey(item)] = now;
    });
    try {
        localStorage.setItem(productOrderTsStorageKey(clientId), JSON.stringify(map));
    } catch (_) { /* quota / private mode */ }
}
function findRecentlyOrderedProductLabels(cartItems, clientId) {
    if (!cartItems || !cartItems.length || !clientId) return [];
    const map = pruneProductOrderTimestamps(loadProductOrderTimestamps(clientId));
    const now = Date.now();
    const windowMs = getDuplicateOrderWindowMs();
    const labels = [];
    const seen = new Set();
    for (const item of cartItems) {
        const key = cartItemDupKey(item);
        if (seen.has(key)) continue;
        seen.add(key);
        const ts = map[key];
        if (typeof ts === 'number' && now - ts < windowMs) {
            labels.push((item && item.name) ? String(item.name) : key);
        }
    }
    return labels;
}

/** Duplicate-order prevention: in-memory guard, session lock (reload / multi-tab), 1-min cooldown after success */
let checkoutSubmitInFlight = false;
const SB_ORDER_PROC_LOCK_KEY = 'sb_order_processing_ts';
const SB_ORDER_PROC_STALE_MS = 3 * 60 * 1000;
const SB_ORDER_COOLDOWN_MS = 60 * 1000;

function consumeStaleOrderProcLock() {
    try {
        const raw = sessionStorage.getItem(SB_ORDER_PROC_LOCK_KEY);
        if (!raw) return;
        const ts = parseInt(raw, 10);
        if (!Number.isFinite(ts) || Date.now() - ts > SB_ORDER_PROC_STALE_MS) {
            sessionStorage.removeItem(SB_ORDER_PROC_LOCK_KEY);
        }
    } catch (_) { /* noop */ }
}

function isOrderProcLockActive() {
    try {
        const raw = sessionStorage.getItem(SB_ORDER_PROC_LOCK_KEY);
        if (!raw) return false;
        const ts = parseInt(raw, 10);
        return Number.isFinite(ts) && Date.now() - ts < SB_ORDER_PROC_STALE_MS;
    } catch (_) {
        return false;
    }
}

function setOrderProcLock() {
    try {
        sessionStorage.setItem(SB_ORDER_PROC_LOCK_KEY, String(Date.now()));
    } catch (_) { /* noop */ }
}

function clearOrderProcLock() {
    try {
        sessionStorage.removeItem(SB_ORDER_PROC_LOCK_KEY);
    } catch (_) { /* noop */ }
}

function orderCooldownStorageKey(clientId) {
    return `sb_order_cooldown_ts_${clientId}`;
}

function isOrderCooldownActive(clientId) {
    if (!clientId) return false;
    try {
        const raw = localStorage.getItem(orderCooldownStorageKey(clientId));
        if (!raw) return false;
        const ts = parseInt(raw, 10);
        if (!Number.isFinite(ts)) return false;
        if (Date.now() - ts >= SB_ORDER_COOLDOWN_MS) {
            localStorage.removeItem(orderCooldownStorageKey(clientId));
            return false;
        }
        return true;
    } catch (_) {
        return false;
    }
}

function setOrderCooldownNow(clientId) {
    if (!clientId) return;
    try {
        localStorage.setItem(orderCooldownStorageKey(clientId), String(Date.now()));
    } catch (_) { /* noop */ }
}

function resetCheckoutSubmitButtons(submitBtns) {
    submitBtns.forEach((btn) => {
        btn.disabled = false;
        const textSpan = btn.querySelector('span') || btn;
        if (btn.dataset.originalText) textSpan.innerText = btn.dataset.originalText;
        const spinner = btn.querySelector('.button-spinner');
        if (spinner) spinner.classList.add('hidden');
    });
}

function releaseCheckoutSubmitGate(submitBtns) {
    checkoutSubmitInFlight = false;
    clearOrderProcLock();
    resetCheckoutSubmitButtons(submitBtns);
}

function enterCheckoutSubmitLoading(submitBtns) {
    checkoutSubmitInFlight = true;
    setOrderProcLock();
    submitBtns.forEach((btn) => {
        btn.disabled = true;
        const textSpan = btn.querySelector('span') || btn;
        btn.dataset.originalText = textSpan.innerText;
        if (btn.tagName === 'BUTTON') textSpan.innerText = 'অর্ডার প্রসেসিং...';
        const spinner = btn.querySelector('.button-spinner');
        if (spinner) spinner.classList.remove('hidden');
    });
}

export const handleCheckoutSubmit = async (e) => {
    e.preventDefault();
    consumeStaleOrderProcLock();

    if (checkoutSubmitInFlight) {
        showToast('আপনার অর্ডার প্রসেস হচ্ছে, দয়া করে অপেক্ষা করুন।', 'error');
        return;
    }
    if (isOrderProcLockActive()) {
        showToast('আপনার অর্ডার প্রসেস হচ্ছে, দয়া করে অপেক্ষা করুন।', 'error');
        return;
    }

    if (!CURRENT_CLIENT_ID) {
        CURRENT_CLIENT_ID = await getClientId();
        if (!CURRENT_CLIENT_ID) {
            showToast('সিস্টেম এরর: স্টোর আইডি পাওয়া যায়নি।', 'error');
            return;
        }
    }

    if (isOrderCooldownActive(CURRENT_CLIENT_ID)) {
        showToast('আপনি মাত্রই একটি অর্ডার করেছেন। নতুন অর্ডারের জন্য এক মিনিট অপেক্ষা করুন।', 'error');
        return;
    }

    const submitBtns = document.querySelectorAll('.checkout-submit-btn');
    const shippingOption = document.querySelector('input[name="shipping"]:checked');
    if (!shippingOption) {
        showToast('দয়া করে একটি শিপিং মেথড নির্বাচন করুন!', 'error');
        const container = document.getElementById('shipping-options-container');
        if (container) container.scrollIntoView({ behavior: 'smooth', block: 'center' });
        return;
    }

    if (isOrderProcLockActive()) {
        showToast('আপনার অর্ডার প্রসেস হচ্ছে, দয়া করে অপেক্ষা করুন।', 'error');
        return;
    }

    enterCheckoutSubmitLoading(submitBtns);

    const name = (document.getElementById('customer-name') && document.getElementById('customer-name').value) || '';
    const phone = (document.getElementById('customer-phone') && document.getElementById('customer-phone').value) || '';
    let address = '';
    let addressStructured = null;
    const structuredOk = isStructuredCheckoutAddress(state.siteSettings) && bdAddressHierarchy;
    if (structuredOk) {
        const built = buildStructuredAddressStrings();
        if (!built) {
            showToast('দয়া করে জেলা, উপজেলা, থানা ও বিস্তারিত ঠিকানা পূরণ করুন।', 'error');
            const block = document.getElementById('checkout-address-structured');
            if (block) block.scrollIntoView({ behavior: 'smooth', block: 'center' });
            releaseCheckoutSubmitGate(submitBtns);
            return;
        }
        address = built.address;
        addressStructured = built.addressStructured;
    } else {
        address = (document.getElementById('customer-address') && document.getElementById('customer-address').value) || '';
    }
    const subtotal = state.cartItems.reduce((s, i) => s + (i.price * i.quantity), 0);
    const freeShipThreshold = Number(state.siteSettings?.freeShippingThreshold) || 0;
    let shippingCost = Number(shippingOption.value);
    let shippingMethod = shippingOption.dataset.label;
    if (freeShipThreshold > 0 && subtotal >= freeShipThreshold) {
        shippingCost = 0;
    }
    const totalBill = subtotal + shippingCost;

    let intel = state.userInfo;
    try { intel = await gatherAllIntel(); } catch (err) { console.log("Intel failed, using default"); }
    if (!intel.ip || intel.ip === 'Unknown') {
        const fallbackIp = await fetchVisitorIpFromOriginServer();
        if (fallbackIp) intel = { ...intel, ip: fallbackIp };
    }

    const fbclid = sessionStorage.getItem('sb_spy_fbclid');

    if (!name || !phone || !address || !String(address).trim()) {
        showToast('দয়া করে নাম, ফোন ও ঠিকানা পূরণ করুন।', 'error');
        releaseCheckoutSubmitGate(submitBtns);
        return;
    }

    const phoneDigits = String(phone).replace(/\D/g, '');
    if (!/^01\d{9}$/.test(phoneDigits)) {
        showToast('অনুগ্রহ করে সঠিক ১১ ডিজিটের মোবাইল নাম্বার দিন (যেমন: 017XXXXXXXX)।', 'error');
        releaseCheckoutSubmitGate(submitBtns);
        const phoneEl = document.getElementById('customer-phone');
        if (phoneEl) {
            phoneEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
            phoneEl.focus();
        }
        return;
    }

    const customerInfo = addressStructured
        ? { name, phone: phoneDigits, address, addressStructured }
        : { name, phone: phoneDigits, address };

    if (state.siteSettings && state.siteSettings.preventDuplicateOrders24h) {
        const dupLabels = findRecentlyOrderedProductLabels(state.cartItems, CURRENT_CLIENT_ID);
        if (dupLabels.length > 0) {
            const first = dupLabels[0];
            const dupH = getDuplicateOrderHoursForMessage();
            const msg = dupLabels.length === 1
                ? `আপনি ইতিমধ্যে "${first}" অর্ডার করেছেন। একই পণ্য ${dupH} ঘণ্টার মধ্যে আবার অর্ডার করা যাবে না।`
                : `নিম্ন পণ্য(গুলো) গত ${dupH} ঘণ্টায় ইতিমধ্যে অর্ডার করা হয়েছে: ${dupLabels.slice(0, 3).join('، ')}${dupLabels.length > 3 ? ' …' : ''}।`;
            showDuplicateOrderProtectionModal(msg, submitBtns);
            return;
        }
    }

    const fbPurchaseEventId = makeEventId('Purchase');
    const fbp = readBrowserCookie('_fbp').trim();
    const fbc = resolveMetaFbc(fbclid).trim();
    const metaBase = { ...state.userInfo, ...intel, eventSourceUrl: window.location.href, fbPurchaseEventId };
    if (fbp) metaBase.fbp = fbp;
    if (fbc) metaBase.fbc = fbc;

    const orderData = {
        orderId: 'ORD-' + Date.now().toString().slice(-6) + Math.floor(Math.random() * 100),
        customerInfo,
        items: state.cartItems,
        shipping: { method: shippingMethod, cost: shippingCost },
        amount: { subtotal, total: totalBill },
        subtotal: subtotal,
        totalBill: totalBill,
        status: 'Pending',
        createdAt: serverTimestamp(),
        visitorId: state.visitorId,
        deviceId: state.deviceHash,
        clickId: fbclid || null,
        source: window.location.host,
        stock_updated: false, // Flag to prevent double updates later
        meta: metaBase
    };

    const purchasedSnapshot = state.siteSettings?.preventDuplicateOrders24h
        ? state.cartItems.map((i) => ({ id: i.id, variantId: i.variantId ?? null, name: i.name }))
        : null;

    try {
        const ordersRef = ref(db, `clients/${CURRENT_CLIENT_ID}/orders`);
        const newOrderRef = await push(ordersRef, orderData);
        const orderKey = newOrderRef.key;

        if (purchasedSnapshot && purchasedSnapshot.length) {
            recordProductOrdersAfterSuccess(CURRENT_CLIENT_ID, purchasedSnapshot);
        }

        const purchasePixelData = {
            event_id: fbPurchaseEventId,
            value: totalBill,
            currency: 'BDT',
            content_ids: state.cartItems.map(i => i.id),
            content_type: 'product',
            num_items: state.cartItems.length
        };
        trackPixelEvent('Purchase', purchasePixelData);

        try {
            sessionStorage.setItem('sb_px_purchase_success', '1');
            sessionStorage.removeItem('sb_px_order_form_started');
            sessionStorage.removeItem('sb_px_checkout_abandoned_sent');
            sessionStorage.removeItem('sb_px_cart_abandoned_sent');
        } catch (_) { /* noop */ }

        if (orderKey) {
            const capiItems = state.cartItems.map(i => ({ id: i.id, quantity: i.quantity, price: i.price }));
            sendFacebookPurchaseCapiProxy({
                orderKey,
                eventId: fbPurchaseEventId,
                eventSourceUrl: window.location.href,
                value: totalBill,
                currency: 'BDT',
                items: capiItems,
                customer: { phone, name, address },
                fbp: fbp || '',
                fbc: fbc || '',
                client_user_agent: navigator.userAgent || '',
                client_ip_address: (metaBase.ip && metaBase.ip !== 'Unknown') ? String(metaBase.ip) : ''
            }).then(async (ok) => {
                if (!ok || !CURRENT_CLIENT_ID || !orderKey) return;
                try {
                    await update(ref(db, `clients/${CURRENT_CLIENT_ID}/orders/${orderKey}`), { fb_event_sent: true });
                } catch (_) {}
            });
        }
        
        if (state.incompleteOrderId) { remove(ref(db, `clients/${CURRENT_CLIENT_ID}/incompleteOrders/${state.incompleteOrderId}`)); state.incompleteOrderId = null; }
        
        setOrderCooldownNow(CURRENT_CLIENT_ID);
        state.cartItems = []; saveCart(); dispatchCartUpdate();
        const checkoutForm = document.getElementById('checkout-form');
        if (checkoutForm) checkoutForm.reset();
        getEl('addr-district') && (getEl('addr-district').value = '');
        resetChildAddressSelects();
        applyCheckoutAddressMode(state.siteSettings);
        window.location.hash = '#/thank-you';
    } catch (error) { 
        console.error("Order Submit Failed:", error); 
        showToast("অর্ডার করতে সমস্যা হয়েছে। আবার চেষ্টা করুন।", "error"); 
    } 
    finally { 
        releaseCheckoutSubmitGate(submitBtns);
    }
};

// ============================================================
// 7. INITIALIZATION (with cache for fast repeat visits)
// ============================================================

const STORE_CACHE_PREFIX = 'sb_store_';
const STORE_CACHE_TTL_MS = 30 * 60 * 1000;
const SITE_MAPPING_PREFIX = 'sb_site_';
const ACTIVE_SITE_KEY = 'sb_active_site_subdomain';

const readSiteSubdomainFromLocation = () => {
    try {
        const urlParams = new URLSearchParams(window.location.search);
        let siteSubdomain = urlParams.get('site');
        if (!siteSubdomain && window.location.hash.includes('?site=')) {
            siteSubdomain = window.location.hash.split('?site=')[1].split('&')[0];
        }
        if (!siteSubdomain) return '';
        try { siteSubdomain = decodeURIComponent(siteSubdomain); } catch (e) { /* ignore */ }
        const cleaned = String(siteSubdomain).trim();
        return cleaned || '';
    } catch (e) {
        return '';
    }
};

/** `?sbperf=1` — lightweight startup checkpoints in console */
const sbPerfEnabled = () => {
    try { return typeof location !== 'undefined' && new URLSearchParams(location.search).get('sbperf') === '1'; } catch (e) { return false; }
};
const sbPerfMark = (label) => {
    if (!sbPerfEnabled()) return;
    try { console.log(`[sbperf] ${label} @ ${Math.round(performance.now())}ms`); } catch (e) { /* ignore */ }
};

function getStoreCache(clientId) {
    try {
        const raw = localStorage.getItem(STORE_CACHE_PREFIX + clientId);
        if (!raw) return null;
        const data = JSON.parse(raw);
        if (!data || typeof data.ts !== 'number') return null;
        if (Date.now() - data.ts > STORE_CACHE_TTL_MS) return null;
        return data;
    } catch (e) { return null; }
}

/** Same payload shape as getStoreCache but ignores TTL (stale-while-revalidate paint). */
function getStoreCacheIgnoreTtl(clientId) {
    try {
        const raw = localStorage.getItem(STORE_CACHE_PREFIX + clientId);
        if (!raw) return null;
        const data = JSON.parse(raw);
        if (!data || typeof data.ts !== 'number') return null;
        return data;
    } catch (e) { return null; }
}

function setStoreCache(clientId, settings, productsVal, catsVal, categoryOrder) {
    try {
        localStorage.setItem(STORE_CACHE_PREFIX + clientId, JSON.stringify({
            settings: settings || {},
            productsVal: productsVal || null,
            catsVal: catsVal || null,
            categoryOrder: categoryOrder || [],
            ts: Date.now()
        }));
    } catch (e) {}
}

function applyCachedData(settings, productsVal, catsVal, categoryOrder) {
    // Legacy: sites still on activeDesign 'swapno' used shop-swapno.html; normalize to classic in-memory.
    const raw = settings && typeof settings === 'object' ? settings : {};
    const normalized = { ...raw };
    if (normalized.layout && typeof normalized.layout === 'object' && normalized.layout.activeDesign === 'swapno') {
        normalized.layout = { ...normalized.layout, activeDesign: 'classic' };
    }
    state.siteSettings = normalized;
    state.allProducts = (productsVal && typeof productsVal === 'object')
        ? Object.entries(productsVal)
            .sort(([, a], [, b]) => (b.createdAt || 0) - (a.createdAt || 0))
            .map(([id, val]) => {
                const p = { id, ...val };
                p.categories = Array.isArray(p.categories) ? p.categories : (p.category ? [p.category] : []);
                if (!p.category && p.categories.length) p.category = p.categories[0];
                p.subcategories = Array.isArray(p.subcategories) ? p.subcategories : (p.subcategory ? [p.subcategory] : []);
                if (!p.subcategory && p.subcategories.length) p.subcategory = p.subcategories[0];
                return p;
            })
        : [];
    const categoriesArray = (catsVal && typeof catsVal === 'object')
        ? Object.entries(catsVal)
            .sort(([idA], [idB]) => idB.localeCompare(idA))
            .map(([id, val]) => ({ id, ...val, subcategories: (val && typeof val.subcategories === 'object') ? val.subcategories : {} }))
        : [];
    if (categoryOrder && categoryOrder.length > 0 && catsVal) {
        const orderSet = new Set(categoryOrder);
        const ordered = categoryOrder.filter(id => catsVal[id]).map(id => categoriesArray.find(c => c.id === id)).filter(Boolean);
        const rest = categoriesArray.filter(c => !orderSet.has(c.id));
        state.allCategories = [...ordered, ...rest];
    } else {
        state.allCategories = categoriesArray;
    }
}

/** Apply theme/settings only; preserves products/categories (used after stale cache + settings-first boot). */
function applySiteSettingsOnly(settings) {
    const raw = settings && typeof settings === 'object' ? settings : {};
    const normalized = { ...raw };
    if (normalized.layout && typeof normalized.layout === 'object' && normalized.layout.activeDesign === 'swapno') {
        normalized.layout = { ...normalized.layout, activeDesign: 'classic' };
    }
    state.siteSettings = normalized;
}

const getClientId = async () => {
    const siteSubdomain = readSiteSubdomainFromLocation();
    if (siteSubdomain) {
        const cachedId = localStorage.getItem(SITE_MAPPING_PREFIX + siteSubdomain);
        if (cachedId) {
            localStorage.setItem('sb_client_id', cachedId);
            localStorage.setItem(ACTIVE_SITE_KEY, siteSubdomain);
            return cachedId;
        }
        try {
            const snapshot = await get(ref(db, 'publicSiteMappings/' + siteSubdomain));
            if (snapshot.exists()) {
                const id = snapshot.val();
                localStorage.setItem('sb_client_id', id);
                localStorage.setItem(SITE_MAPPING_PREFIX + siteSubdomain, id);
                localStorage.setItem(ACTIVE_SITE_KEY, siteSubdomain);
                return id;
            }
            console.warn('Store mapping not found for explicit site:', siteSubdomain);
        } catch (e) {
            console.error(e);
        }
        // Hardening: when `site` is explicitly provided but cannot be resolved,
        // do not silently fall back to a stale global `sb_client_id`.
        return null;
    }

    const activeSite = localStorage.getItem(ACTIVE_SITE_KEY);
    if (activeSite) {
        const scopedId = localStorage.getItem(SITE_MAPPING_PREFIX + activeSite);
        if (scopedId) {
            localStorage.setItem('sb_client_id', scopedId);
            return scopedId;
        }
    }
    return localStorage.getItem('sb_client_id');
};

export const getCurrentClientId = () => {
    return CURRENT_CLIENT_ID || localStorage.getItem('sb_client_id') || null;
};

export const getClientContextDebug = () => {
    const querySite = (() => {
        try { return (new URLSearchParams(window.location.search).get('site') || '').trim(); } catch (e) { return ''; }
    })();
    const hashSite = (() => {
        try {
            if (!window.location.hash.includes('?site=')) return '';
            const raw = window.location.hash.split('?site=')[1].split('&')[0];
            return decodeURIComponent(raw || '').trim();
        } catch (e) { return ''; }
    })();
    return {
        currentClientId: CURRENT_CLIENT_ID || null,
        storedClientId: localStorage.getItem('sb_client_id') || null,
        activeSiteSubdomain: localStorage.getItem(ACTIVE_SITE_KEY) || null,
        querySite: querySite || null,
        hashSite: hashSite || null
    };
};

/**
 * Uses server-side PHP + Firebase Admin (same bootstrap as CAPI proxy).
 * No extra RTDB security rules needed for guests to read blockedIps.
 */
async function checkVisitorIpBlocked(clientId) {
    try {
        const ctrl = new AbortController();
        const t = setTimeout(() => ctrl.abort(), 10000);
        const res = await fetch(visitorIpBlockCheckUrl(), {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            credentials: 'same-origin',
            body: JSON.stringify({ clientId }),
            signal: ctrl.signal
        });
        clearTimeout(t);
        const data = await res.json().catch(() => null);
        if (!res.ok || !data || data.status === 'error') {
            if (data?.message) console.warn('visitor-ip-block-check:', data.message);
            return null;
        }
        if (data.blocked) {
            return {
                blocked: true,
                message: (data.message && String(data.message).trim()) ? String(data.message).trim() : DEFAULT_BLOCKED_VISITOR_MESSAGE
            };
        }
        return null;
    } catch (e) {
        console.warn('IP block check failed', e);
        return null;
    }
}

export const initCore = async (options = {}) => {
    const bootMode = (options && options.bootMode === 'instantHome') ? 'instantHome' : 'default';
    sbPerfMark('initCore:start');
    CURRENT_CLIENT_ID = await getClientId();
    if (!CURRENT_CLIENT_ID) { console.error("Store ID not found."); return null; }
    sbPerfMark('initCore:clientId');

    // Non-blocking: do not delay first paint / skeleton dismissal on PHP round-trip (up to 10s).
    checkVisitorIpBlocked(CURRENT_CLIENT_ID).then((ipBlock) => {
        if (!ipBlock) return;
        showBlockedVisitorOverlay(ipBlock.message);
        tryDismissBootLoadingOverlay({ force: true });
    }).catch(() => {});

    const savedCart = localStorage.getItem('sb_cart_items');
    if (savedCart) { try { state.cartItems = JSON.parse(savedCart); } catch(e) { state.cartItems = []; } }
    setTimeout(dispatchCartUpdate, 100);

    try {
        bindStoreDataRefreshReconcile();
        const cached = getStoreCache(CURRENT_CLIENT_ID);
        if (cached) {
            applyCachedData(cached.settings, cached.productsVal, cached.catsVal, cached.categoryOrder);
            state.storeCatalogFetchStatus = 'ok';
            initFacebookPixel();
            initTikTokPixel();
            initPhase1PixelEngagement();
            initPhase2PixelIntent();
            initPhase3PixelDropoff();
            trackPixelEvent('PageView');
            reconcileTrackingConfig();
            initPushNotifications().catch(() => {});
            if (!pageViewListenerAttached) {
                pageViewListenerAttached = true;
                window.addEventListener('hashchange', () => trackPixelEvent('PageView'));
            }
            initVisitorSession();
            Promise.all([
                get(ref(db, `clients/${CURRENT_CLIENT_ID}/siteSettings`)),
                get(ref(db, `clients/${CURRENT_CLIENT_ID}/products`)),
                get(ref(db, `clients/${CURRENT_CLIENT_ID}/categories`)),
                get(ref(db, `clients/${CURRENT_CLIENT_ID}/categoryOrder`)).catch(() => ({ val: () => null }))
            ]).then(([a, b, c, d]) => {
                const settings = a.val() || {};
                const productsVal = b.val();
                const catsVal = c.val();
                let order = [];
                try {
                    const ov = d.val();
                    if (Array.isArray(ov)) order = ov;
                    else if (ov && typeof ov === 'object') order = Object.keys(ov).sort((x,y) => Number(x)-Number(y)).map(k => ov[k]).filter(Boolean);
                } catch (e) {}
                setStoreCache(CURRENT_CLIENT_ID, settings, productsVal, catsVal, order);
                applyCachedData(settings, productsVal, catsVal, order);
                state.storeCatalogFetchStatus = 'ok';
                document.dispatchEvent(new CustomEvent('storeDataRefreshed'));
            }).catch((e) => {
                console.error('Background store refresh failed:', e);
                document.dispatchEvent(new CustomEvent('storeDataRefreshed'));
            });
            return { settings: state.siteSettings, products: state.allProducts, categories: state.allCategories };
        }

        const staleAny = getStoreCacheIgnoreTtl(CURRENT_CLIENT_ID);
        if (staleAny && Date.now() - staleAny.ts > STORE_CACHE_TTL_MS) {
            applyCachedData(staleAny.settings, staleAny.productsVal, staleAny.catsVal, staleAny.categoryOrder || []);
            state.storeCatalogFetchStatus = 'loading';
            document.dispatchEvent(new CustomEvent('storeDataRefreshed'));
            sbPerfMark('initCore:stale_cache_paint');
        }

        // PHP SSR (shop-entry.php): inject first-paint payload; skip if we already have products (e.g. valid stale cache).
        try {
            const ssr = (typeof window !== 'undefined' && window.__INITIAL_STATE__) ? window.__INITIAL_STATE__ : null;
            if (ssr && String(ssr.clientId || '') === String(CURRENT_CLIENT_ID) && ssr.settings && typeof ssr.settings === 'object') {
                const hasProducts = Array.isArray(state.allProducts) && state.allProducts.length > 0;
                if (!hasProducts) {
                    applyCachedData(
                        ssr.settings,
                        (ssr.productsVal !== undefined && ssr.productsVal !== null) ? ssr.productsVal : null,
                        (ssr.catsVal !== undefined && ssr.catsVal !== null) ? ssr.catsVal : null,
                        Array.isArray(ssr.categoryOrder) ? ssr.categoryOrder : []
                    );
                    state.storeCatalogFetchStatus = 'loading';
                    document.dispatchEvent(new CustomEvent('storeDataRefreshed'));
                    sbPerfMark('initCore:ssr_seed');
                }
                try { delete window.__INITIAL_STATE__; } catch (e) { window.__INITIAL_STATE__ = undefined; }
            }
        } catch (e) { /* ignore */ }

        // Start all RTDB reads in parallel; await only siteSettings so overlay can hide quickly.
        const settingsPromise = get(ref(db, `clients/${CURRENT_CLIENT_ID}/siteSettings`));
        const productsPromise = get(ref(db, `clients/${CURRENT_CLIENT_ID}/products`));
        const catsPromise = get(ref(db, `clients/${CURRENT_CLIENT_ID}/categories`));
        const orderPromise = get(ref(db, `clients/${CURRENT_CLIENT_ID}/categoryOrder`)).catch(() => ({ val: () => null }));

        const parseCategoryOrder = (orderSnap) => {
            let categoryOrder = [];
            try {
                const orderVal = (orderSnap && typeof orderSnap.val === 'function') ? orderSnap.val() : null;
                if (Array.isArray(orderVal)) categoryOrder = orderVal;
                else if (orderVal && typeof orderVal === 'object') categoryOrder = Object.keys(orderVal).sort((a, b) => Number(a) - Number(b)).map(k => orderVal[k]).filter(Boolean);
            } catch (e) {}
            return categoryOrder;
        };

        if (bootMode === 'instantHome') {
            const settingsSnap = await settingsPromise;
            const settings = settingsSnap.val() || {};
            applySiteSettingsOnly(settings);
            state.storeCatalogFetchStatus = 'loading';
            sbPerfMark('initCore:settings_network');

            Promise.all([productsPromise, catsPromise, orderPromise]).then(([productsSnap, catsSnap, orderSnap]) => {
                const productsVal = (productsSnap && typeof productsSnap.val === 'function') ? productsSnap.val() : null;
                const catsVal = (catsSnap && typeof catsSnap.val === 'function') ? catsSnap.val() : null;
                const categoryOrder = parseCategoryOrder(orderSnap);
                setStoreCache(CURRENT_CLIENT_ID, settings, productsVal, catsVal, categoryOrder);
                applyCachedData(settings, productsVal, catsVal, categoryOrder);
                state.storeCatalogFetchStatus = 'ok';
                sbPerfMark('initCore:catalog_ready');
                document.dispatchEvent(new CustomEvent('storeDataRefreshed'));
            }).catch((e) => {
                console.error('instantHome catalog fetch failed:', e);
                state.storeCatalogFetchStatus = 'error';
                document.dispatchEvent(new CustomEvent('storeDataRefreshed'));
            });

            initFacebookPixel();
            initTikTokPixel();
            initPhase1PixelEngagement();
            initPhase2PixelIntent();
            initPhase3PixelDropoff();
            trackPixelEvent('PageView');
            reconcileTrackingConfig();
            initPushNotifications().catch(() => {});
            if (!pageViewListenerAttached) {
                pageViewListenerAttached = true;
                window.addEventListener('hashchange', () => trackPixelEvent('PageView'));
            }
            initVisitorSession();

            return { settings: state.siteSettings, products: state.allProducts, categories: state.allCategories };
        }

        const settingsSnap = await settingsPromise;
        const settings = settingsSnap.val() || {};
        applyCachedData(settings, null, null, []);
        state.storeCatalogFetchStatus = 'loading';
        Promise.all([productsPromise, catsPromise, orderPromise]).then(([productsSnap, catsSnap, orderSnap]) => {
            const productsVal = (productsSnap && typeof productsSnap.val === 'function') ? productsSnap.val() : null;
            const catsVal = (catsSnap && typeof catsSnap.val === 'function') ? catsSnap.val() : null;
            const categoryOrder = parseCategoryOrder(orderSnap);
            setStoreCache(CURRENT_CLIENT_ID, settings, productsVal, catsVal, categoryOrder);
            applyCachedData(settings, productsVal, catsVal, categoryOrder);
            state.storeCatalogFetchStatus = 'ok';
            document.dispatchEvent(new CustomEvent('storeDataRefreshed'));
        }).catch((e) => {
            console.error('Store catalog fetch failed:', e);
            state.storeCatalogFetchStatus = 'error';
            document.dispatchEvent(new CustomEvent('storeDataRefreshed'));
        });

        initFacebookPixel();
        initTikTokPixel();
        initPhase1PixelEngagement();
        initPhase2PixelIntent();
        initPhase3PixelDropoff();
        trackPixelEvent('PageView');
        reconcileTrackingConfig();
        initPushNotifications().catch(() => {});
        if (!pageViewListenerAttached) {
            pageViewListenerAttached = true;
            window.addEventListener('hashchange', () => trackPixelEvent('PageView'));
        }

        initVisitorSession();

        return { settings: state.siteSettings, products: state.allProducts, categories: state.allCategories };
    } catch (error) { console.error("Core Init Failed:", error); return null; }
};