/**
 * OM Sticky Add to Cart JavaScript
 *
 * Handles:
 * - Scroll-based visibility
 * - Smart variation handling
 * - Persistent mode (cross-page navigation)
 * - Form synchronization with main product form
 */

(function($) {
    'use strict';

    var __ = ( window.wp && window.wp.i18n && window.wp.i18n.__ ) ? window.wp.i18n.__ : function (s) { return s; };
    var _x = ( window.wp && window.wp.i18n && window.wp.i18n._x ) ? window.wp.i18n._x : function (s) { return s; };
    var _n = ( window.wp && window.wp.i18n && window.wp.i18n._n ) ? window.wp.i18n._n : function (s, p, n) { return n === 1 ? s : p; };
    var _sprintf = ( window.wp && window.wp.i18n && window.wp.i18n.sprintf ) ? window.wp.i18n.sprintf : function (s) { return s; };

    var OmStickyATC = {
        // Configuration (set from PHP)
        config: {},

        // State
        isVisible: false,
        lastScrollTop: 0,
        scrollThreshold: 100,
        // Dynamic threshold: scroll distance at which the main product
        // page's Add-to-cart button moves OFF the top of the viewport.
        // Below this, showing our sticky bar would duplicate a button the
        // shopper already sees. 0 = not yet computed / not applicable.
        dynamicThreshold: 0,
        variationSelected: false,
        persistentProductId: null,

        // DOM elements
        $wrapper: null,
        $mainForm: null,

        /**
         * Initialize the module
         */
        init: function() {
            var self = this;

            // Get config from localized script
            if (typeof omStickyATC !== 'undefined') {
                this.config = omStickyATC;
            } else {
                console.warn('OM Sticky ATC: Configuration not found');
                return;
            }

            this.$wrapper = $('#om-sticky-atc-wrapper');
            if (!this.$wrapper.length) {
                return;
            }

            this.scrollThreshold = this.config.displayAfterScroll || 100;

            // Initialize quantity buttons in sticky bar (standalone - no Botiga Pro dependency)
            this.initQuantityButtons();

            // Initialize based on context
            if (this.config.isProductPage) {
                this.initProductPage();
            } else if (this.config.persistentEnabled) {
                this.initPersistentMode();
            }

            // Always bind scroll handler
            this.bindScrollHandler();

            // Bind dismiss button
            this.bindDismissButton();

            // Compute "show after main ATC has scrolled past" threshold.
            // Done after layout settles (load event) + on resize, because
            // product gallery images can shift the ATC button's position.
            var self = this;
            this.updateDynamicThreshold();
            $(window).on('load.omStickyATC resize.omStickyATC', function () {
                self.updateDynamicThreshold();
            });
            // One extra pass after a beat in case the theme lazy-renders
            // the ATC button below the fold.
            setTimeout(function () { self.updateDynamicThreshold(); }, 600);
        },

        /**
         * Find the MAIN product-page Add-to-cart button (outside the
         * sticky bar) and record the scroll position at which its bottom
         * edge leaves the viewport. Used by handleScroll() to delay the
         * sticky bar until the shopper has actually scrolled past the
         * primary CTA — avoids showing two Add-to-cart buttons at once.
         */
        updateDynamicThreshold: function () {
            this.dynamicThreshold = 0;
            if (!this.config.isProductPage) { return; }
            var $main = $('.single_add_to_cart_button')
                .not('#om-sticky-atc-wrapper .single_add_to_cart_button')
                .filter(':visible')
                .first();
            if (!$main.length) { return; }
            var rect = $main[0].getBoundingClientRect();
            var pageY = window.pageYOffset || document.documentElement.scrollTop || 0;
            // document-coordinate of the button's BOTTOM edge.
            this.dynamicThreshold = Math.max(0, Math.round(pageY + rect.bottom));
        },

        /**
         * Initialize for product page
         */
        initProductPage: function() {
            var self = this;

            this.$mainForm = $('form.variations_form, form.cart').first();

            // Store product data for persistent mode
            if (this.config.persistentEnabled && this.config.productData && this.config.productData.id) {
                this.storeProductData(this.config.productData);
            }

            // Check if multi-select mode is enabled (OM Product Swatches).
            // ALSO require the AIEO config flag — that flag is only true on
            // VARIABLE products, so simple products won't get the
            // "0 selected / Add Selected to Cart" UI they have nothing to
            // select from.
            if ($('body').hasClass('om-multi-select-enabled') && this.config.multiSelectEnabled) {
                this.initMultiSelectMode();
            }
            // Many-variations dropdown (sticky bar shows a <select> with
            // proper variation names; selecting one syncs the host form).
            else if (this.$wrapper.find('.om-satc-variation-select').length) {
                this.initVariationDropdown();
            }
            // Check for inline swatches (few variations - show swatches directly in sticky bar)
            else if (this.$wrapper.find('.om-satc-inline-swatches').length) {
                this.initInlineSwatches();
            }
            // Check for smart variation handling (many variations - hide until selection)
            else if (this.config.smartVariationEnabled && $('body').hasClass('om-satc-many-vars')) {
                this.initSmartVariations();
            } else {
                // Regular product - sync with main form
                this.initFormSync();
            }

            // Handle add to cart from sticky bar
            this.bindAddToCart();

            // Wire up our custom +/- qty buttons (we render our own
            // instead of relying on Botiga's qty-init JS which doesn't
            // catch late-rendered footer markup).
            this.bindStickyQtyButtons();

            // Force a static "Add to cart" label on the sticky bar's
            // button — Botiga Pro's WC fragments swap the entire button
            // HTML on every cart change to "X in cart ✓", which we don't
            // want here. We re-stamp the label on init AND every time
            // WC announces a fragments refresh.
            this.enforceStickyButtonLabel();
            $(document.body).on('wc_fragments_refreshed wc_fragments_loaded added_to_cart', this.enforceStickyButtonLabel.bind(this));

            // Cart-count summary notices ("X items in cart") from
            // Botiga / WC / OM Product Swatches get re-injected on every
            // cart change. Strip them globally — user explicitly asked
            // for them not to be shown.
            this.suppressCartCountNotice();
            $(document.body).on('added_to_cart wc_fragments_refreshed wc_fragments_loaded removed_from_cart updated_wc_div', this.suppressCartCountNotice.bind(this));

            // Defensive: if om-product-swatches injects the summary on a
            // delay (after our event handler fires), a MutationObserver
            // catches it and removes it as soon as it lands.
            if (typeof MutationObserver !== 'undefined') {
                var self = this;
                var observer = new MutationObserver(function (mutations) {
                    for (var i = 0; i < mutations.length; i++) {
                        for (var j = 0; j < mutations[i].addedNodes.length; j++) {
                            var node = mutations[i].addedNodes[j];
                            if (node.nodeType !== 1) { continue; }
                            if (node.classList && node.classList.contains('om-cart-summary')) {
                                node.remove();
                            } else if (node.querySelectorAll) {
                                var matches = node.querySelectorAll('.om-cart-summary');
                                for (var k = 0; k < matches.length; k++) { matches[k].remove(); }
                            }
                        }
                    }
                });
                observer.observe(document.body, { childList: true, subtree: true });
            }
        },

        /**
         * Replace the sticky bar's add-to-cart button HTML with a clean
         * "Add to cart" label every time WC fragments fire. Reads the
         * label from the CSS variable PHP set on :root so it stays
         * translatable + admin-overridable.
         */
        enforceStickyButtonLabel: function () {
            var $btn = this.$wrapper.find('.single_add_to_cart_button');
            if (!$btn.length) { return; }
            // Read the CSS variable ONCE per page-load and cache. WC
            // fragments fire this handler on every cart change, and
            // getComputedStyle() right after a DOM mutation forces a
            // synchronous reflow — Lighthouse flags it as the source of
            // a 107ms unattributed forced-reflow regression. The label
            // is stable for the page lifetime (it's a server-rendered
            // CSS var), so caching is safe.
            if (!this._cachedLabel) {
                var raw = getComputedStyle(document.documentElement).getPropertyValue('--aieo-satc-add-label').trim();
                if (raw.charAt(0) === '"' || raw.charAt(0) === "'") {
                    raw = raw.substring(1, raw.length - 1);
                }
                this._cachedLabel = raw || (this.config && this.config.i18n && this.config.i18n.addToCart) || __('Add to cart', 'ai-eshop-optimizer');
            }
            var label = this._cachedLabel;
            $btn.each(function () {
                // Wipe Botiga's injected count-text + checkmark spans
                // and put a single text node back. Keep all classes/attrs.
                if (this.textContent !== label) { this.textContent = label; }
            });
        },

        /**
         * Remove the WC/Botiga "X items in cart" toast notice anywhere
         * on the page. The user wanted this gone for sticky-bar adds.
         */
        suppressCartCountNotice: function () {
            // Selectors covering WC core success notice + Botiga's
            // cart-count flag notice + OM Product Swatches' cart summary
            // box (`<div class="om-cart-summary">` injected into the
            // variations form on every add).
            var selector = [
                '.woocommerce-message',
                '.wc-block-components-notice-banner',
                '.botiga-cart-notice',
                '.botiga-cart-flag-notice',
                '.botiga-in-cart-flag-notice',
                '.woocommerce-notices-wrapper .is-success',
                '.om-cart-summary'
            ].join(',');
            // Run NOW + after a delay (notice may be injected later by
            // an async XHR or animation frame).
            $(selector).remove();
            setTimeout(function () { $(selector).remove(); }, 50);
            setTimeout(function () { $(selector).remove(); }, 250);
            setTimeout(function () { $(selector).remove(); }, 600);
        },

        /**
         * Custom +/- handlers for the sticky bar's quantity input.
         */
        bindStickyQtyButtons: function () {
            var $wrap = this.$wrapper;
            $wrap.on('click', '.om-satc-qty-minus, .om-satc-qty-plus', function (e) {
                e.preventDefault();
                var $btn   = $(this);
                var $input = $btn.closest('.om-satc-qty').find('input.qty');
                if (!$input.length) { return; }
                var step = parseInt($input.attr('step'), 10) || 1;
                var min  = parseInt($input.attr('min'),  10) || 1;
                var maxAttr = $input.attr('max');
                var max  = maxAttr ? parseInt(maxAttr, 10) : Infinity;
                var cur  = parseInt($input.val(), 10) || min;
                var next = $btn.hasClass('om-satc-qty-plus') ? cur + step : cur - step;
                if (next < min) { next = min; }
                if (next > max) { next = max; }
                $input.val(next).trigger('change');
            });
        },

        /**
         * Initialize persistent mode (on non-product pages)
         */
        initPersistentMode: function() {
            var self = this;
            var storedProduct = this.getStoredProduct();

            if (!storedProduct) {
                // No stored product, hide the wrapper
                this.$wrapper.remove();
                return;
            }

            // Check if expired
            if (this.isProductExpired(storedProduct)) {
                this.clearStoredProduct();
                this.$wrapper.remove();
                return;
            }

            // Check if the product is already in the cart
            // If so, don't show the persistent bar (no point reminding them)
            this.checkProductInCart(storedProduct.product.id, function(inCart) {
                if (inCart) {
                    // Product is already in cart, clear storage and hide
                    self.clearStoredProduct();
                    self.$wrapper.remove();
                    return;
                }

                // Product NOT in cart, load the persistent product content
                self.loadPersistentProduct(storedProduct);
            });
        },

        /**
         * Check if a product is in the cart via AJAX
         */
        checkProductInCart: function(productId, callback) {
            var self = this;

            $.ajax({
                url: this.config.ajaxUrl,
                type: 'POST',
                data: {
                    action: 'om_sticky_atc_check_cart',
                    product_id: productId
                },
                success: function(response) {
                    if (response.success) {
                        callback(response.data.in_cart);
                    } else {
                        // On error, assume not in cart
                        callback(false);
                    }
                },
                error: function() {
                    // On error, assume not in cart
                    callback(false);
                }
            });
        },

        /**
         * Initialize smart variation handling
         */
        initSmartVariations: function() {
            var self = this;
            var $form = $('form.variations_form');

            if (!$form.length) {
                return;
            }

            /**
             * Check if a variation has been selected
             */
            function hasChosenVariation() {
                var $variationId = $form.find('input[name="variation_id"]');
                return $variationId.length && $variationId.val() && $variationId.val() !== '0';
            }

            /**
             * Get summary of selected attributes
             */
            function getAttributeSummary() {
                var parts = [];
                var processedLabels = {};

                $form.find('select[name^="attribute_"]').each(function() {
                    var $sel = $(this);
                    var labelTxt = '';
                    var id = $sel.attr('id');

                    if (id) {
                        var $lab = $form.find('label[for="' + id + '"]');
                        if ($lab.length) {
                            labelTxt = $lab.text().trim().replace(/\s*:$/, '');
                        }
                    }

                    if (!labelTxt) {
                        labelTxt = $sel.attr('name').replace(/^attribute_/, '').replace(/_/g, ' ');
                        labelTxt = labelTxt.charAt(0).toUpperCase() + labelTxt.slice(1);
                    }

                    // Avoid duplicates
                    if (processedLabels[labelTxt]) {
                        return;
                    }

                    var $opt = $sel.find('option:selected');
                    if ($opt.length && $opt.val()) {
                        parts.push(labelTxt + ': ' + $opt.text());
                        processedLabels[labelTxt] = true;
                    }
                });

                return parts.join(' · ');
            }

            /**
             * Sync price and image from main form
             */
            function syncPriceAndImage() {
                // Update price
                var $priceEl = $form.find('.single_variation .price').first();
                var $stickyPrice = self.$wrapper.find('.om-satc-price');

                if ($priceEl.length && $stickyPrice.length) {
                    $stickyPrice.html($priceEl.html());
                }

                // Update image
                var $mainImg = $('.woocommerce-product-gallery__image img.wp-post-image, .woocommerce-product-gallery__image img').first();
                var $stickyImg = self.$wrapper.find('.om-satc-image img');

                if ($mainImg.length && $stickyImg.length && $mainImg.attr('src')) {
                    $stickyImg.attr('src', $mainImg.attr('src'));
                    if ($mainImg.attr('srcset')) {
                        $stickyImg.attr('srcset', $mainImg.attr('srcset'));
                    }
                }
            }

            /**
             * Update the attributes summary display
             */
            function updateAttributesDisplay() {
                if (!self.config.showSelectedAttrs) {
                    return;
                }

                var $attrsEl = self.$wrapper.find('.om-satc-attrs');
                if (!$attrsEl.length) {
                    // Create it if doesn't exist
                    var $title = self.$wrapper.find('.om-satc-title');
                    if ($title.length) {
                        $attrsEl = $('<div class="om-satc-attrs"></div>');
                        $title.after($attrsEl);
                    }
                }

                if ($attrsEl.length) {
                    $attrsEl.text(getAttributeSummary());
                }
            }

            /**
             * Show the sticky bar
             */
            function showSticky() {
                $('body').removeClass('om-satc-pending');
                self.variationSelected = true;
            }

            /**
             * Hide the sticky bar
             */
            function hideSticky() {
                $('body').addClass('om-satc-pending');
                self.variationSelected = false;
            }

            // Initial check
            if (hasChosenVariation()) {
                updateAttributesDisplay();
                syncPriceAndImage();
                showSticky();
            } else {
                hideSticky();
            }

            // Listen for variation events
            $form.on('found_variation', function(event, variation) {
                updateAttributesDisplay();
                syncPriceAndImage();
                showSticky();

                // Enable the button
                self.$wrapper.find('.single_add_to_cart_button')
                    .removeClass('disabled wc-variation-selection-needed');

                // Store variation data for sticky bar form
                self.$wrapper.find('input.variation_id').val(variation.variation_id);
            });

            $form.on('reset_data hide_variation', function() {
                updateAttributesDisplay();
                hideSticky();
            });

            // Handle scroll-to-form button
            self.$wrapper.on('click', '.om-satc-scroll-to-form', function(e) {
                e.preventDefault();

                $('html, body').animate({
                    scrollTop: $form.offset().top - 100
                }, 400);

                // Optionally focus on first select
                $form.find('select').first().focus();
            });
        },

        /**
         * Initialize variation dropdown for products with MANY variations
         * (more than the configured threshold). Picking an option in the
         * sticky-bar select syncs the host page's variations_form so the
         * Add-to-cart button enables and the right variation_id is sent.
         */
        initVariationDropdown: function() {
            var self      = this;
            var $select   = this.$wrapper.find('.om-satc-variation-select');
            var $form     = this.$wrapper.find('form.om-satc-variable-form');
            var $vidInput = $form.find('input.variation_id');
            var $btn      = $form.find('button.single_add_to_cart_button');
            var $mainForm = $('form.variations_form').first();

            if (!$select.length) { return; }

            $select.on('change', function () {
                var vid   = parseInt($(this).val(), 10) || 0;
                var attrs = {};
                try {
                    attrs = JSON.parse($(this).find('option:selected').attr('data-attributes') || '{}');
                } catch (e) { attrs = {}; }

                $vidInput.val(vid);

                // Sync the host page's variations_form so the price/stock/image
                // update and the host's add-to-cart button gets enabled.
                if ($mainForm.length) {
                    $.each(attrs, function (key, val) {
                        $mainForm.find('select[name="' + key + '"]').val(val).trigger('change');
                    });
                }

                if (vid > 0) {
                    $btn.removeClass('disabled wc-variation-selection-needed').prop('disabled', false);
                    self.$wrapper.addClass('om-satc-variation-selected');
                } else {
                    $btn.addClass('disabled wc-variation-selection-needed').prop('disabled', true);
                    self.$wrapper.removeClass('om-satc-variation-selected');
                }
            });
        },

        /**
         * Initialize inline swatches for products with few variations
         * Shows variation images directly in the sticky bar that can be clicked
         */
        initInlineSwatches: function() {
            var self = this;
            var $swatches = this.$wrapper.find('.om-satc-inline-swatches');
            var $mainForm = $('form.variations_form');

            if (!$swatches.length) {
                return;
            }

            // Mark as having inline swatches
            this.$wrapper.addClass('om-satc-has-inline-swatches');

            /**
             * Select a swatch and update the form
             */
            function selectSwatch($swatch) {
                // Remove selection from other swatches
                $swatches.find('.om-satc-swatch-item').removeClass('selected');

                // Mark this swatch as selected
                $swatch.addClass('selected');

                // Check if this is a color swatch (has data-attribute/data-value) or image swatch (has data-variation)
                var isColorSwatch = $swatch.hasClass('om-satc-swatch-color');
                var variationId = $swatch.data('variation-id');
                var variationData = $swatch.data('variation');

                // For color swatches, sync with main form using attribute value
                if (isColorSwatch && $mainForm.length) {
                    var attribute = $swatch.data('attribute');
                    var value = $swatch.data('value');

                    if (attribute && value) {
                        // Find the attribute in the main form (could be select or OM swatches)
                        var $select = $mainForm.find('select[name="attribute_' + attribute.replace('pa_', '') + '"], select[name="attribute_' + attribute + '"]');
                        if ($select.length) {
                            $select.val(value).trigger('change');
                        }

                        // Also try clicking OM Product Swatches in main form
                        var $omSwatch = $mainForm.find('.om-variation-item[value="' + value + '"]');
                        if ($omSwatch.length) {
                            $omSwatch.trigger('click');
                        }
                    }
                }

                // For image swatches with full variation data
                if (variationData) {
                    // Update the sticky bar form
                    self.$wrapper.find('input.variation_id').val(variationData.variation_id);

                    // Enable the add to cart button
                    self.$wrapper.find('.single_add_to_cart_button')
                        .removeClass('disabled wc-variation-selection-needed');

                    // Update price display if variation has price
                    if (variationData.price_html) {
                        self.$wrapper.find('.om-satc-price').html(variationData.price_html);
                    }

                    // Update image in sticky bar
                    if (variationData.image && variationData.image.thumb_src) {
                        self.$wrapper.find('.om-satc-image img').attr('src', variationData.image.thumb_src);
                    }

                    // Also update the main form to stay in sync
                    if ($mainForm.length && variationData.attributes) {
                        // Update each attribute select
                        for (var attrName in variationData.attributes) {
                            if (variationData.attributes.hasOwnProperty(attrName)) {
                                var $select = $mainForm.find('select[name="' + attrName + '"]');
                                if ($select.length) {
                                    $select.val(variationData.attributes[attrName]).trigger('change');
                                }
                            }
                        }
                    }
                } else if (variationId) {
                    // Just have variation ID (color swatches)
                    self.$wrapper.find('input.variation_id').val(variationId);
                    self.$wrapper.find('.single_add_to_cart_button')
                        .removeClass('disabled wc-variation-selection-needed');
                }

                // Mark wrapper as having selection
                self.$wrapper.addClass('om-satc-variation-selected');

                // Update attributes display
                var label = $swatch.attr('title');
                if (label) {
                    var $attrsEl = self.$wrapper.find('.om-satc-attrs');
                    if ($attrsEl.length) {
                        $attrsEl.text(label);
                    }
                }
            }

            // Bind click on swatches
            $swatches.on('click', '.om-satc-swatch-item', function(e) {
                e.preventDefault();
                e.stopPropagation();
                selectSwatch($(this));
            });

            // Listen for variation changes from main form and sync
            if ($mainForm.length) {
                $mainForm.on('found_variation', function(event, variation) {
                    // Find matching swatch and select it
                    var matched = false;

                    // First try by variation_id
                    $swatches.find('.om-satc-swatch-item').each(function() {
                        var $swatch = $(this);
                        if ($swatch.data('variation-id') == variation.variation_id) {
                            $swatches.find('.om-satc-swatch-item').removeClass('selected');
                            $swatch.addClass('selected');
                            matched = true;
                            return false;
                        }
                    });

                    // If no match by variation_id, try matching by attribute value (for color swatches)
                    if (!matched && variation.attributes) {
                        $swatches.find('.om-satc-swatch-item.om-satc-swatch-color').each(function() {
                            var $swatch = $(this);
                            var swatchAttr = $swatch.data('attribute');
                            var swatchValue = $swatch.data('value');

                            // Check if this swatch's attribute value matches the variation
                            for (var attrName in variation.attributes) {
                                if (attrName.indexOf(swatchAttr) !== -1 || attrName.indexOf(swatchAttr.replace('pa_', '')) !== -1) {
                                    if (variation.attributes[attrName] === swatchValue) {
                                        $swatches.find('.om-satc-swatch-item').removeClass('selected');
                                        $swatch.addClass('selected');
                                        matched = true;
                                        return false;
                                    }
                                }
                            }
                        });
                    }

                    // Update sticky bar
                    self.$wrapper.find('input.variation_id').val(variation.variation_id);
                    self.$wrapper.find('.single_add_to_cart_button')
                        .removeClass('disabled wc-variation-selection-needed');
                    self.$wrapper.addClass('om-satc-variation-selected');
                    // For `many_vars_mode = swatches`, PHP adds
                    // `om-satc-pending` to hide the bar until a variation
                    // is picked. Clear it on first selection so the bar
                    // becomes visible from the main form OR the bar itself.
                    $('body').removeClass('om-satc-pending');

                    // Update price
                    if (variation.price_html) {
                        self.$wrapper.find('.om-satc-price').html(variation.price_html);
                    }

                    // Update attributes display
                    if (matched) {
                        var $selectedSwatch = $swatches.find('.om-satc-swatch-item.selected');
                        var label = $selectedSwatch.attr('title');
                        if (label) {
                            var $attrsEl = self.$wrapper.find('.om-satc-attrs');
                            if ($attrsEl.length) {
                                $attrsEl.text(label);
                            }
                        }
                    }
                });

                $mainForm.on('reset_data', function() {
                    $swatches.find('.om-satc-swatch-item').removeClass('selected');
                    self.$wrapper.find('input.variation_id').val('');
                    self.$wrapper.find('.single_add_to_cart_button')
                        .addClass('disabled wc-variation-selection-needed');
                    self.$wrapper.removeClass('om-satc-variation-selected');
                    self.$wrapper.find('.om-satc-attrs').text('');
                });

                // Check if a variation is already selected on page load
                var initialVariationId = $mainForm.find('input.variation_id').val();
                if (initialVariationId && initialVariationId !== '0') {
                    $swatches.find('.om-satc-swatch-item').each(function() {
                        var $swatch = $(this);
                        if ($swatch.data('variation-id') == initialVariationId) {
                            $swatch.addClass('selected');
                            self.$wrapper.find('input.variation_id').val(initialVariationId);
                            self.$wrapper.find('.single_add_to_cart_button')
                                .removeClass('disabled wc-variation-selection-needed');
                            self.$wrapper.addClass('om-satc-variation-selected');
                            return false;
                        }
                    });
                }
            }

            // Select first swatch by default if no initial selection
            if (!$swatches.find('.om-satc-swatch-item.selected').length) {
                var $firstSwatch = $swatches.find('.om-satc-swatch-item').first();
                if ($firstSwatch.length) {
                    selectSwatch($firstSwatch);
                }
            }
        },

        /**
         * Initialize form synchronization for regular products
         * For variable products, we sync the variation selection from the main form
         * (which may have swatches, images, etc.) - we don't show our own selectors.
         */
        initFormSync: function() {
            var self = this;

            if (!this.$mainForm.length) {
                return;
            }

            // For variable products, sync the variation selection from main form
            if (this.$mainForm.hasClass('variations_form')) {

                // Listen for variation found on main form
                this.$mainForm.on('found_variation', function(event, variation) {
                    // Update sticky bar variation_id
                    self.$wrapper.find('input.variation_id').val(variation.variation_id);

                    // Enable button
                    self.$wrapper.find('.single_add_to_cart_button')
                        .removeClass('disabled wc-variation-selection-needed');

                    // Update price if visible
                    var $priceEl = self.$mainForm.find('.single_variation .price');
                    var $stickyPrice = self.$wrapper.find('.om-satc-price');
                    if ($priceEl.length && $stickyPrice.length) {
                        $stickyPrice.html($priceEl.html());
                    }

                    // Update selected attributes display
                    self.updateSelectedAttrsDisplay(variation.attributes);
                    self.$wrapper.addClass('om-satc-variation-selected');
                });

                // Handle variation reset
                this.$mainForm.on('reset_data', function() {
                    self.$wrapper.find('input.variation_id').val('');
                    self.$wrapper.find('.single_add_to_cart_button')
                        .addClass('disabled wc-variation-selection-needed');
                    self.$wrapper.find('.om-satc-attrs').empty();
                    // Remove variation-selected class to show prompt again
                    self.$wrapper.removeClass('om-satc-variation-selected');
                });

                // Check if variation is already selected on page load
                var initialVariationId = this.$mainForm.find('input.variation_id').val();
                if (initialVariationId && initialVariationId !== '0') {
                    self.$wrapper.find('input.variation_id').val(initialVariationId);
                    self.$wrapper.find('.single_add_to_cart_button')
                        .removeClass('disabled wc-variation-selection-needed');

                    // Get the selected attributes and update display
                    var selectedAttrs = {};
                    self.$mainForm.find('select[name^="attribute_"]').each(function() {
                        var name = $(this).attr('name');
                        var value = $(this).val();
                        if (value) {
                            selectedAttrs[name] = value;
                        }
                    });
                    if (Object.keys(selectedAttrs).length > 0) {
                        self.updateSelectedAttrsDisplay(selectedAttrs);
                        self.$wrapper.addClass('om-satc-variation-selected');
                    }
                }
            }
        },

        /**
         * Update the selected attributes display text
         */
        updateSelectedAttrsDisplay: function(attributes) {
            var $attrsEl = this.$wrapper.find('.om-satc-attrs');
            if (!$attrsEl.length || !attributes) {
                return;
            }

            var attrsText = [];
            for (var key in attributes) {
                if (attributes.hasOwnProperty(key) && attributes[key]) {
                    // Remove 'attribute_' prefix and format nicely
                    var label = key.replace('attribute_', '').replace(/pa_/g, '').replace(/-/g, ' ');
                    attrsText.push(attributes[key]);
                }
            }

            if (attrsText.length) {
                $attrsEl.text(attrsText.join(' - '));
            } else {
                $attrsEl.empty();
            }
        },

        /**
         * Initialize multi-select mode (OM Product Swatches integration)
         * Shows all selected variations in the sticky bar
         */
        initMultiSelectMode: function() {
            var self = this;

            // Add multi-select class to wrapper
            this.$wrapper.addClass('om-satc-multi-select-mode');

            // Replace the form content with multi-select display
            this.setupMultiSelectDisplay();

            // Poll for changes in the swatches handler
            // We use polling because the swatches plugin doesn't emit custom events we can listen to
            this.multiSelectPollInterval = setInterval(function() {
                self.syncMultiSelectState();
            }, 300);

            // Also listen for clicks on swatches to trigger immediate update
            $(document).on('click.omStickyMultiSelect', '.om-variations-wrapper[data-multi-select="1"] .om-variation-item', function() {
                // Small delay to let the swatches handler update first
                setTimeout(function() {
                    self.syncMultiSelectState();
                }, 50);
            });

            // Listen for batch add to cart success
            $(document).on('om.multiselect.added', function() {
                self.syncMultiSelectState();
            });
        },

        /**
         * Setup the multi-select display in the sticky bar
         */
        setupMultiSelectDisplay: function() {
            var $form = this.$wrapper.find('.om-satc-form');
            var i18n = this.config.i18n || {};
            var selectedText = i18n.selected || 'selected';
            var addToCartText = i18n.addSelectedToCart || 'Add Selected to Cart';

            // Clear existing form content and replace with multi-select summary
            $form.html(
                '<div class="om-satc-multi-select-summary">' +
                    '<span class="om-satc-multi-count">0 ' + selectedText + '</span>' +
                    '<span class="om-satc-multi-names"></span>' +
                    '<span class="om-satc-multi-subtotal"></span>' +
                '</div>' +
                '<button type="button" class="om-satc-multi-add-btn single_add_to_cart_button button alt disabled' +
                    (typeof wc_wp_theme_get_element_class_name === 'function' ? ' ' + wc_wp_theme_get_element_class_name('button') : '') + '">' +
                    addToCartText +
                '</button>'
            );

            // Bind the add button
            var self = this;
            this.$wrapper.on('click', '.om-satc-multi-add-btn', function(e) {
                e.preventDefault();
                self.triggerMultiSelectAddToCart();
            });
        },

        /**
         * Sync with the OM Product Swatches multi-select handler
         */
        syncMultiSelectState: function() {
            // Access the swatches plugin's multi-select handler
            if (typeof omProductSwatches === 'undefined' || !omProductSwatches.multiSelectHandler) {
                return;
            }

            var handler = omProductSwatches.multiSelectHandler;
            var selectedVariations = handler.selectedVariations || [];
            var count = selectedVariations.length;

            var $summary = this.$wrapper.find('.om-satc-multi-select-summary');
            var $count = $summary.find('.om-satc-multi-count');
            var $names = $summary.find('.om-satc-multi-names');
            var $subtotal = $summary.find('.om-satc-multi-subtotal');
            var $btn = this.$wrapper.find('.om-satc-multi-add-btn');

            var i18n = this.config.i18n || {};
            var selectedText = i18n.selected || 'selected';
            var subtotalText = i18n.subtotal || 'Subtotal';

            // Update count
            $count.text(count + ' ' + selectedText);

            if (count === 0) {
                $names.empty();
                $subtotal.empty();
                $btn.addClass('disabled').prop('disabled', true);
                this.$wrapper.removeClass('om-satc-has-selections');
                return;
            }

            // Get variation names
            var names = selectedVariations.map(function(item) {
                return item.label || '';
            }).filter(function(name) {
                return name.length > 0;
            });

            // Display names (truncate if too many)
            if (names.length > 3) {
                $names.text(names.slice(0, 3).join(', ') + '...');
            } else {
                $names.text(names.join(', '));
            }

            // Calculate subtotal
            var subtotal = 0;
            var $mainForm = $('form.variations_form');
            var variations = $mainForm.data('product_variations');

            if (variations && Array.isArray(variations)) {
                selectedVariations.forEach(function(item) {
                    for (var i = 0; i < variations.length; i++) {
                        if (variations[i].variation_id === item.variation_id) {
                            subtotal += parseFloat(variations[i].display_price) * (item.quantity || 1);
                            break;
                        }
                    }
                });
            }

            // Format subtotal
            if (subtotal > 0) {
                var formattedSubtotal = this.formatPrice(subtotal);
                $subtotal.html(subtotalText + ': <strong>' + formattedSubtotal + '</strong>');
            }

            // Enable button
            $btn.removeClass('disabled').prop('disabled', false);
            this.$wrapper.addClass('om-satc-has-selections');
        },

        /**
         * Format price with currency symbol
         */
        formatPrice: function(price) {
            var currencySymbol = '€';
            var decimalSeparator = ',';
            var thousandSeparator = '.';
            var decimals = 2;

            // Try to get from WooCommerce settings
            if (typeof wc_add_to_cart_params !== 'undefined' && wc_add_to_cart_params.currency_symbol) {
                currencySymbol = wc_add_to_cart_params.currency_symbol;
            } else if (typeof woocommerce_params !== 'undefined' && woocommerce_params.currency_symbol) {
                currencySymbol = woocommerce_params.currency_symbol;
            }

            // Format number
            var formattedNumber = price.toFixed(decimals).replace('.', decimalSeparator);

            // Add thousand separator
            var parts = formattedNumber.split(decimalSeparator);
            parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator);
            formattedNumber = parts.join(decimalSeparator);

            // Euro typically goes after with space
            if (currencySymbol === '€') {
                return formattedNumber + ' ' + currencySymbol;
            }

            return currencySymbol + formattedNumber;
        },

        /**
         * Trigger the swatches plugin's batch add to cart
         */
        triggerMultiSelectAddToCart: function() {
            if (typeof omProductSwatches === 'undefined' || !omProductSwatches.multiSelectHandler) {
                return;
            }

            var handler = omProductSwatches.multiSelectHandler;

            if (!handler.selectedVariations || !handler.selectedVariations.length) {
                return;
            }

            // Show loading state on our button
            var $btn = this.$wrapper.find('.om-satc-multi-add-btn');
            $btn.addClass('loading').prop('disabled', true);

            // Call the swatches handler's batch add to cart
            handler.batchAddToCart();

            // Reset our button after a delay (the swatches handler will handle the actual response)
            setTimeout(function() {
                $btn.removeClass('loading');
            }, 3000);
        },

        /**
         * Bind scroll handler for showing/hiding
         */
        bindScrollHandler: function() {
            var self = this;
            var ticking = false;

            $(window).on('scroll.omStickyATC', function() {
                if (!ticking) {
                    window.requestAnimationFrame(function() {
                        self.handleScroll();
                        ticking = false;
                    });
                    ticking = true;
                }
            });

            // Initial check
            this.handleScroll();
        },

        /**
         * Handle scroll events
         */
        handleScroll: function() {
            var scrollTop = $(window).scrollTop();

            // For many-vars products, don't show until variation is selected
            // But always allow if in multi-select mode (it has its own UI)
            var isMultiSelectMode = $('body').hasClass('om-multi-select-enabled') || this.$wrapper.hasClass('om-satc-multi-select-mode');
            if (!isMultiSelectMode && $('body').hasClass('om-satc-many-vars') && $('body').hasClass('om-satc-pending')) {
                this.hide();
                this.lastScrollTop = scrollTop;
                return;
            }

            // Effective threshold is the greater of the admin-set fixed
            // scroll distance and the dynamic "past the main ATC button"
            // offset. On product pages we prefer the dynamic one so the
            // bar only appears once the shopper has actually scrolled past
            // the primary CTA — no duplicate Add-to-cart on screen.
            var effectiveThreshold = Math.max(this.scrollThreshold, this.dynamicThreshold || 0);

            // Check if past threshold
            if (scrollTop >= effectiveThreshold) {
                // Check scroll direction for hide-on-scroll-up
                if (this.config.scrollHide) {
                    if (scrollTop > this.lastScrollTop) {
                        // Scrolling down - show
                        this.show();
                    } else {
                        // Scrolling up - hide
                        this.hide();
                    }
                } else {
                    // No scroll-hide, just show
                    this.show();
                }
            } else {
                // Before threshold - hide
                this.hide();
            }

            this.lastScrollTop = scrollTop;
        },

        /**
         * Show the sticky bar
         */
        show: function() {
            if (this.isVisible) {
                return;
            }

            this.$wrapper.addClass('om-satc-visible').removeClass('om-satc-hidden');
            this.isVisible = true;
        },

        /**
         * Hide the sticky bar
         */
        hide: function() {
            if (!this.isVisible) {
                return;
            }

            this.$wrapper.removeClass('om-satc-visible').addClass('om-satc-hidden');
            this.isVisible = false;
        },

        /**
         * Bind add to cart functionality
         */
        bindAddToCart: function() {
            var self = this;

            // For variable products with many variations, clicking button should trigger main form
            this.$wrapper.on('click', '.single_add_to_cart_button', function(e) {
                var $btn = $(this);

                // Check if this is a scroll-to-form button
                if ($btn.hasClass('om-satc-scroll-to-form')) {
                    return; // Handled elsewhere
                }

                // Multi-select mode (AIEO swatches grid where the
                // shopper picks several variations with per-variation
                // quantities, then commits with one button) takes
                // priority over the regular single-variation path.
                //
                // Detection uses the SERVER-RENDERED
                // `[data-multi-select="1"]` wrapper attribute, not the
                // `om-multi-select-enabled` class on the form (which is
                // added by `omProductSwatches.multiSelectHandler.init`
                // at runtime — could race the click handler's bind).
                // The dynamic button `.om-multi-select-add-to-cart` is
                // appended into the main form after init, so we
                // re-resolve `$mainForm` fresh and look the button up
                // each click rather than caching a stale reference.
                if (window.console && console.log) {
                    console.log('[AIEO sticky-ATC] click captured', {
                        button: this,
                        wrapper: $('.om-variations-wrapper[data-multi-select="1"]').length,
                        multiBtn: $('.om-multi-select-add-to-cart').length,
                        manyVars: $('body').hasClass('om-satc-many-vars')
                    });
                }
                var $multiSelectWrap = $('.om-variations-wrapper[data-multi-select="1"]').first();

                // On a multi-select product, prefer the in-page grid's
                // batch flow — but ONLY if the shopper has actually
                // picked something there. Otherwise fall through to
                // the regular sticky bar paths so a variation chosen
                // in the sticky's OWN dropdown still works.
                var multiHandler = (typeof omProductSwatches !== 'undefined' && omProductSwatches.multiSelectHandler) || null;
                var multiHasPicks = multiHandler
                    && multiHandler.selectedVariations
                    && multiHandler.selectedVariations.length > 0;

                if ($multiSelectWrap.length && multiHasPicks) {
                    e.preventDefault();
                    e.stopPropagation();

                    // Prefer calling the handler's syncCart() directly
                    // — `.trigger('click')` on a dynamically created
                    // jQuery button can miss handlers attached via
                    // `.on()` if event delegation timing is off, and
                    // the syncCart path is the canonical one (POSTs
                    // every selected variation + qty to wc-ajax in one
                    // batch).
                    var handler = (typeof omProductSwatches !== 'undefined' && omProductSwatches.multiSelectHandler) || null;

                    var hasSelections = handler
                        && handler.selectedVariations
                        && handler.selectedVariations.length > 0;

                    // Also check the dom button as a fallback signal
                    // — when the handler hasn't initialised yet but
                    // the wrapper exists, fall through to scroll.
                    var $multiBtn = $('.om-multi-select-add-to-cart').first();

                    // Pick the ADD-semantic batchAddToCart over the
                    // SYNC-semantic syncCart. The in-page "Update cart"
                    // button intentionally syncs (so deselected items
                    // get removed); the sticky bar is invoked when
                    // the shopper has scrolled past the grid and
                    // expects "add what I picked" — not "make my cart
                    // match the picker exactly". Routing through
                    // batchAddToCart preserves cart contents and just
                    // adds (or increments) the picked variations.
                    if (!handler || typeof handler.batchAddToCart !== 'function') {
                        if (window.console && console.warn) {
                            console.warn('[AIEO sticky-ATC] multiSelectHandler.batchAddToCart unavailable, scrolling to grid');
                        }
                        $('html, body').animate({ scrollTop: $multiSelectWrap.offset().top - 100 }, 400);
                        return false;
                    }

                    if (!hasSelections) {
                        // Telemetry: tell us WHY we're bailing. The
                        // multi-select handler's `selectedVariations`
                        // is the source of truth — if it's empty here
                        // even though the shopper clicked swatches, it
                        // means clicks aren't registering on the
                        // singleton (different instance, init not
                        // run, etc.).
                        if (window.console && console.warn) {
                            console.warn('[AIEO sticky-ATC] no selections — scrolling to grid', {
                                selectedVariations: handler.selectedVariations,
                                handlerForm: handler.$form && handler.$form.length ? handler.$form.attr('class') : '(no $form)',
                                multiBtnText: $('.om-multi-select-add-to-cart').first().text(),
                                domSelected: $('.om-variation-item.om-multi-selected, .om-variation-item.selected').length,
                                anyOmSwatchSelected: $('.om-variations-wrapper[data-multi-select="1"] .om-variation-item').filter(function(){
                                    return /select|active/i.test($(this).attr('class'));
                                }).map(function(){ return $(this).attr('class'); }).get().slice(0, 5)
                            });
                        }
                        $('html, body').animate({ scrollTop: $multiSelectWrap.offset().top - 100 }, 400);
                        return false;
                    }

                    $btn.addClass('loading disabled');
                    try {
                        if (window.console && console.log) {
                            console.log('[AIEO sticky-ATC] calling batchAddToCart with selections:',
                                (handler.selectedVariations || []).map(function(v){ return { id: v.variation_id, qty: v.quantity }; })
                            );
                        }
                        var ajaxLogger = function (event, xhr, settings) {
                            if (settings && settings.data && /aieo_swatch_(sync_cart|batch_add_to_cart)/.test(String(settings.data))) {
                                try {
                                    console.log('[AIEO sticky-ATC] AJAX response:', xhr.status, (xhr.responseText || '').slice(0, 800));
                                } catch (_e) {}
                                $(document).off('ajaxComplete', ajaxLogger);
                            }
                        };
                        $(document).on('ajaxComplete', ajaxLogger);
                        setTimeout(function(){ $(document).off('ajaxComplete', ajaxLogger); }, 8000);

                        handler.batchAddToCart();
                    } catch (err) {
                        if (window.console && console.error) {
                            console.error('[AIEO sticky-ATC] batchAddToCart threw:', err);
                        }
                    }
                    setTimeout(function () { $btn.removeClass('loading disabled'); }, 2000);
                    return false;
                }

                // Many-vars products (single-variation path) — the
                // sticky bar has its own variations_form with the
                // shopper's pick already in its `<input name="variation_id">`
                // and the WC required fields (`add-to-cart`,
                // `product_id`, `aieo_satc_source`). AJAX-post that
                // form straight to `wc-ajax=add_to_cart` instead of
                // playing form-mirror games with the main form. WC's
                // wc-ajax handler accepts the same payload shape its
                // own variations form submits, so this is a clean
                // standalone add — no dependency on the main form's
                // wc_variation_form state.
                if ($('body').hasClass('om-satc-many-vars')) {
                    e.preventDefault();
                    e.stopPropagation();

                    var $stickyForm = self.$wrapper.find('form.om-satc-variable-form').first();
                    var stickyVid   = parseInt($stickyForm.find('input.variation_id').val(), 10) || 0;
                    if (!stickyVid) {
                        if (self.$mainForm.length) {
                            $('html, body').animate({
                                scrollTop: self.$mainForm.offset().top - 100
                            }, 400);
                        }
                        return false;
                    }

                    // Route through the AIEO swatches' canonical
                    // `aieo_swatch_quick_add_to_cart` admin-ajax
                    // endpoint (see
                    // ai-eshop-optimizer-runtime/.../class-aieo-dmm-swatches-ajax.php
                    // line 136). Unlike WC core's `wc-ajax=add_to_cart`
                    // which is built around simple-product semantics,
                    // this endpoint takes `variation_id` + `quantity`,
                    // validates purchasability + stock + the nonce,
                    // and returns a clear error message via wp_send_json_error
                    // when something's wrong. The endpoint reads from
                    // `omSwatchSettings.security` (registered by the
                    // swatches runtime). Falls back to the wc-ajax
                    // path with the merged attribute payload if the
                    // swatches settings aren't on the page.
                    $btn.addClass('loading disabled');

                    var hasSwatchSettings = (typeof omSwatchSettings !== 'undefined') && omSwatchSettings && omSwatchSettings.ajax_url && omSwatchSettings.security;
                    if (hasSwatchSettings) {
                        $.ajax({
                            url: omSwatchSettings.ajax_url,
                            type: 'POST',
                            data: {
                                action:       'aieo_swatch_quick_add_to_cart',
                                security:     omSwatchSettings.security,
                                variation_id: stickyVid,
                                quantity:     1
                            },
                            dataType: 'json'
                        })
                        .done(function (resp) {
                            if (window.console && console.log) {
                                console.log('[AIEO sticky-ATC] quick-add response:', resp);
                            }
                            if (!resp || !resp.success) {
                                var msg = (resp && resp.data && resp.data.message) || '(no message)';
                                if (window.console && console.warn) {
                                    console.warn('[AIEO sticky-ATC] quick-add rejected:', msg);
                                }
                                return;
                            }
                            if (resp.data && resp.data.fragments) {
                                $.each(resp.data.fragments, function (sel, html) {
                                    $(sel).replaceWith(html);
                                });
                            }
                            $(document.body).trigger('added_to_cart', [
                                resp.data && resp.data.fragments,
                                resp.data && resp.data.cart_hash,
                                $btn
                            ]);
                        })
                        .fail(function (xhr, status, err) {
                            if (window.console && console.error) {
                                console.error('[AIEO sticky-ATC] quick-add failed', status, err, (xhr && xhr.responseText || '').slice(0, 500));
                            }
                        })
                        .always(function () {
                            $btn.removeClass('loading disabled');
                        });
                        return false;
                    }

                    // Fallback (no swatches runtime on page) — POST
                    // the sticky form to wc-ajax=add_to_cart with the
                    // attribute pairs merged in. Same path that
                    // worked before omSwatchSettings was a hard
                    // dependency.
                    var ajaxUrl = (typeof wc_add_to_cart_params !== 'undefined' && wc_add_to_cart_params.wc_ajax_url)
                        ? wc_add_to_cart_params.wc_ajax_url.replace('%%endpoint%%', 'add_to_cart')
                        : '/?wc-ajax=add_to_cart';
                    var payload = $stickyForm.serialize();
                    var $picked = $stickyForm.find('select.om-satc-variation-select option:selected').first();
                    var attrsRaw = $picked.attr('data-attributes') || '';
                    if (attrsRaw) {
                        try {
                            var attrs = JSON.parse(attrsRaw);
                            $.each(attrs, function (k, v) {
                                payload += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(v);
                            });
                        } catch (_e) {}
                    }
                    $.ajax({ url: ajaxUrl, type: 'POST', data: payload, dataType: 'json' })
                        .done(function (resp) {
                            if (resp && !resp.error && resp.fragments) {
                                $.each(resp.fragments, function (sel, html) { $(sel).replaceWith(html); });
                                $(document.body).trigger('added_to_cart', [resp.fragments, resp.cart_hash, $btn]);
                            } else if (window.console && console.warn) {
                                console.warn('[AIEO sticky-ATC] wc-ajax fallback rejected:', resp);
                            }
                        })
                        .always(function () { $btn.removeClass('loading disabled'); });
                    return false;
                }

                // For other products, let the form handle it normally
                // but show loading state
                $btn.addClass('loading');
            });

            // Listen for WooCommerce AJAX add to cart events
            $(document.body).on('added_to_cart', function(event, fragments, cart_hash, $button) {
                self.$wrapper.find('.single_add_to_cart_button').removeClass('loading disabled');

                // Clear persistent storage when product is added to cart
                // so it won't show on other pages anymore
                if (self.config.persistentEnabled) {
                    self.clearStoredProduct();
                }
            });

            $(document.body).on('wc_cart_button_updated', function() {
                self.$wrapper.find('.single_add_to_cart_button').removeClass('loading disabled');
            });
        },

        /**
         * Bind dismiss button for persistent mode
         */
        bindDismissButton: function() {
            var self = this;

            this.$wrapper.on('click', '.om-satc-dismiss', function(e) {
                e.preventDefault();

                // Clear stored product
                self.clearStoredProduct();

                // Hide the bar
                self.$wrapper.addClass('om-satc-animate-out');
                setTimeout(function() {
                    self.$wrapper.remove();
                }, 300);
            });
        },

        /**
         * Initialize quantity +/- buttons in sticky bar
         * Standalone implementation that works without Botiga Pro
         */
        initQuantityButtons: function() {
            var self = this;

            // Find all quantity wrappers in the sticky bar
            this.$wrapper.find('.quantity').each(function() {
                var $wrapper = $(this);

                // Skip if already initialized
                if ($wrapper.data('om-qty-init')) {
                    return;
                }

                var $minus = $wrapper.find('.botiga-quantity-minus');
                var $plus = $wrapper.find('.botiga-quantity-plus');
                var $input = $wrapper.find('.qty, input[type="number"]');

                if (!$input.length) {
                    return;
                }

                // Mark as initialized
                $wrapper.data('om-qty-init', true);

                // Force show the buttons (they may be hidden by default)
                $minus.addClass('show');
                $plus.addClass('show');

                // Get min/max/step values
                var min = parseFloat($input.attr('min')) || 1;
                var max = parseFloat($input.attr('max')) || 9999;
                var step = parseFloat($input.attr('step')) || 1;

                // Minus button click
                $minus.off('click.omStickyQty').on('click.omStickyQty', function(e) {
                    e.preventDefault();
                    e.stopPropagation();

                    var currentVal = parseFloat($input.val()) || min;
                    var newVal = currentVal - step;

                    if (newVal >= min) {
                        $input.val(newVal).trigger('change');
                    }

                    return false;
                });

                // Plus button click
                $plus.off('click.omStickyQty').on('click.omStickyQty', function(e) {
                    e.preventDefault();
                    e.stopPropagation();

                    var currentVal = parseFloat($input.val()) || min;
                    var newVal = currentVal + step;

                    if (newVal <= max || max === -1) {
                        $input.val(newVal).trigger('change');
                    }

                    return false;
                });
            });

            // Re-initialize after AJAX content updates
            $(document.body).on('updated_wc_div wc_fragments_refreshed', function() {
                self.initQuantityButtons();
            });
        },

        // =====================================================================
        // PERSISTENT MODE STORAGE
        // =====================================================================

        /**
         * Store product data for persistent mode
         */
        storeProductData: function(productData) {
            if (!this.config.persistentEnabled) {
                return;
            }

            var data = {
                product: productData,
                timestamp: Date.now()
            };

            try {
                localStorage.setItem('om_sticky_atc_product', JSON.stringify(data));
            } catch (e) {
                console.warn('OM Sticky ATC: Could not store product data', e);
            }
        },

        /**
         * Get stored product data
         */
        getStoredProduct: function() {
            try {
                var data = localStorage.getItem('om_sticky_atc_product');
                if (data) {
                    return JSON.parse(data);
                }
            } catch (e) {
                console.warn('OM Sticky ATC: Could not retrieve stored product', e);
            }
            return null;
        },

        /**
         * Clear stored product data
         */
        clearStoredProduct: function() {
            try {
                localStorage.removeItem('om_sticky_atc_product');
            } catch (e) {
                console.warn('OM Sticky ATC: Could not clear stored product', e);
            }
        },

        /**
         * Check if stored product is expired
         */
        isProductExpired: function(storedData) {
            if (!storedData || !storedData.timestamp) {
                return true;
            }

            var expireMs = (this.config.persistentExpireHours || 24) * 60 * 60 * 1000;
            var age = Date.now() - storedData.timestamp;

            return age > expireMs;
        },

        /**
         * Load persistent product content via AJAX
         */
        loadPersistentProduct: function(storedData) {
            var self = this;
            var productId = storedData.product.id;

            // Show loading placeholder
            this.$wrapper.find('.om-satc-persistent-placeholder').show();

            $.ajax({
                url: this.config.ajaxUrl,
                type: 'POST',
                data: {
                    action: 'om_sticky_atc_get_product',
                    nonce: this.config.nonce,
                    product_id: productId
                },
                success: function(response) {
                    if (response.success && response.data.html) {
                        self.$wrapper.find('.om-satc-inner').html(response.data.html);
                        self.persistentProductId = productId;

                        // Bind add to cart for the loaded content
                        self.bindPersistentAddToCart();
                    } else {
                        // Failed to load, clear storage and hide
                        self.clearStoredProduct();
                        self.$wrapper.remove();
                    }
                },
                error: function() {
                    self.clearStoredProduct();
                    self.$wrapper.remove();
                }
            });
        },

        /**
         * Bind add to cart for persistent mode
         */
        bindPersistentAddToCart: function() {
            var self = this;

            // For persistent mode, clicking add to cart should redirect to product page
            // or use AJAX add to cart for simple products
            this.$wrapper.on('click', '.single_add_to_cart_button', function(e) {
                var $btn = $(this);
                var productId = self.persistentProductId;
                var $form = $btn.closest('form');

                // Check if it's a simple product form
                if ($form.hasClass('om-satc-simple-form')) {
                    // Allow normal AJAX add to cart
                    $btn.addClass('loading');
                    return true;
                }

                // For variable products, redirect to product page
                e.preventDefault();
                var storedData = self.getStoredProduct();
                if (storedData && storedData.product && storedData.product.url) {
                    window.location.href = storedData.product.url;
                }
            });
        }
    };

    // Initialize on document ready
    $(document).ready(function() {
        OmStickyATC.init();
    });

    // Expose for external access
    window.OmStickyATC = OmStickyATC;

})(jQuery);
