/**
 * AI eShop Chat - Session Tracker
 *
 * Tracks user behavior including:
 * - Page/Product/Category visits with timing
 * - Cart interactions (add/remove)
 * - Wishlist interactions
 * - Engagement metrics (scroll depth, clicks, mouse movement)
 * - Device/browser information
 *
 * LAZY INITIALIZATION: This tracker does NOT run on page load.
 * It only initializes when there is actual user interaction (click, scroll, mousemove, touch).
 * This approach:
 * - Eliminates impact on page load metrics (LCP, FID, etc.)
 * - Filters out bots (they don't interact)
 * - Only tracks engaged real users
 *
 * Performance optimizations:
 * - Lazy initialization on first user interaction
 * - Uses requestIdleCallback for non-critical operations
 * - sendBeacon for fire-and-forget tracking (doesn't block)
 * - No synchronous XHR calls
 */

(function() {
    'use strict';

    // Helper: Check if jQuery is available
    var $ = window.jQuery || null;

    // Track if already initialized
    var isInitialized = false;
    var interactionListenersAttached = false;

    // Store page load time early (before lazy init)
    var pageLoadTime = Date.now();

    // Page-views-in-this-session counter. Incremented eagerly (BEFORE the
    // lazy interaction-gate fires) because bounce visits still count as a
    // page-view in the session. Backed by sessionStorage so it survives
    // navigations within the same tab session but resets when the user
    // closes the browser — matching the natural definition of a "session".
    // Exposed on window.AIEO_SESSION_TRACKER.pageViewsInSession (set when
    // the tracker object is built later) so consumers like
    // AIEO Web Push opt-in can read it without depending on the lazy init.
    var pageViewsInSession = 1;
    try {
        var pv = parseInt(sessionStorage.getItem('aieo_pv_in_session') || '0', 10);
        pageViewsInSession = (pv > 0 ? pv : 0) + 1;
        sessionStorage.setItem('aieo_pv_in_session', String(pageViewsInSession));
    } catch (e) { /* sessionStorage blocked — leave at 1, never grows */ }

    /**
     * Dispatch a unified `aieo:engagement` CustomEvent on document.body
     * whenever a milestone fires. Push opt-in and any other consumer can
     * passively listen + react. Each reason fires at most once per page-
     * view so listeners don't get noise. `detail.reason` is one of:
     *   'scroll-25' | 'scroll-50' | 'add-to-cart' | 'time-30s' | 'pageviews-N'
     */
    var firedReasons = {};
    function dispatchEngagement(reason, extra) {
        if (firedReasons[reason]) { return; }
        firedReasons[reason] = true;
        try {
            var detail = { reason: reason, pageViewsInSession: pageViewsInSession, timeOnPage: Date.now() - pageLoadTime };
            if (extra) { for (var k in extra) { if (extra.hasOwnProperty(k)) detail[k] = extra[k]; } }
            document.dispatchEvent(new CustomEvent('aieo:engagement', { bubbles: true, detail: detail }));
        } catch (e) { /* browsers w/o CustomEvent support — silent */ }
    }
    // Make the dispatcher reachable from inside the tracker object too.
    window.__aieoSessionEngagement = {
        pageViewsInSession: pageViewsInSession,
        pageLoadTime: pageLoadTime,
        dispatch: dispatchEngagement,
    };
    // First milestone: page-view count crossing 2 (and any subsequent
    // thresholds operators could opt into). Fires immediately on init
    // because the value is already known before any interaction.
    if (pageViewsInSession >= 2) {
        dispatchEngagement('pageviews-2');
    }
    if (pageViewsInSession >= 3) {
        dispatchEngagement('pageviews-3');
    }
    // Time-on-page milestone — fires 30s after page load regardless of
    // any user interaction. Cheap setTimeout; uses requestIdleCallback if
    // available to avoid contending with first-paint work.
    setTimeout(function () { dispatchEngagement('time-30s'); }, 30000);

    // Interaction events that trigger initialization
    var interactionEvents = ['click', 'scroll', 'mousemove', 'touchstart', 'keydown'];

    /**
     * Attach lightweight listeners that will trigger full initialization
     * These listeners are passive and have minimal overhead
     */
    function attachInteractionListeners() {
        if (interactionListenersAttached) return;
        interactionListenersAttached = true;

        function onFirstInteraction(e) {
            // Remove all interaction listeners (one-time trigger)
            interactionEvents.forEach(function(eventName) {
                document.removeEventListener(eventName, onFirstInteraction, { passive: true, capture: true });
            });

            // Initialize the tracker
            initTrackerLazy(e);
        }

        // Attach listeners with capture phase to catch events early
        interactionEvents.forEach(function(eventName) {
            document.addEventListener(eventName, onFirstInteraction, { passive: true, capture: true });
        });
    }

    /**
     * Initialize tracker after first user interaction
     * @param {Event} triggerEvent - The event that triggered initialization
     */
    function initTrackerLazy(triggerEvent) {
        if (isInitialized) return;
        isInitialized = true;

        // Check if config is available
        if (typeof aieoSessionConfig === 'undefined') {
            // Config not ready, wait for it
            var configCheckInterval = setInterval(function() {
                if (typeof aieoSessionConfig !== 'undefined') {
                    clearInterval(configCheckInterval);
                    createAndInitTracker(triggerEvent);
                }
            }, 100);
            // Stop checking after 10 seconds
            setTimeout(function() { clearInterval(configCheckInterval); }, 10000);
            return;
        }

        createAndInitTracker(triggerEvent);
    }

    /**
     * Create and initialize the tracker object
     * @param {Event} triggerEvent - The event that triggered initialization
     */
    function createAndInitTracker(triggerEvent) {

    var AIEO_SESSION_TRACKER = {
        // Public engagement state — readable from any other script on the
        // page without depending on the lazy-init timing. Set at IIFE-top
        // time from sessionStorage so even bounce visitors get a count.
        pageViewsInSession: pageViewsInSession,
        pageLoadTime: pageLoadTime,
        // Configuration
        config: aieoSessionConfig,

        // Session data
        sessionId: null,
        userId: null,
        queue: [],

        // Lazy init metadata
        lazyInitialized: true,
        initTriggerEvent: triggerEvent ? triggerEvent.type : 'unknown',
        timeToFirstInteraction: Date.now() - pageLoadTime,

        // Cookie helpers
        getCookie: function(name) {
            const value = `; ${document.cookie}`;
            const parts = value.split(`; ${name}=`);
            if (parts.length === 2) return parts.pop().split(';').shift();
            return null;
        },

        setCookie: function(name, value, minutes) {
            const expires = new Date();
            expires.setTime(expires.getTime() + (minutes * 60 * 1000));
            document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/`;
        },

        generateSessionId: function() {
            const timestamp = Date.now();
            const random = Math.random().toString(36).substring(2, 15);
            return `session_${timestamp}_${random}`;
        },

        // §8.2 — per-event idempotency id so a re-delivered sendBeacon is de-duped
        // server-side (UNIQUE event_id + INSERT IGNORE), never double-counted.
        generateEventId: function() {
            try { if (window.crypto && crypto.randomUUID) { return crypto.randomUUID(); } } catch (e) {}
            return 'e_' + Date.now().toString(36) + '_' + Math.random().toString(36).substring(2, 12);
        },

        // Page timing - use the early captured time
        pageLoadTime: pageLoadTime,
        lastActivityTime: Date.now(),

        // Scroll tracking
        maxScrollDepth: 0,
        scrollCheckpoints: [25, 50, 75, 100],
        triggeredScrollCheckpoints: [],

        // Click tracking
        clickCount: 0,

        // Product image click tracking (debounced)
        lastImageClickTime: 0,
        lastImageClickSrc: null,

        // Mouse movement tracking (optional, privacy-sensitive)
        mouseMovementCount: 0,
        lastMouseMoveTime: 0,

        // Event sequencing (for attention analytics)
        eventSequence: 0,
        isFirstEventFired: false,

        // Product visit tracking (hesitation metric)
        productVisits: {}, // { product_id: visit_count }

        // Current page context
        currentProductId: null,
        currentCategoryId: null,

        /**
         * Initialize tracker (called after user interaction)
         */
        init: function() {
            var self = this;

            // Get or create session ID (client-side, cache-proof).
            // §8.1 — adopt the durable, always-on aieo_attr_sid (Attribution's UUIDv4,
            // 30-day) as the key when present, so sessions.session_id matches
            // addcart_events.session_id and the session↔attribution bridge joins.
            // Fall back to our own 30-min cookie otherwise.
            this.sessionId = this.getCookie('aieo_attr_sid') || this.getCookie('aieo_session_id');
            if (!this.sessionId) {
                this.sessionId = this.generateSessionId();
                this.setCookie('aieo_session_id', this.sessionId, 30); // 30 minutes
            }

            this.userId = this.config.userId || 0;

            // console.log('[AIEO Tracker] Lazy initialized after', this.timeToFirstInteraction, 'ms on', this.initTriggerEvent);

            // Attach unload handler immediately (critical for data capture)
            this.attachUnloadHandler();

            // Attach event listeners
            this.attachEventListeners();

            // Track initial page view with lazy init metadata
            this.trackPageView();
            this.trackOrderCompletion();

            // Track A — start checkout active/idle accumulation (no-op unless
            // we are on a checkout-form page).
            this.initCheckoutTiming();

            // Start auto-flush timer
            this.startAutoFlush();
        },

        /**
         * Schedule a task to run during idle time (non-blocking)
         * Falls back to setTimeout for browsers without requestIdleCallback
         */
        scheduleIdleTask: function(callback, timeout) {
            timeout = timeout || 2000;
            if (typeof requestIdleCallback === 'function') {
                requestIdleCallback(callback, { timeout: timeout });
            } else {
                setTimeout(callback, 50); // Small delay to yield to main thread
            }
        },

        /**
         * Attach all event listeners
         * Uses vanilla JS where possible, jQuery only for WooCommerce events
         */
        attachEventListeners: function() {
            var self = this;
            var body = document.body;
            var hasJQuery = ($ !== null);

            // Helper: check if body has class (vanilla JS)
            function bodyHasClass(className) {
                return body.classList.contains(className);
            }

            // Product page detection
            if (bodyHasClass('single-product')) {
                this.trackProductView();
            }

            // Category page detection
            if (bodyHasClass('tax-product_cat') || bodyHasClass('product-category')) {
                this.trackCategoryView();
            }

            // WooCommerce events require jQuery (they use jQuery custom events)
            if (hasJQuery) {
                // WooCommerce add to cart events
                $(document.body).on('added_to_cart', function(event, fragments, cart_hash, $button) {
                    self.trackAddToCart($button);
                });

                // WooCommerce remove from cart (on cart page)
                $(document).on('click', '.product-remove > a', function() {
                    var productName = $(this).closest('tr').find('.product-name').text().trim();
                    self.track('remove_from_cart', {
                        product_name: productName
                    });
                });

                // Wishlist events (YITH WooCommerce Wishlist)
                $(document).on('added_to_wishlist', function(event, $link, product_id) {
                    var productName = $link.closest('.product').find('.woocommerce-loop-product__title').text().trim();
                    self.track('add_to_wishlist', {
                        product_id: product_id,
                        product_name: productName
                    });
                });
            }

            // Scroll depth tracking (vanilla JS with throttle)
            if (this.config.trackScrollDepth) {
                window.addEventListener('scroll', throttle(500, function() {
                    self.trackScrollDepth();
                }), { passive: true });
            }

            // Click tracking (vanilla JS)
            if (this.config.trackClicks) {
                document.addEventListener('click', function(e) {
                    var target = e.target;
                    var $target = hasJQuery ? $(target) : null;
                    var trackedByCustomRule = false;

                    // Detect product image clicks (works across different themes)
                    if (bodyHasClass('single-product')) {
                        // Check custom image click rules first (they take precedence) - requires jQuery
                        if (hasJQuery && typeof AIEO_SESSION_TRACKER.customImageClickRules === 'function') {
                            trackedByCustomRule = AIEO_SESSION_TRACKER.customImageClickRules($target, self);
                        }

                        // Only track standard product gallery clicks if NOT already tracked by custom rule
                        if (!trackedByCustomRule) {
                            // Check if click is on/within product gallery (vanilla JS)
                            var isProductImage = target.tagName === 'IMG' ||
                                                target.querySelector('img') !== null ||
                                                target.closest('.woocommerce-product-gallery, .product-images, .images, .product-gallery') !== null;

                            if (isProductImage && hasJQuery) {
                                self.trackProductImageClick($target, 'product_gallery');
                            }
                        }
                    }

                    // Only track general clicks if NOT tracked by custom rule
                    if (!trackedByCustomRule) {
                        var clickedElement = target.closest('a, button');

                        // When throttling enabled: only track significant clicks (buttons/links)
                        // When throttling disabled: track ALL clicks
                        if (self.config.throttleClicks) {
                            // Only track significant clicks (links and buttons)
                            if (clickedElement && hasJQuery) {
                                self.clickCount++;
                                self.trackClick($(clickedElement));
                            }
                        } else {
                            // Track ALL clicks
                            self.clickCount++;
                            if (hasJQuery) {
                                self.trackClick($target || $(target));
                            }
                        }
                    }
                });
            }

            // Mouse movement tracking (vanilla JS with throttle)
            if (this.config.trackMouseMovement) {
                document.addEventListener('mousemove', throttle(1000, function(e) {
                    self.trackMouseMovement(e);
                }), { passive: true });
            }

            // Activity tracking - vanilla JS with throttle
            var activityThrottled = throttle(1000, function() {
                self.lastActivityTime = Date.now();
            });
            document.addEventListener('click', activityThrottled, { passive: true });
            document.addEventListener('scroll', activityThrottled, { passive: true });
            document.addEventListener('keydown', activityThrottled, { passive: true });
            document.addEventListener('mousemove', activityThrottled, { passive: true });
        },

        /**
         * Track generic event
         */
        track: function(eventType, data) {
            data = data || {};

            // Increment event sequence
            this.eventSequence++;

            // Add sequence and position flags to data
            data.event_sequence = this.eventSequence;
            data.is_first_event = !this.isFirstEventFired ? 1 : 0;

            // Mark first event as fired
            if (!this.isFirstEventFired) {
                this.isFirstEventFired = true;
            }

            // Track product visits (hesitation metric)
            if (eventType === 'product_view' && data.product_id) {
                if (!this.productVisits[data.product_id]) {
                    this.productVisits[data.product_id] = 0;
                }
                this.productVisits[data.product_id]++;
                data.visit_number = this.productVisits[data.product_id];
            }

            // Add checkout step tracking
            const checkoutStep = this.getCheckoutStep();
            if (checkoutStep) {
                data.checkout_step = checkoutStep;
            }

            // Calculate time on page for product/category views
            if (eventType === 'product_view' || eventType === 'category_view') {
                data.time_on_page = Math.round((Date.now() - this.pageLoadTime) / 1000);
            }

            const event = {
                event_id: this.generateEventId(),
                session_id: this.sessionId,
                user_id: this.userId,
                event_type: eventType,
                data: data,
                url: window.location.href,
                referrer: document.referrer,
                timestamp: Date.now()
            };

            this.queue.push(event);

            // Immediate flush for critical events
            if (this.isCriticalEvent(eventType)) {
                this.flush();
            }
        },

        /**
         * Check if event is critical (should be flushed immediately)
         */
        isCriticalEvent: function(eventType) {
            const criticalEvents = [
                'add_to_cart',
                'remove_from_cart',
                'add_to_wishlist',
                'checkout_start',
                'purchase_complete',
                'checkout_timing'
            ];
            return criticalEvents.indexOf(eventType) !== -1;
        },

        /**
         * Track page view
         */
        trackPageView: function() {
            var pageType = this.getPageType();
            var pageId = this.getPageId();
            var pageTitle = document.title;

            this.track('page_view', {
                page_type: pageType,
                page_id: pageId,
                page_title: pageTitle,
                // Add lazy init metadata
                lazy_init: 1,
                init_trigger: this.initTriggerEvent,
                time_to_interaction: this.timeToFirstInteraction
            });
        },

        /**
         * Helper: Get first matching element's text content (vanilla JS)
         */
        getFirstElementText: function(selectors) {
            var selectorArray = selectors.split(',').map(function(s) { return s.trim(); });
            for (var i = 0; i < selectorArray.length; i++) {
                var el = document.querySelector(selectorArray[i]);
                if (el) {
                    return (el.textContent || el.innerText || '').trim();
                }
            }
            return '';
        },

        /**
         * Track product view
         */
        trackProductView: function() {
            var productId = this.getProductId();
            var productName = this.getFirstElementText('.product_title, .product-title, h1.title');
            var productPrice = this.getFirstElementText('.price .amount, .price');

            if (productId) {
                // Store current product ID for context
                this.currentProductId = productId;

                this.track('product_view', {
                    product_id: productId,
                    product_name: productName,
                    product_price: productPrice
                });

                // console.log('[AIEO Tracker] Product view:', productName);
            }
        },

        /**
         * Track category view
         */
        trackCategoryView: function() {
            var categoryId = this.getCategoryId();
            var categoryName = this.getFirstElementText('.page-title, .woocommerce-products-header__title, h1');

            if (categoryId) {
                // Store current category ID for context
                this.currentCategoryId = categoryId;

                this.track('category_view', {
                    category_id: categoryId,
                    category_name: categoryName
                });

                // console.log('[AIEO Tracker] Category view:', categoryName);
            }
        },

        /**
         * Track add to cart
         */
        trackAddToCart: function($button) {
            // $button is optional — callers like our own swatches bulk-add (and
            // some themes) trigger `added_to_cart` with only [fragments, cart_hash],
            // so the 4th arg is undefined. Guard every $button.* access and fall
            // back to page context for product identification.
            const hasButton = $button && typeof $button.data === 'function' && $button.length;

            // Try to get product ID from button data attributes
            let productId = hasButton ? ($button.data('product_id') || $button.data('productId')) : undefined;

            // If not found, try to get from current product context (single product page)
            if (!productId) {
                productId = this.currentProductId || this.getProductId();
            }

            const quantity = hasButton ? ($button.data('quantity') || 1) : 1;

            // For single product pages, get the main product title directly from the form context
            let productName = '';

            // First check if we're in the add to cart form (most reliable for variable products)
            const $form = hasButton ? $button.closest('form.cart, .cart') : $();
            if ($form.length > 0) {
                // Look for title in the same product wrapper as the form
                productName = $form.closest('.product, div[itemtype*="schema.org/Product"]').find('.product_title, h1.entry-title').first().text().trim();
            }

            // If not found, try button's product container (for product loops/archives)
            if (!productName && hasButton) {
                productName = $button.closest('.product, .product-item').find('.woocommerce-loop-product__title, .product-title').first().text().trim();
            }

            // If still not found, look in the main product div that has the type="main" or is-layout-flow
            if (!productName) {
                productName = $('div.product.type-product .product_title, div[itemtype*="Product"] .product_title').first().text().trim();
            }

            // Fallback to document.title if we're on a single product page (remove site suffix)
            if (!productName && $('body').hasClass('single-product')) {
                productName = document.title.split('–')[0].split('|')[0].trim();
            }

            this.track('add_to_cart', {
                product_id: productId,
                product_name: productName,
                quantity: quantity
            });

            // Dispatch engagement milestone — strongest commercial-intent
            // signal. Consumers (push opt-in banner, etc.) can mount on
            // this without waiting for time/scroll thresholds.
            if (window.__aieoSessionEngagement && typeof window.__aieoSessionEngagement.dispatch === 'function') {
                window.__aieoSessionEngagement.dispatch('add-to-cart', { productId: productId });
            }

            // console.log('[AIEO Tracker] Added to cart:', productId, productName, 'Qty:', quantity);
        },

        /**
         * Track scroll depth (vanilla JS)
         */
        trackScrollDepth: function() {
            var windowHeight = window.innerHeight;
            var documentHeight = Math.max(
                document.body.scrollHeight,
                document.body.offsetHeight,
                document.documentElement.clientHeight,
                document.documentElement.scrollHeight,
                document.documentElement.offsetHeight
            );
            var scrollTop = window.pageYOffset || document.documentElement.scrollTop;

            var scrollPercent = Math.round(((scrollTop + windowHeight) / documentHeight) * 100);

            // Update max depth
            if (scrollPercent > this.maxScrollDepth) {
                this.maxScrollDepth = scrollPercent;
            }

            // Check for milestone checkpoints
            var self = this;
            this.scrollCheckpoints.forEach(function(checkpoint) {
                if (scrollPercent >= checkpoint && self.triggeredScrollCheckpoints.indexOf(checkpoint) === -1) {
                    self.triggeredScrollCheckpoints.push(checkpoint);

                    self.track('scroll_depth', {
                        scroll_depth: checkpoint,
                        max_depth: self.maxScrollDepth
                    });

                    // Dispatch engagement milestone at the 25 / 50 / 75
                    // scroll points. Push opt-in listens for these to
                    // gate the banner mount — visitors who scroll past
                    // 25% have shown real reading intent.
                    if (window.__aieoSessionEngagement && typeof window.__aieoSessionEngagement.dispatch === 'function') {
                        if (checkpoint >= 25 && checkpoint < 50) {
                            window.__aieoSessionEngagement.dispatch('scroll-25', { scrollPercent: checkpoint });
                        } else if (checkpoint >= 50 && checkpoint < 75) {
                            window.__aieoSessionEngagement.dispatch('scroll-50', { scrollPercent: checkpoint });
                        } else if (checkpoint >= 75) {
                            window.__aieoSessionEngagement.dispatch('scroll-75', { scrollPercent: checkpoint });
                        }
                    }

                    // console.log('[AIEO Tracker] Scroll milestone:', checkpoint + '%');
                }
            });
        },

        /**
         * Track product image click
         */
        trackProductImageClick: function($element, imageType) {
            // Try to get product ID from element's data attribute first (for wishlist buttons, etc)
            // Note: jQuery converts data-product-id to productId (camelCase)
            let productId = $element.data('productId') || $element.data('product_id') ||
                           $element.closest('[data-product-id]').data('productId');

            // Fallback to current product context
            if (!productId) {
                productId = this.currentProductId || this.getProductId();
            }

            const $img = $element.is('img') ? $element : $element.find('img').first();
            const imageSrc = $img.attr('src') || $img.attr('data-src') || '';
            const imageAlt = $img.attr('alt') || '';
            const now = Date.now();

            // Get product name (same logic as trackProductView)
            const productName = $('.product_title, .product-title, h1.title').first().text().trim();

            // For non-image events (like wishlist), skip debounce
            const isImageClick = $img.length > 0 && imageSrc;

            // Debounce: Only track if it's been more than 5 seconds since last image click
            // OR if it's a different image (handles gallery navigation and variation switches)
            // Skip debounce for non-image events (wishlist, etc.)
            if (isImageClick && now - this.lastImageClickTime < 5000 && imageSrc === this.lastImageClickSrc) {
                // console.log('[AIEO Tracker] Image click debounced (too soon)');
                return;
            }

            // Update debounce trackers (only for actual image clicks)
            if (isImageClick) {
                this.lastImageClickTime = now;
                this.lastImageClickSrc = imageSrc;
            }

            // Get variation data if it's a swatch
            let variationData = {};
            if (imageType === 'variation_swatch') {
                const $variationItem = $element.closest('.merchant-variation-item');
                if ($variationItem.length > 0) {
                    variationData = {
                        variation_text: $variationItem.attr('data-original-text') || $variationItem.find('.merchant-variation-text').text().trim(),
                        variation_value: $variationItem.attr('value') || ''
                    };
                }
            }

            this.track('product_image_click', {
                product_id: productId,
                product_name: productName,
                image_src: imageSrc.substring(0, 200), // Limit URL length
                image_alt: imageAlt.substring(0, 100),
                image_type: imageType || 'product_gallery',
                click_count: this.clickCount,
                element_class: $element.attr('class') || '',
                is_gallery_image: $element.closest('.woocommerce-product-gallery').length > 0,
                ...variationData
            });
        },

        /**
         * Track custom click event (for custom rules like wishlist)
         */
        trackCustomClick: function($element, eventName) {
            // Try to get product ID from element's data attribute first
            let productId = $element.data('productId') || $element.data('product_id') ||
                           $element.closest('[data-product-id]').data('productId');

            // Fallback to current product context
            if (!productId) {
                productId = this.currentProductId || this.getProductId();
            }

            // Get product name
            const productName = $('.product_title, .product-title, h1.title').first().text().trim();

            // Get element info
            const elementClass = $element.attr('class') || '';
            const elementId = $element.attr('id') || '';
            const elementText = $element.text().trim().substring(0, 50);

            // Track with the custom event name
            this.track(eventName, {
                product_id: productId,
                product_name: productName,
                element_class: elementClass,
                element_id: elementId,
                element_text: elementText,
                click_count: this.clickCount
            });

            // console.log('[AIEO Tracker] Custom click:', eventName, productId, productName);
        },

        /**
         * Track click
         */
        trackClick: function($element) {
            const elementType = $element.prop('tagName').toLowerCase();
            const elementText = $element.text().trim().substring(0, 50);
            const elementClass = $element.attr('class') || '';
            const elementId = $element.attr('id') || '';
            const href = $element.attr('href') || '';

            // Check if this is a related product click (has UTM parameters)
            let utmData = null;
            if (href && href.indexOf('utm_source=AIO') !== -1) {
                utmData = this.parseUTMParameters(href);
            }

            // Only track significant clicks (not tracking every single one to reduce data)
            const significantClasses = ['add_to_cart', 'button', 'product', 'category', 'wishlist'];
            const isSignificant = significantClasses.some(cls => elementClass.indexOf(cls) !== -1);

            // Also track if it has UTM parameters (related product recommendation)
            if (isSignificant || elementType === 'button' || utmData) {
                const clickData = {
                    element_type: elementType,
                    element_text: elementText,
                    element_class: elementClass,
                    element_id: elementId,
                    click_count: this.clickCount
                };

                // Add UTM data if present (related product recommendation tracking)
                if (utmData) {
                    clickData.utm_source = utmData.utm_source;
                    clickData.utm_campaign = utmData.utm_campaign;
                    clickData.recommendation_type = utmData.recommendation_type;
                    clickData.source_product_id = utmData.source_product_id;
                    clickData.target_product_id = utmData.target_product_id;
                    clickData.sequence = utmData.sequence;
                }

                this.track('click', clickData);

                // If it's a related product click, also track a specific event
                if (utmData) {
                    this.track('related_product_click', {
                        recommendation_type: utmData.recommendation_type,
                        source_product_id: utmData.source_product_id,
                        target_product_id: utmData.target_product_id,
                        sequence: utmData.sequence,
                        utm_source: utmData.utm_source,
                        utm_campaign: utmData.utm_campaign
                    });
                }
            }
        },

        /**
         * Parse UTM parameters from URL
         * Expected format: utm_source=AIO<Type>&utm_campaign=<sequence>_<origin_product_id>_<target_product_id>
         */
        parseUTMParameters: function(url) {
            try {
                const urlObj = new URL(url, window.location.origin);
                const params = new URLSearchParams(urlObj.search);

                const utmSource = params.get('utm_source');
                const utmCampaign = params.get('utm_campaign');

                if (!utmSource || !utmCampaign) {
                    return null;
                }

                // Parse recommendation type from utm_source (e.g., "AIOUpSell" -> "UpSell")
                let recommendationType = utmSource.replace(/^AIO/, '');

                // Parse campaign: <sequence>_<origin_product_id>_<target_product_id>
                const campaignParts = utmCampaign.split('_');

                if (campaignParts.length >= 3) {
                    return {
                        utm_source: utmSource,
                        utm_campaign: utmCampaign,
                        recommendation_type: recommendationType,
                        sequence: parseInt(campaignParts[0]) || 0,
                        source_product_id: parseInt(campaignParts[1]) || 0,
                        target_product_id: parseInt(campaignParts[2]) || 0
                    };
                }

                return null;
            } catch (e) {
                // console.warn('[AIEO Tracker] Failed to parse UTM parameters:', e);
                return null;
            }
        },

        /**
         * Track mouse movement (privacy-sensitive, throttled heavily)
         */
        trackMouseMovement: function(e) {
            this.mouseMovementCount++;

            // Only record every 5 seconds
            if (Date.now() - this.lastMouseMoveTime > 5000) {
                this.lastMouseMoveTime = Date.now();

                this.track('mouse_movement', {
                    movement_count: this.mouseMovementCount,
                    // NOT tracking coordinates for privacy
                });
            }
        },

        /**
         * Helper: check if body has class (vanilla JS)
         */
        bodyHasClass: function(className) {
            return document.body.classList.contains(className);
        },

        /**
         * Get page type (vanilla JS)
         */
        getPageType: function() {
            if (this.bodyHasClass('single-product')) return 'product';
            if (this.bodyHasClass('tax-product_cat') || this.bodyHasClass('product-category')) return 'category';
            if (this.bodyHasClass('woocommerce-cart')) return 'cart';
            if (this.bodyHasClass('woocommerce-checkout')) return 'checkout';
            if (this.bodyHasClass('home') || this.bodyHasClass('front-page')) return 'home';
            if (this.bodyHasClass('shop') || this.bodyHasClass('woocommerce-shop')) return 'shop';
            if (this.bodyHasClass('single-post')) return 'blog_post';
            if (this.bodyHasClass('page')) return 'page';

            return 'other';
        },

        /**
         * Get current page ID (vanilla JS)
         */
        getPageId: function() {
            var pageIdMeta = document.querySelector('meta[name="page-id"]');
            if (pageIdMeta) return pageIdMeta.getAttribute('content');

            // Try to extract from body class
            var bodyClasses = document.body.className;
            var match = bodyClasses ? bodyClasses.match(/page-id-(\d+)/) : null;
            return match ? match[1] : 0;
        },

        /**
         * Get product ID (vanilla JS)
         */
        getProductId: function() {
            // Try meta tag first
            var productIdMeta = document.querySelector('meta[name="product-id"]');
            if (productIdMeta) return productIdMeta.getAttribute('content');

            // Try data attribute
            var productEl = document.querySelector('.product');
            if (productEl) {
                var productIdData = productEl.getAttribute('data-product-id') || productEl.dataset.productId;
                if (productIdData) return productIdData;
            }

            // Try body class
            var bodyClasses = document.body.className;
            var match = bodyClasses ? bodyClasses.match(/postid-(\d+)/) : null;
            return match ? match[1] : 0;
        },

        /**
         * Get category ID (vanilla JS)
         */
        getCategoryId: function() {
            // Try body class
            var bodyClasses = document.body.className;
            var match = bodyClasses ? bodyClasses.match(/term-(\d+)/) : null;
            return match ? match[1] : 0;
        },

        /**
         * Helper: check if element is visible (vanilla JS)
         */
        isElementVisible: function(el) {
            if (!el) return false;
            return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
        },

        /**
         * Get checkout step (for funnel analysis) - vanilla JS
         */
        getCheckoutStep: function() {
            if (this.bodyHasClass('woocommerce-cart')) {
                return 'cart';
            }

            if (this.bodyHasClass('woocommerce-checkout')) {
                // Detect specific checkout steps by form sections or body classes
                if (this.bodyHasClass('woocommerce-order-received') || document.querySelector('.woocommerce-order-received')) {
                    return 'order_received';
                }

                // Check for checkout form fields to determine step
                var billingField = document.getElementById('billing_first_name');
                if (billingField) {
                    var paymentEl = document.getElementById('payment');
                    if (paymentEl && this.isElementVisible(paymentEl)) {
                        return 'payment';
                    }
                    var shippingEl = document.getElementById('shipping_method');
                    if (shippingEl && this.isElementVisible(shippingEl)) {
                        return 'shipping';
                    }
                    return 'billing';
                }

                // Default checkout step
                return 'checkout';
            }

            return null;
        },

        /**
         * Track A — checkout active/idle accumulation.
         * Light heartbeat while the shopper is ON a checkout-form page,
         * splitting wall-clock time into ACTIVE (interacted within the idle
         * threshold) vs IDLE (no interaction, or tab hidden). State is kept in
         * sessionStorage so it survives the checkout -> order-received
         * navigation, then emitted once as a `checkout_timing` event.
         */
        initCheckoutTiming: function() {
            var step = this.getCheckoutStep();
            if (!step || step === 'order_received' || step === 'cart') {
                return;
            }
            var self = this;
            var idleMs = (this.config.checkoutIdleThreshold || 20) * 1000;
            var now = Date.now();
            var ct = this.readCheckoutTiming();
            if (!ct) {
                ct = { startedMs: now, activeMs: 0, idleMs: 0, steps: {}, lastTickMs: now, lastStep: step, lastStepMs: now };
            } else {
                ct.lastTickMs = now;
                if (!ct.lastStep) { ct.lastStep = step; ct.lastStepMs = now; }
            }
            this.checkoutTiming = ct;
            this.writeCheckoutTiming(ct);

            this._ckInterval = setInterval(function() {
                self.tickCheckoutTiming(idleMs);
            }, 2000);
        },

        tickCheckoutTiming: function(idleMs) {
            var ct = this.checkoutTiming;
            if (!ct) { return; }
            var now = Date.now();
            var delta = now - ct.lastTickMs;
            ct.lastTickMs = now;
            if (delta <= 0) { return; }

            var isIdle = (typeof document.hidden !== 'undefined' && document.hidden) ||
                         ((now - this.lastActivityTime) > idleMs);
            if (isIdle) { ct.idleMs += delta; } else { ct.activeMs += delta; }

            var step = this.getCheckoutStep();
            if (step && step !== 'order_received' && step !== ct.lastStep) {
                if (ct.lastStep) {
                    ct.steps[ct.lastStep] = (ct.steps[ct.lastStep] || 0) + (now - ct.lastStepMs);
                }
                ct.lastStep = step;
                ct.lastStepMs = now;
            }
            this.writeCheckoutTiming(ct);
        },

        finalizeCheckoutTiming: function(orderId) {
            var ct = this.readCheckoutTiming();
            this.clearCheckoutTiming();
            if (this._ckInterval) { clearInterval(this._ckInterval); this._ckInterval = null; }
            if (!ct || !orderId) { return; }
            var now = Date.now();
            if (ct.lastStep) {
                ct.steps[ct.lastStep] = (ct.steps[ct.lastStep] || 0) + Math.max(0, now - (ct.lastStepMs || now));
            }
            var stepsSec = {};
            for (var k in ct.steps) {
                if (Object.prototype.hasOwnProperty.call(ct.steps, k)) {
                    stepsSec[k] = Math.round(ct.steps[k] / 1000);
                }
            }
            this.track('checkout_timing', {
                order_id: parseInt(orderId, 10),
                total_seconds: Math.round((ct.activeMs + ct.idleMs) / 1000),
                active_seconds: Math.round(ct.activeMs / 1000),
                idle_seconds: Math.round(ct.idleMs / 1000),
                step_durations: stepsSec
            });
        },

        readCheckoutTiming: function() {
            try {
                var raw = sessionStorage.getItem('aieoCheckoutTiming');
                return raw ? JSON.parse(raw) : null;
            } catch (e) { return null; }
        },
        writeCheckoutTiming: function(ct) {
            try { sessionStorage.setItem('aieoCheckoutTiming', JSON.stringify(ct)); } catch (e) {}
        },
        clearCheckoutTiming: function() {
            try { sessionStorage.removeItem('aieoCheckoutTiming'); } catch (e) {}
        },

        /**
         * Flush queue to server (non-blocking, fire-and-forget)
         * @param {boolean} isUnload - True if called during page unload (skip idle scheduling)
         */
        flush: function(isUnload) {
            if (this.queue.length === 0) {
                return;
            }

            var self = this;
            var eventsToSend = this.queue.splice(0); // Empty queue immediately

            var payload = {
                events: eventsToSend
            };

            // console.log('[AIEO Tracker] Flushing', eventsToSend.length, 'events');

            // For page unload, send immediately
            if (isUnload) {
                this.sendBeaconOrFetch(payload);
                return;
            }

            // For normal flushes, schedule during idle time to avoid blocking
            this.scheduleIdleTask(function() {
                self.sendBeaconOrFetch(payload);
            }, 500);
        },

        /**
         * Send data using sendBeacon (preferred) or fetch (fallback)
         * Both are non-blocking and fire-and-forget
         */
        sendBeaconOrFetch: function(payload) {
            var jsonPayload = JSON.stringify(payload);

            // Use sendBeacon for reliability (works even on page unload, truly fire-and-forget)
            if (navigator.sendBeacon) {
                var blob = new Blob([jsonPayload], { type: 'application/json' });
                var sent = navigator.sendBeacon(this.config.endpoint, blob);

                if (!sent) {
                    // console.warn('[AIEO Tracker] sendBeacon failed, falling back to fetch');
                    this.flushWithFetch(payload);
                }
            } else {
                // Fallback to fetch
                this.flushWithFetch(payload);
            }
        },

        /**
         * Flush using fetch API (fallback, non-blocking)
         */
        flushWithFetch: function(payload) {
            // Use fetch with keepalive for non-blocking fire-and-forget request
            fetch(this.config.endpoint, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(payload),
                keepalive: true, // Important: allows request to complete even if page unloads
                // Don't wait for response - fire and forget
                mode: 'cors',
                credentials: 'same-origin'
            }).catch(function() {
                // Silently ignore errors - fire and forget
            });
        },

        /**
         * Start auto-flush timer
         */
        startAutoFlush: function() {
            const interval = this.config.flushInterval || 5000; // Default 5 seconds

            setInterval(function() {
                this.flush();
            }.bind(this), interval);

            // console.log('[AIEO Tracker] Auto-flush every', interval / 1000, 'seconds');
        },

        /**
         * Attach page unload handler
         * Uses vanilla JS for cache plugin compatibility
         */
        attachUnloadHandler: function() {
            var self = this;

            // Track time on page before leaving - use vanilla JS
            window.addEventListener('beforeunload', function() {
                var timeOnPage = Math.round((Date.now() - self.pageLoadTime) / 1000);

                // Mark this as the last event
                self.track('page_exit', {
                    time_on_page: timeOnPage,
                    max_scroll_depth: self.maxScrollDepth,
                    click_count: self.clickCount,
                    is_last_event: 1
                });

                // Final flush - pass true to skip idle scheduling (critical for unload)
                self.flush(true);
            });

            // pagehide is more reliable than beforeunload on mobile
            window.addEventListener('pagehide', function() {
                self.flush(true);
            });

            // Also track visibility change (tab switching)
            document.addEventListener('visibilitychange', function() {
                if (document.hidden) {
                    self.track('tab_hidden', {
                        time_active: Math.round((Date.now() - self.pageLoadTime) / 1000)
                    });
                    self.flush(true); // Use immediate flush for tab hidden too
                } else {
                    self.track('tab_visible', {});
                }
            });
        },

        /**
         * Track WooCommerce order completion (vanilla JS)
         */
        trackOrderCompletion: function() {
            // Detect order received page
            if (this.bodyHasClass('woocommerce-order-received') || document.querySelector('.woocommerce-order-received')) {
                // Try to extract order ID from URL or page content
                var orderId = null;

                // Method 1: From URL parameter (?order=123 or /order-received/123/)
                var urlParams = new URLSearchParams(window.location.search);
                orderId = urlParams.get('order') || urlParams.get('order_id');

                // Method 2: From URL path
                if (!orderId) {
                    var pathMatch = window.location.pathname.match(/order-received\/(\d+)/);
                    if (pathMatch) {
                        orderId = pathMatch[1];
                    }
                }

                // Method 3: From order details on page (vanilla JS)
                if (!orderId) {
                    var orderNumberText = this.getFirstElementText('.woocommerce-order-overview__order.order strong, .order-number');
                    var orderMatch = orderNumberText.match(/\d+/);
                    if (orderMatch) {
                        orderId = orderMatch[0];
                    }
                }

                if (orderId) {
                    this.track('purchase_complete', {
                        order_id: parseInt(orderId),
                        checkout_step: 'order_received'
                    });
                    // Track A — finalize + emit the checkout active/idle summary.
                    this.finalizeCheckoutTiming(orderId);
                }
            }
        }
    };

    /**
     * Vanilla JS throttle helper
     */
    function throttle(delay, fn) {
        var lastCall = 0;
        return function() {
            var now = Date.now();
            var args = arguments;
            var context = this;
            if (now - lastCall >= delay) {
                lastCall = now;
                return fn.apply(context, args);
            }
        };
    }

    // Add throttle to jQuery if available (for WooCommerce event compatibility)
    if ($ && typeof $.throttle === 'undefined') {
        $.throttle = throttle;
    }

    // Initialize the tracker
    AIEO_SESSION_TRACKER.init();

    // Expose globally for manual tracking if needed
    window.AIEO_SESSION_TRACKER = AIEO_SESSION_TRACKER;

    } // End of createAndInitTracker function

    // Start: Attach lightweight interaction listeners
    // The tracker will only initialize when user actually interacts with the page
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', attachInteractionListeners);
    } else {
        // DOM already loaded
        attachInteractionListeners();
    }

})();
