/**
 * GGM Commerce - Product Variations Handler
 *
 * Handles variation selection, price updates, and gallery integration.
 *
 * @package GGM_Commerce
 */

(function($) {
    'use strict';

    /**
     * Escape a value for use in jQuery attribute selectors.
     * Values containing quotes or special characters need escaping.
     * @param {string} value - The value to escape
     * @return {string} Escaped value safe for use in selectors like [data-value="..."]
     */
    function escapeAttrSelector(value) {
        if (!value) return '';
        // Escape backslashes first, then double quotes
        return String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
    }

    /**
     * Variation Selector Class
     */
    class VariationSelector {
        /**
         * Constructor
         * @param {jQuery} $container - The variation selector container element
         */
        constructor($container) {
            this.$container = $container;
            this.productId = $container.data('product-id');
            this.variations = $container.data('variations') || [];
            this.hideOutOfStock = $container.data('hide-out-of-stock') === 1;
            this.showPriceChange = $container.data('show-price-change') === 1;
            this.currencySymbol = $container.data('currency-symbol') || '$';
            this.currencyPosition = $container.data('currency-position') || 'before';
            this.variationMode = $container.data('variation-mode') || 'combined';
            this.basePrice = parseFloat($container.data('base-price')) || 0;

            // For Independent mode: attribute options with price modifiers
            this.attributeOptions = $container.data('attribute-options') || {};

            this.selectedAttributes = {};
            this.selectedVariation = null;
            this.calculatedPrice = this.basePrice;

            this.init();
        }

        /**
         * Initialize the selector
         */
        init() {
            this.bindEvents();
            this.initializeDefaults();
            this.updateAvailability();
        }

        /**
         * Bind event handlers
         */
        bindEvents() {
            // Button/Swatch clicks
            this.$container.on('click', '.ggmc-variation-button, .ggmc-variation-swatch', (e) => {
                e.preventDefault();
                const $button = $(e.currentTarget);

                if ($button.hasClass('disabled')) {
                    return;
                }

                this.handleOptionClick($button);
            });

            // Select dropdown changes - use direct binding as well as delegation
            const selects = this.$container.find('.ggmc-variation-select');

            // Direct binding to selects
            selects.on('change', (e) => {
                const $select = $(e.currentTarget);
                this.handleSelectChange($select);
            });

            // Also keep delegated binding as fallback
            this.$container.on('change', '.ggmc-variation-select', (e) => {
                const $select = $(e.currentTarget);
                this.handleSelectChange($select);
            });

            // Clear selection button
            this.$container.on('click', '.ggmc-variation-clear', (e) => {
                e.preventDefault();
                this.clearSelection();
            });
        }

        /**
         * Initialize default selections
         */
        initializeDefaults() {
            if (this.variationMode === 'independent') {
                // Independent mode: defaults come from per-product option config
                this.$container.find('.ggmc-variation-attribute-row').each((i, row) => {
                    const $row = $(row);
                    const attrId = String($row.data('attribute-id'));

                    // Check for pre-selected button (marked by widget renderer)
                    const $selected = $row.find('.ggmc-variation-button.selected, .ggmc-variation-swatch.selected').first();
                    if ($selected.length) {
                        this.selectedAttributes[attrId] = String($selected.data('value'));
                    }

                    // Check for pre-selected dropdown
                    const $select = $row.find('.ggmc-variation-select');
                    if ($select.length && $select.val()) {
                        this.selectedAttributes[attrId] = $select.val();
                    }
                });

                // Calculate price with defaults
                if (Object.keys(this.selectedAttributes).length > 0) {
                    this.calculateIndependentPrice();
                    this.updateHiddenInputs();
                    this.updatePriceDisplay();
                    this.triggerVariationEvent();
                }
            } else {
                // Combined mode: defaults from variation marked as default
                const defaultVariation = this.variations.find(v => v.is_default);

                if (defaultVariation) {
                    // Set initial selections from default variation
                    Object.entries(defaultVariation.attributes).forEach(([attrId, value]) => {
                        this.selectedAttributes[String(attrId)] = String(value);
                    });

                    this.selectedVariation = defaultVariation;
                    this.calculatedPrice = parseFloat(defaultVariation.price) || this.basePrice;
                    this.updateHiddenInputs();
                    this.updatePriceDisplay();
                    this.triggerVariationEvent();
                } else {
                    // No default variation - check for any pre-selected values in the HTML
                    this.$container.find('.ggmc-variation-attribute-row').each((i, row) => {
                        const $row = $(row);
                        const attrId = String($row.data('attribute-id'));

                        const $selected = $row.find('.ggmc-variation-button.selected, .ggmc-variation-swatch.selected').first();
                        if ($selected.length) {
                            this.selectedAttributes[attrId] = String($selected.data('value'));
                        }

                        const $select = $row.find('.ggmc-variation-select');
                        if ($select.length && $select.val()) {
                            this.selectedAttributes[attrId] = $select.val();
                        }
                    });

                    if (Object.keys(this.selectedAttributes).length > 0) {
                        this.findMatchingVariation();
                        this.updateHiddenInputs();
                        this.updatePriceDisplay();
                        this.triggerVariationEvent();
                    }
                }
            }

            // Show clear button if there are any selections
            this.updateClearButton();
        }

        /**
         * Handle button/swatch click
         * @param {jQuery} $button - The clicked button element
         */
        handleOptionClick($button) {
            const $row = $button.closest('.ggmc-variation-attribute-row');
            const attrId = String($row.data('attribute-id')); // Ensure string for consistent key matching
            const value = String($button.data('value')); // Ensure string for consistent value matching

            // Toggle selection
            if ($button.hasClass('selected')) {
                // Deselect
                $button.removeClass('selected');
                delete this.selectedAttributes[attrId];
            } else {
                // Select new value
                $row.find('.ggmc-variation-button, .ggmc-variation-swatch').removeClass('selected');
                $button.addClass('selected');
                this.selectedAttributes[attrId] = value;
            }

            this.updateSelectedValueDisplay($row, value);
            this.onSelectionChange();
        }

        /**
         * Handle select dropdown change
         * @param {jQuery} $select - The changed select element
         */
        handleSelectChange($select) {
            const $row = $select.closest('.ggmc-variation-attribute-row');
            // Get attribute ID from select element first, fall back to row
            let attrId = $select.data('attribute-id');
            if (!attrId) {
                attrId = $row.data('attribute-id');
            }
            attrId = String(attrId); // Ensure string for consistent key matching
            const value = $select.val(); // Already a string from select element

            if (value) {
                this.selectedAttributes[attrId] = value;
            } else {
                delete this.selectedAttributes[attrId];
            }

            this.updateSelectedValueDisplay($row, value);
            this.onSelectionChange();
        }

        /**
         * Update the displayed selected value label
         * @param {jQuery} $row - The attribute row
         * @param {string} value - The selected value
         */
        updateSelectedValueDisplay($row, value) {
            const $valueDisplay = $row.find('.ggmc-variation-selected-value');
            if ($valueDisplay.length) {
                // Escape value for use in attribute selectors (handles quotes like 32")
                const escapedValue = escapeAttrSelector(value);

                // Find label for this value
                const $option = $row.find(`[data-value="${escapedValue}"]`);
                let label = value;

                if ($option.attr('title')) {
                    label = $option.attr('title');
                } else if ($option.text().trim()) {
                    label = $option.text().trim();
                }

                // Check select options too
                const $selectOption = $row.find(`.ggmc-variation-select option[value="${escapedValue}"]`);
                if ($selectOption.length) {
                    label = $selectOption.text().trim();
                }

                $valueDisplay.text(value ? label : '');
            }
        }

        /**
         * Handle selection change - find matching variation and update UI
         */
        onSelectionChange() {
            if (this.variationMode === 'independent') {
                this.calculateIndependentPrice();
            } else {
                this.findMatchingVariation();
            }

            this.updateAvailability();
            this.updatePriceDisplay();
            this.updateHiddenInputs();
            this.triggerVariationEvent();
            this.updateClearButton();
        }

        /**
         * Calculate price for independent mode.
         * In independent mode, there are no pre-generated variations.
         * Price = base_price + sum(selected option modifiers).
         */
        calculateIndependentPrice() {
            let total = this.basePrice;

            // Add modifiers for each selected attribute
            Object.entries(this.selectedAttributes).forEach(([attrId, value]) => {
                const attrOptions = this.attributeOptions[attrId];
                if (attrOptions) {
                    const option = attrOptions.find(opt => opt.value === value);
                    if (option && option.price_modifier) {
                        total += parseFloat(option.price_modifier);
                    }
                }
            });

            this.calculatedPrice = total;

            // For Independent mode, create a "virtual" variation object
            // so the rest of the code works consistently
            const totalAttributes = this.$container.find('.ggmc-variation-attribute-row').length;
            const selectedCount = Object.keys(this.selectedAttributes).length;

            if (selectedCount === totalAttributes) {
                // All attributes selected - create virtual variation
                this.selectedVariation = {
                    id: null, // No actual variation ID
                    price: total,
                    stock_status: 'instock', // Uses parent stock
                    available: true,
                    attributes: { ...this.selectedAttributes }
                };
            } else {
                this.selectedVariation = null;
            }
        }

        /**
         * Find variation matching current selection
         */
        findMatchingVariation() {
            const selectedCount = Object.keys(this.selectedAttributes).length;
            const totalAttributes = this.$container.find('.ggmc-variation-attribute-row').length;

            // Need all attributes selected to find a match
            if (selectedCount < totalAttributes) {
                this.selectedVariation = null;
                return;
            }

            // Find exact match - need to ensure string comparison for keys
            this.selectedVariation = this.variations.find(variation => {
                const attrs = variation.attributes;

                // Check if all selected attributes match
                for (const [attrId, value] of Object.entries(this.selectedAttributes)) {
                    const varValue = attrs[String(attrId)];
                    if (varValue !== value) {
                        return false;
                    }
                }

                // Check if variation has same number of attributes
                return Object.keys(attrs).length === selectedCount;
            }) || null;
        }

        /**
         * Update availability state of options based on current selection
         */
        updateAvailability() {
            // In Independent mode, all enabled options are always available
            // (there's no variation matrix to check against)
            if (this.variationMode === 'independent') {
                // All options that are rendered are available (disabled ones aren't rendered)
                this.$container.find('.ggmc-variation-button, .ggmc-variation-swatch').each((j, btn) => {
                    const $btn = $(btn);
                    $btn.removeClass('disabled out-of-stock');
                    $btn.show();
                });
                this.$container.find('.ggmc-variation-select option').each((j, opt) => {
                    const $opt = $(opt);
                    if ($opt.val()) { // Skip placeholder
                        $opt.prop('disabled', false).show();
                    }
                });
                return;
            }

            // Combined mode - check availability based on variation matrix
            this.$container.find('.ggmc-variation-attribute-row').each((i, row) => {
                const $row = $(row);
                const currentAttrId = String($row.data('attribute-id')); // Ensure string for consistent key matching

                // Get all other selected attributes
                const otherSelections = { ...this.selectedAttributes };
                delete otherSelections[currentAttrId];

                // Check each option in this row
                $row.find('.ggmc-variation-button, .ggmc-variation-swatch').each((j, btn) => {
                    const $btn = $(btn);
                    const value = String($btn.data('value')); // Ensure string for consistent matching

                    const availability = this.checkOptionAvailability(currentAttrId, value, otherSelections);

                    $btn.toggleClass('disabled', !availability.available);
                    $btn.toggleClass('out-of-stock', availability.available && !availability.inStock);

                    if (this.hideOutOfStock && !availability.inStock) {
                        $btn.hide();
                    } else {
                        $btn.show();
                    }
                });

                // Handle select options
                $row.find('.ggmc-variation-select option').each((j, opt) => {
                    const $opt = $(opt);
                    const value = $opt.val();

                    if (!value) return; // Skip placeholder

                    const availability = this.checkOptionAvailability(currentAttrId, value, otherSelections);

                    $opt.prop('disabled', !availability.available);

                    if (this.hideOutOfStock && !availability.inStock) {
                        $opt.hide();
                    } else {
                        $opt.show();
                    }
                });
            });
        }

        /**
         * Check if an option value is available given other selections
         * @param {string} attrId - Attribute ID
         * @param {string} value - Option value
         * @param {Object} otherSelections - Other selected attributes
         * @returns {Object} Availability info
         */
        checkOptionAvailability(attrId, value, otherSelections) {
            let available = false;
            let inStock = false;

            // Check all variations to see if this combination exists
            for (const variation of this.variations) {
                const attrs = variation.attributes;

                // Check if this variation has our value
                if (attrs[attrId] !== value) {
                    continue;
                }

                // Check if other selections match
                let matches = true;
                for (const [otherAttrId, otherValue] of Object.entries(otherSelections)) {
                    if (attrs[otherAttrId] !== otherValue) {
                        matches = false;
                        break;
                    }
                }

                if (matches) {
                    available = true;
                    if (variation.available && variation.stock_status !== 'outofstock') {
                        inStock = true;
                        break; // Found available variation
                    }
                }
            }

            return { available, inStock };
        }

        /**
         * Update price display
         */
        updatePriceDisplay() {
            if (!this.showPriceChange) {
                return;
            }

            const $priceWrapper = this.$container.find('.ggmc-variation-price-wrapper');
            const $price = this.$container.find('.ggmc-variation-price');

            // Check if all attributes are selected
            const totalAttributes = this.$container.find('.ggmc-variation-attribute-row').length;
            const selectedCount = Object.keys(this.selectedAttributes).length;
            const allSelected = selectedCount === totalAttributes;

            if (this.variationMode === 'independent') {
                // In independent mode, only show price when all attributes selected
                if (allSelected && this.selectedVariation && this.selectedVariation.price !== null) {
                    const formattedPrice = this.formatPrice(this.selectedVariation.price);
                    $price.text(formattedPrice);
                    $priceWrapper.show();
                } else {
                    $priceWrapper.hide();
                }
            } else {
                // Combined mode - only show price when all selected and variation found
                if (allSelected && this.selectedVariation && this.selectedVariation.price !== null) {
                    const formattedPrice = this.formatPrice(this.selectedVariation.price);
                    $price.text(formattedPrice);
                    $priceWrapper.show();
                } else {
                    $priceWrapper.hide();
                }
            }
        }

        /**
         * Format price with currency
         * @param {number} price - Price value
         * @returns {string} Formatted price
         */
        formatPrice(price) {
            const formatted = parseFloat(price).toFixed(2);

            if (this.currencyPosition === 'before') {
                return this.currencySymbol + formatted;
            }
            return formatted + this.currencySymbol;
        }

        /**
         * Update hidden form inputs
         */
        updateHiddenInputs() {
            const $variationId = this.$container.find('.ggmc-selected-variation-id');
            const $variationData = this.$container.find('.ggmc-selected-variation-data');

            if (this.selectedVariation) {
                $variationId.val(this.selectedVariation.id);
                $variationData.val(JSON.stringify(this.selectedAttributes));
            } else {
                $variationId.val('');
                $variationData.val('');
            }
        }

        /**
         * Trigger variation changed event for other widgets (gallery, price, etc.)
         */
        triggerVariationEvent() {
            // Build variationData JSON string for cart.js compatibility
            const hasSelection = Object.keys(this.selectedAttributes).length > 0;
            const variationDataStr = hasSelection ? JSON.stringify(this.selectedAttributes) : '';

            const eventData = {
                productId: this.productId,
                variationId: this.selectedVariation ? this.selectedVariation.id : null,
                variationData: variationDataStr, // JSON string for cart.js Independent mode
                variation: this.selectedVariation,
                attributes: this.selectedAttributes,
                mode: this.variationMode,
                calculatedPrice: this.calculatedPrice,
                basePrice: this.basePrice
            };

            // Trigger custom event
            $(document).trigger('ggmc_variation_changed', [eventData]);

            // Also trigger on the container for more specific listening
            this.$container.trigger('ggmc_variation_changed', [eventData]);
        }

        /**
         * Update clear button visibility
         */
        updateClearButton() {
            const $clearBtn = this.$container.find('.ggmc-variation-clear');
            const hasSelections = Object.keys(this.selectedAttributes).length > 0;

            if (hasSelections) {
                $clearBtn.show();
            } else {
                $clearBtn.hide();
            }
        }

        /**
         * Clear all selections
         */
        clearSelection() {
            this.selectedAttributes = {};
            this.selectedVariation = null;

            // Clear button selections
            this.$container.find('.ggmc-variation-button, .ggmc-variation-swatch').removeClass('selected');

            // Clear select values
            this.$container.find('.ggmc-variation-select').val('');

            // Clear selected value displays
            this.$container.find('.ggmc-variation-selected-value').text('');

            // Update everything
            this.updateAvailability();
            this.updatePriceDisplay();
            this.updateHiddenInputs();
            this.triggerVariationEvent();
            this.updateClearButton();
        }

        /**
         * Programmatically select a variation by ID
         * @param {number} variationId - Variation ID to select
         */
        selectVariation(variationId) {
            const variation = this.variations.find(v => v.id === variationId);

            if (!variation) {
                return false;
            }

            // Set all attributes
            this.selectedAttributes = { ...variation.attributes };
            this.selectedVariation = variation;

            // Update UI to reflect selection
            Object.entries(variation.attributes).forEach(([attrId, value]) => {
                const $row = this.$container.find(`[data-attribute-id="${attrId}"]`).closest('.ggmc-variation-attribute-row');
                const escapedValue = escapeAttrSelector(value);

                // Update buttons/swatches
                $row.find('.ggmc-variation-button, .ggmc-variation-swatch').removeClass('selected');
                $row.find(`[data-value="${escapedValue}"]`).addClass('selected');

                // Update select
                $row.find('.ggmc-variation-select').val(value);

                // Update display
                this.updateSelectedValueDisplay($row, value);
            });

            this.updateAvailability();
            this.updatePriceDisplay();
            this.updateHiddenInputs();
            this.triggerVariationEvent();
            this.updateClearButton();

            return true;
        }

        /**
         * Get current selection state
         * @returns {Object} Current state
         */
        getState() {
            return {
                productId: this.productId,
                selectedAttributes: { ...this.selectedAttributes },
                selectedVariation: this.selectedVariation,
                isComplete: Object.keys(this.selectedAttributes).length === this.$container.find('.ggmc-variation-attribute-row').length,
                mode: this.variationMode,
                calculatedPrice: this.calculatedPrice,
                basePrice: this.basePrice
            };
        }
    }

    /**
     * Initialize all variation selectors on the page
     */
    function initVariationSelectors() {
        $('.ggmc-variation-selector').each(function() {
            const $container = $(this);

            // Avoid double initialization
            if ($container.data('ggmc-variation-init')) {
                return;
            }

            $container.data('ggmc-variation-init', true);
            const selector = new VariationSelector($container);

            // Store instance for external access
            $container.data('ggmc-variation-selector', selector);
        });
    }

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

    // Re-initialize when Elementor editor loads widget
    $(window).on('elementor/frontend/init', function() {
        if (typeof elementorFrontend !== 'undefined') {
            elementorFrontend.hooks.addAction('frontend/element_ready/ggmc-variation-selector.default', function($scope) {
                initVariationSelectors();
            });
        }
    });

    // Expose for external use
    window.GGMCVariationSelector = VariationSelector;
    window.ggmcInitVariationSelectors = initVariationSelectors;

})(jQuery);
