jQuery(function ($) {
    // Namespace our registration form functionality
    window.EVGE = window.EVGE || {};
    window.EVGE.Registration = window.EVGE.Registration || {};

    // Registration form initializer
    window.EVGE.Registration.Initializer = {
        init: function() {
            if (!this.checkDependencies()) return;
            
            // Handle initial page load
            window.EVGE.Hooks.addAction('evge_page_created', this.handlePageCreated.bind(this));
            
            // Handle modal content loading
            window.EVGE.Hooks.addAction('evge_modal_content_loaded', this.handleModalContentLoaded.bind(this));
        },

        checkDependencies: function() {
            if (typeof window.EVGE.Hooks === 'undefined') {
                console.error('EVGE Registration: Required dependencies not loaded');
                return false;
            }
            return true;
        },

        handlePageCreated: function() {
            const $context = this.getContext();
            if (!$context.length) return;

            this.initializeMainForm($context);
            this.initializeStandaloneForms();
        },

        handleModalContentLoaded: function(data) {
            // Check if the loaded content contains a registration form
            const $modalContent = $('.evge-modal-content');
            this.initializeMainForm($modalContent);
            
            // If the modal content contains a registration form, handle edit mode fields
            const $registrationForm = $modalContent.find('form.evge-registration-form');
            if ($registrationForm.length) {
                const isEditMode = $registrationForm.find('input[name="registration_id"]').length > 0;
                if (isEditMode) {
                    // Create a temporary form instance to handle edit mode fields
                    const tempForm = new EvgeRegistrationForm($registrationForm);
                    tempForm.handleEditModeFieldsInContext($modalContent);
                }
            }
        },

        getContext: function() {
            return $('.evge-standalone-registration-form').length 
                ? $('.evge-standalone-registration-form').first() 
                : $('.evge-modal-content').first();
        },

        initializeMainForm: function($context) {
            window.Evge.RegistrationForm = new EvgeRegistrationForm($context);
            window.Evge.RegistrationForm.init();
        },

        initializeStandaloneForms: function() {
            if (!$('.evge-standalone-registration-form').length) return;

            window.Evge.StandaloneRegistrationForms = [];
            $('.evge-standalone-registration-form').each((index, element) => {
                // add form source to the form if it doesn't exist
                if (!$(element).find('input[name="evge_form_source"]').length) {
                    $(element).find('form').append('<input type="hidden" name="evge_form_source" value="standalone">');
                }
                if (index > 0) {
                    window.Evge.StandaloneRegistrationForms[index] = new EvgeRegistrationForm($(element).find('form'));
                    window.Evge.StandaloneRegistrationForms[index].init();
                }
            });
        }
    };

    // Initialize the registration form functionality
    window.EVGE.Registration.Initializer.init();

    // Registration type selector — keeps hidden inputs in sync with UI (none / additional_guests: radio; simple_count: quantities per type)
    window.EVGE.Registration.TypeSelector = {
        run: function () {
            $('.evge-registration-type-selector').each(function () {
                var $container = $(this);
                if (!$container.length) return;
                var guestMode = $container.attr('data-guest-mode') || 'none';
                var minQty = parseInt($container.attr('data-min-quantity'), 10) || 1;
                var maxQty = parseInt($container.attr('data-max-quantity'), 10) || 1000;
                if (guestMode === 'simple_count') {
                    EVGE.Registration.TypeSelector.initSimpleCountMode($container, minQty, maxQty);
                }
            });
        },
        initSimpleCountMode: function ($container, minQty, maxQty) {
            function getTotal() {
                var total = 0;
                $container.find('.evge-reg-type-input-quantity').each(function () {
                    total += parseInt($(this).val(), 10) || 0;
                });
                return total;
            }
            function updateTotalInput() {
                var total = getTotal();
                $container.find('.evge-reg-type-total-quantity').val(total);
            }
            function getQtyDisplay($cont, typeId) {
                var $display = $cont.find('.evge-reg-type-qty-display[data-type-id="' + typeId + '"]');
                if ($display.length) { return parseInt($display.text(), 10) || 0; }
                var $input = $cont.find('.evge-reg-type-qty-value[data-type-id="' + typeId + '"]');
                return parseInt($input.val(), 10) || 0;
            }
            function setQtyDisplay($cont, typeId, val) {
                var $display = $cont.find('.evge-reg-type-qty-display[data-type-id="' + typeId + '"]');
                if ($display.length) { $display.text(val); return; }
                $cont.find('.evge-reg-type-qty-value[data-type-id="' + typeId + '"]').val(val);
            }
            /** Max selectable for this type: per-type remaining capacity and form max guest count (total). */
            function maxSelectableForType($wrap, val, total) {
                var raw = $wrap.attr('data-type-remaining');
                var typeCap = (raw === undefined || raw === '') ? Number.POSITIVE_INFINITY : parseInt(raw, 10);
                if (isNaN(typeCap)) {
                    typeCap = Number.POSITIVE_INFINITY;
                }
                var maxFromFormTotal = val + maxQty - total;
                return Math.min(typeCap, maxFromFormTotal);
            }
            function refreshAllButtonStates() {
                var total = getTotal();
                $container.find('.evge-registration-type-quantity-wrap').each(function () {
                    var $wrap = $(this);
                    var typeId = $wrap.closest('.evge-registration-type-option').attr('data-type-id');
                    if (!typeId) return;
                    var val = getQtyDisplay($container, typeId);
                    var maxAllowed = maxSelectableForType($wrap, val, total);
                    $wrap.find('.evge-reg-type-qty-minus').toggleClass('evge-button-disabled', val <= minQty);
                    $wrap.find('.evge-reg-type-qty-plus').toggleClass('evge-button-disabled', val >= maxAllowed);
                });
            }
            /** If prefill or edits exceed type capacity or form max guests, reduce counts (iterative). */
            function clampOversizedQuantities() {
                var guard = 0;
                while (guard < 20) {
                    guard += 1;
                    var total = getTotal();
                    var changed = false;
                    $container.find('.evge-registration-type-quantity-wrap').each(function () {
                        var $wrap = $(this);
                        var typeId = $wrap.closest('.evge-registration-type-option').attr('data-type-id');
                        if (!typeId) return;
                        var val = getQtyDisplay($container, typeId);
                        var maxAllowed = maxSelectableForType($wrap, val, total);
                        if (val > maxAllowed) {
                            setQtyDisplay($container, typeId, maxAllowed);
                            $container.find('.evge-reg-type-input-quantity[data-type-id="' + typeId + '"]').val(maxAllowed);
                            changed = true;
                        }
                    });
                    if (!changed) break;
                }
            }
            $container.off('click.evgeTypeSelector', '.evge-reg-type-qty-plus').on('click.evgeTypeSelector', '.evge-reg-type-qty-plus', function (e) {
                e.preventDefault();
                if ($(this).hasClass('evge-button-disabled')) return;
                var typeId = $(this).attr('data-type-id');
                var $wrap = $(this).closest('.evge-registration-type-quantity-wrap');
                var $hiddenQty = $container.find('.evge-reg-type-input-quantity[data-type-id="' + typeId + '"]');
                var val = getQtyDisplay($container, typeId);
                var total = getTotal();
                var maxAllowed = maxSelectableForType($wrap, val, total);
                var newVal = Math.min(val + 1, maxAllowed);
                setQtyDisplay($container, typeId, newVal);
                $hiddenQty.val(newVal);
                refreshAllButtonStates();
                updateTotalInput();
                EVGE.Registration.triggerSummaryUpdate();
            });
            $container.off('click.evgeTypeSelector', '.evge-reg-type-qty-minus').on('click.evgeTypeSelector', '.evge-reg-type-qty-minus', function (e) {
                e.preventDefault();
                if ($(this).hasClass('evge-button-disabled')) return;
                var typeId = $(this).attr('data-type-id');
                var $hiddenQty = $container.find('.evge-reg-type-input-quantity[data-type-id="' + typeId + '"]');
                var val = getQtyDisplay($container, typeId);
                var newVal = Math.max(0, val - 1);
                setQtyDisplay($container, typeId, newVal);
                $hiddenQty.val(newVal);
                refreshAllButtonStates();
                updateTotalInput();
                EVGE.Registration.triggerSummaryUpdate();
            });
            clampOversizedQuantities();
            refreshAllButtonStates();
            updateTotalInput();
        }
    };
    window.EVGE.Registration.triggerSummaryUpdate = function () {
        if (window.Evge && window.Evge.RegistrationForm) {
            if (typeof window.Evge.RegistrationForm.clearRegistrationTypeError === 'function') {
                window.Evge.RegistrationForm.clearRegistrationTypeError();
            }
            if (typeof window.Evge.RegistrationForm.update === 'function') {
                window.Evge.RegistrationForm.update();
            }
        }
    };
    $(document).on('change', '.evge-reg-type-radio', function () {
        var $container = $(this).closest('.evge-registration-type-selector');
        if (!$container.length) return;
        var typeId = String($(this).val());
        $container.find('.evge-reg-type-input-quantity').each(function () {
            var $input = $(this);
            $input.val(String($input.attr('data-type-id')) === typeId ? '1' : '0');
        });
        window.EVGE.Registration.triggerSummaryUpdate();
    });
    $(document).on('click', '.evge-registration-type-option', function (e) {
        var $option = $(this);
        var $radio = $option.find('.evge-reg-type-radio');
        if (!$radio.length || $radio.prop('disabled')) return;
        if ($(e.target).closest('.evge-reg-type-radio, .evge-reg-type-qty-plus, .evge-reg-type-qty-minus').length) return;
        $radio.prop('checked', true).trigger('change');
    });
    $(function () {
        window.EVGE.Registration.TypeSelector.run();
    });
    if (typeof window.EVGE !== 'undefined' && window.EVGE.Hooks) {
        window.EVGE.Hooks.addAction('evge_modal_content_loaded', function () {
            window.EVGE.Registration.TypeSelector.run();
        });
        window.EVGE.Hooks.addAction('evge_page_created', function () {
            window.EVGE.Registration.TypeSelector.run();
        });
    }

    function EvgeRegistrationForm($registrationForm) {
        this.$element = $registrationForm;

        this.$context = $registrationForm.closest('.evge-modal-content').length ? $registrationForm.closest('.evge-modal-content') : $registrationForm.closest('.evge-standalone-registration-form');
        this.$dynamic = this.$context.find('.evge-left-dynamic').length ? this.$context.find('.evge-left-dynamic') : this.$element;
        if (this.$context.find('.evge-standalone-registration-form-container-inner').length) {
            this.$dynamic = this.$context.find('.evge-standalone-registration-form-container-inner');
        }
        
        // Try to get payment JSON from modal summary first, then from standalone form container
        var paymentJsonAttr = this.$context.find('.evge-modal-summary').attr('data-payment-json');
        if (typeof paymentJsonAttr === 'undefined' || paymentJsonAttr === null) {
            // Fallback to standalone form container data attribute
            paymentJsonAttr = this.$context.attr('data-payment-json');
        }
        this.registrationData = typeof paymentJsonAttr !== 'undefined' && paymentJsonAttr !== null ? JSON.parse(paymentJsonAttr) : false;
        this.fields = [];
        this.Validator = new EvgeValidator();
        this.isEditMode = this.$element.find('input[name="registration_id"]').length > 0;
        this.isSubmitting = false;
    }
    EvgeRegistrationForm.prototype = {
        init() {
            this.update();
            this.initTriggers();
            this.initAccessibility();
            this.handleEditModeFields();
        },
        handleEditModeFields() {
            // If this is edit mode, make non-editable fields readonly (PHP outputs data-editable="0" or "1")
            if (this.isEditMode) {
                this.handleEditModeFieldsInContext(this.$context);
            }
        },
        handleEditModeFieldsInContext($context) {
            // Make non-editable fields readonly (PHP outputs data-editable="0", not "false")
            $context.find('.evge-field-wrapper[data-editable="0"]').each(function() {
                const $fieldWrapper = $(this);
                const $input = $fieldWrapper.find('input, select, textarea');
                
                if ($input.length) {
                    // Make the input readonly
                    $input.prop('readonly', true);
                    
                    // For select elements, disable them instead of readonly
                    if ($input.is('select')) {
                        $input.prop('disabled', true);
                    }
                    
                    // Add visual indication that the field is readonly
                    $fieldWrapper.addClass('evge-field-readonly');
                }
            });
        },
        initAccessibility() {
            // Initialize form validation messages
            this.$formMessages = this.$context.find('#evge-form-messages');

            // Handle form validation
            this.$element.on('submit', function (e) {
                this.validateForm(e);
            }.bind(this));

            // Add ARIA attributes to form fields
            this.$context.find('input, select, textarea').each(function () {
                const $field = $(this);
                const $label = $('label[for="' + $field.attr('id') + '"]');

                if ($label.length) {
                    $field.attr('aria-labelledby', $label.attr('id'));
                }

                if ($field.attr('required')) {
                    $field.attr('aria-required', 'true');
                }
            });
        },
        validateForm(e) {
            let isValid = true;
            let firstError = null;

            this.fields.forEach(function (field) {
                if (!field.isValid()) {
                    isValid = false;
                    if (!firstError) {
                        firstError = field.$context;
                    }
                    field.$context.addClass('evge-has-error');
                    field.$input.attr('aria-invalid', 'true');
                } else {
                    field.$context.removeClass('evge-has-error');
                    field.$input.attr('aria-invalid', 'false');
                }
            });

            if (!isValid) {
                this.updateFormMessage(
                    'Please correct the errors in the form before submitting.',
                    'error'
                );
                if (firstError) {
                    firstError.find('input, select, textarea').first().focus();
                }
                return;
            }

            this.submitForm();
        },
        updateFormMessage(message, type = 'info') {
            this.$formMessages
                .attr('role', 'alert')
                .attr('aria-live', 'polite')
                .text(message);
        },
        submitForm() {
            this.updateFormMessage('Processing your registration...', 'info');

            // ... existing form submission code ...

            // After successful submission
            this.updateFormMessage('Registration successful!', 'success');

            // After error
            this.updateFormMessage('There was an error processing your registration. Please try again.', 'error');
        },
        initTriggers() {
            this.initCalculators();
            this.initFields();
            this.initSubmitButton();
        },
        
        startProcessing: function($target, context = 'element') {
            if (context === 'button') {
                $target.wrap('<div class="evge-processing-wrap-flex evge-is-processing"></div>');
                $target.addClass('evge-fade').prop('disabled', true);
                $target.after(window.Evge.spinnerHTML());
            } else if (context === 'area') {
                $target.wrap('<div class="evge-processing-wrap evge-is-processing"></div>');
                $target.addClass('evge-fade');
                $target.after(window.Evge.spinnerHTML());
            } else if (context === 'element') {
                $target.addClass('evge-fade evge-is-processing');
                $target.append(window.Evge.spinnerHTML());
            }
        },
        
        stopProcessing: function($target, context = 'element') {
            setTimeout(function() {
                if (context === 'button') {
                    $target.closest('.evge-processing-wrap-flex').find('.evge-spinner-container').remove();
                    $target.unwrap('evge-processing-wrap');
                    $target.removeClass('evge-fade').prop('disabled', false);
                } else if (context === 'area') {
                    $target.closest('.evge-processing-wrap').find('.evge-spinner-container').remove();
                    $target.removeClass('evge-fade evge-is-processing');
                    $target.unwrap('evge-processing-wrap');
                } else if (context === 'element') {
                    $target.find('.evge-spinner-container').remove();
                    $target.removeClass('evge-fade evge-is-processing');
                }
            }, 500);
        },
        initCalculators() {
            if (this.$context.find('.evge-add').length) {
                this.initQuantityButtons();
            }
        },
        initFields() {
            var self = this;
            self.$context.find('.evge-field-wrapper').each(function () {
                self.fields.push(new EvgeFormField($(this)));
            });
            // loop through fields and call init on each
            this.fields.forEach(function (field) {
                field.init();
            });
        },
        update() {
            this.updateCalculations();
            this.updateDisplay();
        },
        /**
         * True when the context has the registration form (or bulk order) so cost/quantity can be changed by the user.
         * When false (e.g. "Manage Registration" view-only modal), we skip updating the cost summary so server-rendered values are preserved.
         */
        canUpdateCostSummary() {
            if (this.$context.find('.evge-bulk-order-summary').length) {
                return true;
            }
            return this.$context.find('form.evge-registration-form').length > 0;
        },
        updateCalculations() {
            if (!this.canUpdateCostSummary()) {
                return;
            }
            // Check if this is a bulk order (handle even without registrationData.costs)
            if (this.$context.find('.evge-bulk-order-summary').length) {
                this.updateBulkOrderCalculations();
                return;
            }
            
            if (typeof this.registrationData.costs === 'undefined') {
                return;
            }
            
            var $qtySummary = this.$context.find('.evge-modal-quantity-summary');
            if ($qtySummary.length && $qtySummary.attr('data-has-type-lines') === '1') {
                // Per-type line items: update each from registration type selector + additional guests; hide when quantity 0
                var qtyByType = this.getQuantityByType();
                var additionalCount = this.getAdditionalGuestCount();
                $qtySummary.find('.evge-quantity-line-item[data-type-id]').each(function () {
                    var typeId = $(this).attr('data-type-id');
                    var qty = (qtyByType[typeId] !== undefined) ? qtyByType[typeId] : 0;
                    $(this).find('.evge-line-item-quantity').html(qty);
                    $(this).toggle(qty > 0);
                });
                var $guestsLine = $qtySummary.find('.evge-additional-guests-line');
                if (additionalCount > 0) {
                    $guestsLine.show().find('.evge-line-item-quantity').html(additionalCount);
                } else {
                    $guestsLine.hide();
                }
            } else {
                var totalQuantity = this.getTotalQuantity();
                this.$context.find('.evge-line-item-quantity').html(totalQuantity);
            }
            // Type-based cost lines (event has no cost; types have cost)
            var $costDetails = this.$context.find('.evge-modal-cost-details');
            var self = this;
            if ($costDetails.length && $costDetails.attr('data-type-prices')) {
                try {
                    var costQtyByType = this.getQuantityByType();
                    $costDetails.find('.evge-type-cost-line').each(function () {
                        var typeId = $(this).attr('data-type-id');
                        var qty = (costQtyByType[typeId] !== undefined) ? costQtyByType[typeId] : 0;
                        var unitPrice = parseFloat($(this).attr('data-unit-price')) || 0;
                        var lineTotal = (Math.round(qty * unitPrice * 100) / 100).toFixed(2);
                        $(this).find('.evge-line-item-quantity').html(qty);
                        self.setCostSpan($(this).find('.evge-line-item-total .evge-line-item-cost, .evge-line-item-total span'), lineTotal);
                        $(this).toggle(qty > 0);
                    });
                } catch (e) { /* ignore */ }
            } else {
                var eventCost = this.calculateEventCost();
                self.setCostSpan(this.$context.find('#evge-line-item-event .evge-line-item-total .evge-line-item-cost, #evge-line-item-event .evge-line-item-total span'), eventCost);
            }
            var subTotal = this.calculateLineItemSubtotal();
            self.setCostSpan(this.$context.find('#evge-line-item-subtotal .evge-line-item-total .evge-line-item-cost, #evge-line-item-subtotal .evge-line-item-total span'), subTotal);
            var fee = this.calculateFee();
            self.setCostSpan(this.$context.find('#evge-line-item-fees .evge-line-item-total .evge-line-item-cost, #evge-line-item-fees .evge-line-item-total span'), fee);
            var total = this.calculateTotal();
            self.setCostSpan(this.$context.find('#evge-line-item-total .evge-line-item-total .evge-line-item-cost, #evge-line-item-total .evge-line-item-total span'), total);
        },
        updateBulkOrderCalculations() {
            // Get the total quantity (applies to all events in bulk order)
            var totalQuantity = this.getTotalQuantity();
            
            // Update quantity for each event line item
            var self = this;
            var totalCost = 0;
            var currencyBefore = '';
            var currencyAfter = '';
            
            this.$context.find('.evge-bulk-order-summary .evge-cost-item').each(function() {
                var $lineItem = $(this);
                var baseCost = parseFloat($lineItem.attr('data-base-cost')) || 0;
                var eventCost = baseCost * totalQuantity;
                
                // Update quantity display
                $lineItem.find('.evge-line-item-quantity').html(totalQuantity);
                
                // Update cost display (may be visually suppressed when total is zero)
                $lineItem.find('.evge-line-item-cost').html(eventCost.toFixed(2));
                
                // Track currency symbols from first item with cost
                if (eventCost > 0 && !currencyBefore) {
                    currencyBefore = $lineItem.attr('data-currency-before') || '';
                    currencyAfter = $lineItem.attr('data-currency-after') || '';
                }
                
                totalCost += eventCost;
            });
            
            // Update total + toggle zero-cost styling for bulk summary
            var $totalLine = this.$context.find('#evge-line-item-total');
            var $summary = this.$context.find('.evge-bulk-order-summary');
            var $break = this.$context.find('#evge-subtotal-line-break');
            if ($totalLine.length) {
                if (totalCost > 0) {
                    $summary.removeClass('evge-bulk-no-cost');
                    if ($break.length) {
                        $break.show();
                    }
                    $totalLine.show();
                    // Get currency from first event with cost if not already set
                    if (!currencyBefore) {
                        var $firstItem = this.$context.find('.evge-bulk-order-summary .evge-cost-item').first();
                        currencyBefore = $firstItem.attr('data-currency-before') || '';
                        currencyAfter = $firstItem.attr('data-currency-after') || '';
                    }
                    $totalLine.find('.evge-total-cost').html(totalCost.toFixed(2));
                } else {
                    $summary.addClass('evge-bulk-no-cost');
                    if ($break.length) {
                        $break.hide();
                    }
                    $totalLine.hide();
                }
            }
        },
        updateDisplay() {
            if (typeof this.registrationData.costs === 'undefined') {
                return;
            }
            
            var totalQuantity = this.getTotalQuantity();
            var effectiveMaxQuantity = this.getEffectiveMaxQuantity();
            
            if (totalQuantity >= effectiveMaxQuantity) {
                this.$context.find('.evge-add').addClass('evge-button-disabled');
            } else {
                this.$context.find('.evge-add').removeClass('evge-button-disabled');
            }

            // Check minimum quantity (default to 1 if not set)
            var minQuantity = (this.registrationData && this.registrationData.restrictions && this.registrationData.restrictions.minQuantity !== undefined) 
                ? this.registrationData.restrictions.minQuantity 
                : 1;
            if (totalQuantity <= minQuantity) {
                this.$context.find('.evge-subtract').addClass('evge-button-disabled');
            } else {
                this.$context.find('.evge-subtract').removeClass('evge-button-disabled');
            }
        },
        getEffectiveMaxQuantity() {
            // Default to a high number if restrictions data is not available
            var maxQuantity = (this.registrationData && this.registrationData.restrictions && this.registrationData.restrictions.maxQuantity !== undefined)
                ? this.registrationData.restrictions.maxQuantity
                : 999;

            // If we're in edit mode and have current user capacity data, adjust the max
            if (this.isEditMode && this.registrationData && this.registrationData.restrictions && this.registrationData.restrictions.currentUserCapacity) {
                var currentUserCapacity = this.registrationData.restrictions.currentUserCapacity;
                // Add back the current user's capacity since they're editing their existing registration
                maxQuantity += currentUserCapacity;
            }
            
            return maxQuantity;
        },
        /**
         * Format a numeric amount using the event's cost display (template + symbol from .evge-modal-cost-details).
         * @param {number|string} amount - Numeric amount (e.g. 10.50)
         * @returns {string} Formatted string for display
         */
        formatCostDisplay(amount) {
            var $costDetails = this.$context.find('.evge-modal-cost-details');
            var template = ($costDetails.attr('data-cost-template') || '').toString();
            var symbol = ($costDetails.attr('data-currency-symbol') || '').toString();
            var num = parseFloat(amount);
            if (isNaN(num)) {
                num = 0;
            }
            var amountStr = (Math.round(num * 100) / 100).toFixed(2);
            if (template && template.indexOf('{amount}') !== -1) {
                return template.replace(/{amount}/g, amountStr).replace(/{symbol}/g, symbol);
            }
            return symbol + amountStr;
        },
        setCostSpan($span, amount) {
            var formatted = this.formatCostDisplay(amount);
            var num = (Math.round(parseFloat(amount) * 100) / 100).toFixed(2);
            $span.html(formatted).attr('data-amount', num);
        },
        calculateEventCost() {
            var totalQuantity = this.getTotalQuantity();
            var eventCost = (this.registrationData && this.registrationData.costs && this.registrationData.costs.eventCost !== undefined)
                ? this.registrationData.costs.eventCost
                : 0;
            var rawCost = totalQuantity * eventCost;

            return (Math.round(rawCost * 100) / 100).toFixed(2);
        },
        calculateLineItemSubtotal() {
            var self = this;
            this.registrationData.costs.subTotal = 0;
            this.$context.find('.evge-cost-item').each(function () {
                var $span = $(this).find('.evge-line-item-total .evge-line-item-cost, .evge-line-item-total span');
                var amt = parseFloat($span.attr('data-amount'));
                self.registrationData.costs.subTotal = self.registrationData.costs.subTotal + (isNaN(amt) ? 0 : amt);
            });

            this.registrationData.costs.subTotal = (Math.round(this.registrationData.costs.subTotal * 100) / 100).toFixed(2);

            return this.registrationData.costs.subTotal;
        },
        getSelectedGateway() {
            return 'offline';
        },
        /**
         * Gateway key to use for fee/total lookups. Uses selected gateway if present in data,
         * otherwise the first available gateway (e.g. when extension overrides to woocommerce only).
         */
        getEffectiveGateway() {
            var gateways = this.registrationData && this.registrationData.costs && this.registrationData.costs.fees && this.registrationData.costs.fees.gateways ? this.registrationData.costs.fees.gateways : {};
            var selected = this.getSelectedGateway();
            if (gateways[selected]) {
                return selected;
            }
            var keys = Object.keys(gateways);
            return keys.length ? keys[0] : selected;
        },
        feeSettings() {
            let gateway = this.getEffectiveGateway(),
                g = this.registrationData.costs.fees.gateways[gateway];
            if (!g) {
                return { feePercent: 0, feeFlat: 0 };
            }
            let feePercent = parseFloat(g.feePercent),
                feeFlat = parseFloat(g.feeFlat);

            return {
                feePercent: feePercent,
                feeFlat: feeFlat
            }
        },
        calculateFee() {
            let gateway = this.getEffectiveGateway();
            var g = this.registrationData.costs.fees.gateways[gateway];
            if (!g) {
                return 0;
            }
            g.feeCalculated = (this.feeSettings().feePercent / 100) * this.registrationData.costs.subTotal + this.feeSettings().feeFlat;
            g.feeCalculated = (Math.round(g.feeCalculated * 100) / 100).toFixed(2);
            return g.feeCalculated;
        },
        calculateTotal() {
            var gateway = this.getEffectiveGateway(),
                g = this.registrationData.costs.fees.gateways[gateway];
            if (!g) {
                this.registrationData.costs.total = (Math.round(parseFloat(this.registrationData.costs.subTotal || 0) * 100) / 100).toFixed(2);
                return this.registrationData.costs.total;
            }
            var rawTotal = parseFloat(g.feeCalculated) + parseFloat(this.registrationData.costs.subTotal);

            this.registrationData.costs.total = (Math.round(rawTotal * 100) / 100).toFixed(2);

            return this.registrationData.costs.total;
        },
        /**
         * When the form has registration type selector(s), returns quantities per type.
         * Aggregates from all selectors (main + guest selectors for additional_guests).
         * Keys are type IDs (string), values are quantities (number). Returns {} when no type selector.
         */
        getQuantityByType() {
            var $containers = this.$context.find('.evge-registration-type-selector');
            if (!$containers.length) {
                return {};
            }
            var qtyByType = {};
            $containers.each(function () {
                var $container = $(this);
                $container.find('.evge-reg-type-input-quantity').each(function () {
                    var typeId = $(this).attr('data-type-id');
                    if (typeId !== undefined) {
                        var qty = parseInt($(this).val(), 10) || 0;
                        qtyByType[typeId] = (qtyByType[typeId] || 0) + qty;
                    }
                });
            });
            return qtyByType;
        },
        getAdditionalGuestCount() {
            return this.$context.find('.evge-guest-tab:not([data-guest-number="main"])').length;
        },
        getTotalQuantity() {
            var qtyByType = this.getQuantityByType();
            var additionalGuestCount = this.getAdditionalGuestCount();
            if (Object.keys(qtyByType).length > 0) {
                var sum = 0;
                for (var k in qtyByType) {
                    if (qtyByType.hasOwnProperty(k)) {
                        sum += qtyByType[k];
                    }
                }
                // Sum already includes main + all guest selectors when using type selectors (additional_guests or simple_count).
                return sum;
            }
            // First, try to get the quantity from the count display (how many registrations area)
            var countDisplay = this.$context.find('.evge-count span');
            if (countDisplay.length) {
                var displayQuantity = parseInt(countDisplay.text());
                if (!isNaN(displayQuantity) && displayQuantity > 0) {
                    return displayQuantity + additionalGuestCount;
                }
            }
            
            // Fallback to registration data
            var baseQuantity = (this.registrationData && this.registrationData.costs && this.registrationData.costs.quantity !== undefined)
                ? this.registrationData.costs.quantity
                : 1;

            //evge-guest-count
            if (this.$context.find('.evge-guest-count').length && $('.evge-already-registered-management-modal').length) {
                var guestCount = this.$context.find('.evge-guest-count strong').text();
                return guestCount;
            }
            
            // Return total quantity (base + additional guests)
            return baseQuantity + additionalGuestCount;
        },
        initQuantityButtons() {
            var self = this;
            // Only bind to the event-wide simple counter (not the type selector's per-type counters)
            self.$context.find('.evge-num-registrations-counter-wrap .evge-counter-button').on('click', function (event) {
                event.preventDefault();
                if ($(this).hasClass('evge-button-disabled')) {
                    return;
                }
                var buttonType = $(this).hasClass('evge-add') ? 'add' : 'subtract',
                    currentValue = parseInt(self.$context.find('.evge-num-registrations-counter-wrap .evge-count span').text(), 10) || 0,
                    minQuantity = (self.registrationData && self.registrationData.restrictions && self.registrationData.restrictions.minQuantity !== undefined)
                        ? self.registrationData.restrictions.minQuantity
                        : 1,
                    maxQuantity = self.getEffectiveMaxQuantity();

                if (buttonType === 'add') {
                    currentValue++;
                } else {
                    currentValue--;
                }
                
                // Enforce minimum and maximum limits
                currentValue = Math.max(currentValue, minQuantity);
                currentValue = Math.min(currentValue, maxQuantity);
                
                // Update both the count display and registration data (event-wide counter only)
                self.$context.find('.evge-num-registrations-counter-wrap .evge-count span').html(currentValue);
                
                if (typeof self.registrationData.costs !== 'undefined') {
                    self.registrationData.costs.quantity = currentValue;
                } else {
                    self.$context.find('.evge-line-item-quantity').text(currentValue);
                }

                self.update();
            })
        },
        initSubmitButton() {
            var self = this;
            self.$context.find('.evge-registration-form').on('submit', function (event) {
                event.preventDefault();
                var $form = $(event.currentTarget);
                var $submitButton = $form.find('button[type=submit]');
                if (self.isSubmitting || $submitButton.hasClass('evge-button-disabled') || $submitButton.hasClass('evge-ajax-check-processing')) {
                    return;
                }
                self.isSubmitting = true;
                $submitButton.addClass('evge-button-disabled').prop('disabled', true);
                self.clearErrors();

                let form = event.currentTarget;
                let formData = new FormData(form);

                if (self.fieldValuesAreValid(formData)) {
                    self.$context.addClass('evge-is-processing').append(window.Evge.spinnerHTML());
                    // Add the quantity selector to the form submission
                    if (self.$context.find('.evge-count').length) {
                        formData.append('quantity', self.$context.find('.evge-count').text());
                    }

                    window.Evge.ajax({
                        data: formData,
                        processData: false,
                        contentType: false,
                        success: function(data) {
                            if (!data || !data.success || !data.data || data.data.submission_status !== 'success') {
                                self.$context.removeClass('evge-is-processing');
                                self.$context.find('.evge-spinner-container').remove();
                                if (data && data.data && typeof data.data.error_fields === 'object') {
                                    data.data.error_fields.forEach(function (error) {
                                        self.addError(error);
                                    });
                                    self.scrollToError();
                                } else {
                                    self.$context.find('.evge-field-error').first().text(evge.i18n.ajaxError || 'There was an error processing your registration. Please try again.');
                                }
                                self.isSubmitting = false;
                                $submitButton.removeClass('evge-button-disabled').prop('disabled', false);
                                return;
                            }
                            $('.evge-spinner-container div').fadeOut(function () {
                                $('.evge-spinner-container').remove();
                            });
                            // Remove the submit button to prevent confusion after successful submission
                            self.$context.find('button[type=submit]').fadeOut(300, function() {
                                $(this).remove();
                                self.$context.removeClass('evge-is-processing');
                            });
                            
     
                            if ($('.evge-modal-col.evge-modal-col-left').length) {
                                self.$dynamic.fadeOut(function () {
                                    self.$context.find('.evge-left-dynamic').html('<div class="evge-message-default" data-evge-refresh-on-close><div class="evge-message-centered-content">' + data.data.response_html).fadeIn(function() {
                                        // Trigger event for other scripts to hook into after content is loaded
                                        $(document).trigger('evge_registration_success', [data.data]);
                                    }) + '</div></div>';
                                });
                            } else {
                                self.$dynamic.fadeOut(function () {
                                    self.$context.append('<div class="evge-left-dynamic evge-standalone-dynamic"></div>');
                                    self.$context.find('.evge-left-dynamic').html('<div class="evge-message-default" data-evge-refresh-on-close><div class="evge-message-centered-content">' + data.data.response_html).fadeIn(function() {
                                        // Trigger event for other scripts to hook into after content is loaded
                                        $(document).trigger('evge_registration_success', [data.data]);
                                    }) + '</div></div>';
                                });
                            }
                        },
                        error: function() {
                            self.$context.removeClass('evge-is-processing');
                            self.$context.find('.evge-spinner-container').remove();
                            self.$context.find('.evge-field-error').first().text(evge.i18n.ajaxError || 'There was an error processing your registration. Please try again.');
                            self.isSubmitting = false;
                            $submitButton.removeClass('evge-button-disabled').prop('disabled', false);
                        }
                    });
                } else {
                    self.handleErrors();
                    self.isSubmitting = false;
                    $submitButton.removeClass('evge-button-disabled').prop('disabled', false);
                    self.scrollToError();
                }
            });

            self.$context.find('#evge-cancel-form').on('submit', function (event) {
                event.preventDefault();
                var $context = $(this);
                var $submitButton = $context.find('button[type=submit]');
                if (self.isSubmitting || $submitButton.hasClass('evge-button-disabled')) {
                    return;
                }
                self.isSubmitting = true;
                self.clearErrors();

                $submitButton.addClass('evge-button-disabled').prop('disabled', true);

                let form = event.currentTarget;
                let formData = new FormData(form);
                let field = new EvgeFormField(self.$context.find('#evge_cancel_email').closest('.evge-field-wrapper'));

                if (field.isValid()) {
                    window.Evge.Modal.$element.addClass('evge-is-processing').append(window.Evge.spinnerHTML());
                    $context.find('.evge-field-error').attr('aria-busy', 'true');

                    window.Evge.ajax({
                        data: formData,
                        processData: false,
                        contentType: false,
                        success: function(data) {
                            window.Evge.Modal.$element.removeClass('evge-is-processing');
                            $('.evge-spinner-container div').fadeOut(function () {
                                $('.evge-spinner-container').remove();
                            });

                            if (data.data.submission_status !== 'success') {
                                self.isSubmitting = false;
                                $submitButton.removeClass('evge-button-disabled').prop('disabled', false);
                                if (typeof data.data.error_fields === 'object') {
                                    data.data.error_fields.forEach(function (error) {
                                        self.addError(error);
                                    });
                                    $context.find('.evge-field-error')
                                        .attr('aria-busy', 'false')
                                        .attr('role', 'alert')
                                        .attr('aria-live', 'polite');
                                }
                            } else {
                                // Remove the submit button to prevent confusion after successful cancellation
                                $submitButton.fadeOut(300, function() {
                                    $(this).remove();
                                });
                                
                                $('.evge-left-dynamic').fadeOut(function () {
                                    $('.evge-modal-col.evge-modal-col-left').append('<div class="evge-left-dynamic" style="visibility: hidden"></div>');
                                    $('.evge-left-dynamic')
                                        .html('<div class="evge-message-center"><div class="evge-message-centered-content">' + data.data.response_html)
                                        .attr('role', 'status')
                                        .attr('aria-live', 'polite')
                                        .fadeIn();
                                });
                            }
                        },
                        error: function() {
                            window.Evge.Modal.$element.removeClass('evge-is-processing');
                            self.isSubmitting = false;
                            $submitButton.removeClass('evge-button-disabled').prop('disabled', false);
                            $('.evge-spinner-container div').fadeOut(function () {
                                $('.evge-spinner-container').remove();
                            });
                            $context.find('.evge-field-error')
                                .attr('aria-busy', 'false')
                                .attr('role', 'alert')
                                .attr('aria-live', 'polite')
                                .text(evge.i18n.ajaxError);
                        }
                    });
                } else {
                    field.addError('cancel_email');
                    self.isSubmitting = false;
                    $submitButton.removeClass('evge-button-disabled').prop('disabled', false);
                    self.scrollToError();
                }
            });
        },
        fieldValuesAreValid(formData) {
            var isValid = true;

            this.fields.forEach(function (field) {
                if (!field.isValid()) {
                    isValid = false;
                }
            });

            // Validate registration type total >= minimum when type selector is present (2+ types)
            var typeValidation = this.validateRegistrationTypes();
            if (!typeValidation.valid) {
                isValid = false;
            }

            // Also validate additional guest fields if they exist
            if (window.Evge.AdditionalGuests && typeof window.Evge.AdditionalGuests.validateGuestFields === 'function') {
                var guestValidation = window.Evge.AdditionalGuests.validateGuestFields();
                if (!guestValidation.isValid) {
                    isValid = false;
                }
            }

            return isValid;
        },
        validateRegistrationTypes() {
            // For additional_guests, validate the visible selector (main or current guest). Otherwise use first selector.
            var $visibleBlock = this.$context.find('.evge-main-registration:visible').add(this.$context.find('.evge-guest-registration:visible'));
            var $selector = $visibleBlock.first().find('.evge-registration-type-selector');
            if (!$selector.length) {
                $selector = this.$context.find('.evge-registration-type-selector').first();
            }
            if (!$selector.length) {
                return { valid: true };
            }
            var minQty = parseInt($selector.attr('data-min-quantity'), 10) || 1;
            var total = 0;
            $selector.find('.evge-reg-type-input-quantity').each(function () {
                total += parseInt($(this).val(), 10) || 0;
            });
            if (total < minQty) {
                return {
                    valid: false,
                    message: (typeof evge !== 'undefined' && evge.i18n && evge.i18n.registrationTypeSelectionRequired ? evge.i18n.registrationTypeSelectionRequired : 'Please make a selection')
                };
            }
            return { valid: true };
        },
        showRegistrationTypeError(message) {
            var $visibleBlock = this.$context.find('.evge-main-registration:visible').add(this.$context.find('.evge-guest-registration:visible'));
            var $wrap = $visibleBlock.first().find('.evge-field-wrapper-registration-types');
            if (!$wrap.length) {
                $wrap = this.$context.find('.evge-registration-type-selector').first().closest('.evge-field-wrapper-registration-types');
            }
            if (!$wrap.length) return;
            this.clearRegistrationTypeError();
            $wrap.find('.evge-registration-type-selector').addClass('evge-has-error');
            var $err = $wrap.find('.evge-field-error');
            $err.find('span').text(message || 'Please make a selection');
            $err.attr('role', 'alert').attr('aria-live', 'polite').attr('aria-atomic', 'true');
        },
        clearRegistrationTypeError() {
            this.$context.find('.evge-field-wrapper-registration-types').each(function () {
                $(this).find('.evge-registration-type-selector').removeClass('evge-has-error');
                $(this).find('.evge-field-error span').empty();
            });
        },
        addError(errorName, message) {
            var $fieldWrap = this.$context.find('#evge_' + errorName).closest('.evge-field-wrapper');
            if (!$fieldWrap.length) return;
            if (errorName === 'registration_types') {
                this.showRegistrationTypeError(message);
                return;
            }
            let field = new EvgeFormField($fieldWrap);
            field.clearErrors();
            field.$context.addClass('evge-has-error');
            field.$input.attr('aria-invalid', 'true');
            field.errorInput
                .attr('role', 'alert')
                .attr('aria-live', 'polite')
                .attr('aria-atomic', 'true')
                .show();
            if (message && field.errorInput.find('span').length) {
                field.errorInput.find('span').text(message);
            }
        },
        handleErrors() {
            this.fields.forEach(function (field) {
                field.clearErrors();

                if (!field.isValid()) {
                    field.$context.addClass('evge-has-error');
                    field.$input.attr('aria-invalid', true);
                }
                
                // Trigger validation state change for each field
                field.triggerValidationStateChange();
            });

            // Registration type minimum quantity
            var typeValidation = this.validateRegistrationTypes();
            if (!typeValidation.valid) {
                this.showRegistrationTypeError(typeValidation.message);
            }

            // Also handle guest field errors
            if (window.Evge.AdditionalGuests && typeof window.Evge.AdditionalGuests.validateGuestFields === 'function') {
                window.Evge.AdditionalGuests.validateGuestFields();
            }
        },
        clearErrors() {
            this.fields.forEach(function (field) {
                field.clearErrors();
            });

            this.clearRegistrationTypeError();

            // Also clear guest field errors
            if (window.Evge.AdditionalGuests && typeof window.Evge.AdditionalGuests.clearGuestFieldErrors === 'function') {
                window.Evge.AdditionalGuests.clearGuestFieldErrors();
            }
        },
        scrollToError() {
            $('.evge-modal').animate({
                scrollTop: this.$context.find('.evge-has-error').first().offset().top - 400
            }, 750);
        }
    }

    // Make EvgeFormField globally accessible
    window.EvgeFormField = function($formFieldWrap) {
        this.$context = $formFieldWrap;
        this.$input = this.getInputElement($formFieldWrap);
        this.type = this.getFieldType($formFieldWrap);
        this.required = $formFieldWrap.attr('data-required') !== undefined ?
            $formFieldWrap.attr('data-required') === '1' :
            false;
        this.validation = this.getValidationRules();
        this.errorInput = this.$context.find('.evge-field-error');
        this.errorInputText = this.$context.find('.evge-field-error').text();
        this.afterInput = false;
    }

    EvgeFormField.prototype = {
        init() {
            if (this.type === 'email') {
                this.initEmailValidation();
            }
            
            // Initialize file upload change button functionality
            if (this.type === 'file') {
                this.initFileUploadChangeButton();
            }
        },
        initEmailValidation() {
            let self = this;
            let validationTimer;

            this.$input.on('input', function () {
                // Clear any previous timer
                clearTimeout(validationTimer);

                // Set a new timer to validate after user stops typing
                validationTimer = setTimeout(function () {
                    self.validateEmail();
                }, 1500);
            });
        },
        
        initFileUploadChangeButton() {
            let self = this;
            
            // Find the change file button within this field context
            const $changeButton = this.$context.find('.evge-file-upload-change');
            
            if ($changeButton.length) {
                $changeButton.on('click', function(e) {
                    e.preventDefault();
                    
                    // Start processing state using helper method
                    window.Evge.RegistrationForm.startProcessing($changeButton, 'button');
                    
                    // After 2 second delay, fade out the existing file info and show the upload field
                    setTimeout(function() {
                        // Stop processing state using helper method
                        window.Evge.RegistrationForm.stopProcessing($changeButton, 'button');
                        
                        // Fade out the existing file information
                        const $existingInfo = self.$context.find('.evge-file-upload-existing');
                        $existingInfo.fadeOut(300, function() {
                            // Show the file upload field
                            const $uploadField = self.$context.find('.evge-file-upload-field');
                            $uploadField.removeClass('evge-hidden').fadeIn(300);
                            
                            $existingInfo.remove();
                            
                            // Initialize file size validation after the field becomes visible
                            self.initFileSizeValidation();
                        });
                    }, 1000);
                });
            }
            
            // Initialize file size validation (for fields that are already visible)
            this.initFileSizeValidation();
        },
        
        initFileSizeValidation() {
            let self = this;
            const $fileInput = this.$context.find('input[type="file"].evge-file-input');
            
            if (!$fileInput.length) {
                return;
            }
            
            // Get max file size from data attribute (in MB)
            const maxSizeMB = $fileInput.attr('data-max-size');
            
            if (!maxSizeMB || maxSizeMB === '') {
                return;
            }
            
            // Convert MB to bytes
            const maxSizeBytes = parseFloat(maxSizeMB) * 1024 * 1024;
            
            // Remove any existing handlers to prevent duplicates
            $fileInput.off('change.evgeFileSizeValidation');
            
            // Listen for file selection
            $fileInput.on('change.evgeFileSizeValidation', function(e) {
                const file = this.files[0];
                
                if (!file) {
                    self.clearErrors();
                    return;
                }
                
                // Check file size
                if (file.size > maxSizeBytes) {
                    // File is too large
                    const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
                    const errorMessage = evge.i18n.fileSizeExceeds || 
                        'File size (' + fileSizeMB + ' MB) exceeds maximum allowed size of ' + maxSizeMB + ' MB.';
                    
                    self.addError('file_size');
                    self.errorInput.text(errorMessage);
                    self.$input.attr('aria-invalid', 'true');
                    
                    // Clear the file input
                    $(this).val('');
                } else {
                    // File size is valid
                    self.clearErrors();
                    self.$input.attr('aria-invalid', 'false');
                }
            });
        },
        validateEmail() {
            let self = this;
            let email = this.getValue();
            let eventId = this.$context.closest('.evge-registration-form').find('input[name="event_id"]').val();
            let $submitButton = this.$context.closest('.evge-registration-form').find('button[type=submit]');

            if (!email || !eventId) {
                return;
            }

            // First validate email format using existing validator
            if (!window.Evge.RegistrationForm.Validator.validateEmail(email)) {
                self.addError('email_format');
                self.errorInput.text(self.errorInputText);
                self.$input.removeAttr('data-email-duplicate');
                return;
            }

            if (!evgeRegistrationForm.settings.validateDuplicateEmail) {
                return;
            }

            self._emailValidateSeq = (self._emailValidateSeq || 0) + 1;
            var emailValidateSeq = self._emailValidateSeq;

            // Disable submit button while validating
            $submitButton.addClass('evge-ajax-check-processing');

            // If email format is valid and duplicate validation is enabled, check for duplicates
            window.Evge.ajax({
                data: { 
                    action: 'evge_validate_email',
                    email: email,
                    event_id: eventId
                },
                success: function(response) {
                    if (emailValidateSeq !== self._emailValidateSeq) {
                        return;
                    }
                    if (!response.data.prevent_registration) {
                        self.clearErrors();
                        self.$input.attr('aria-invalid', 'false');
                        self.$input.removeAttr('data-email-duplicate');
                    } else {
                        self.addError('email_duplicate');
                        self.errorInput.text(response.data.message);
                        self.$input.attr('data-email-duplicate', 'true');
                    }
                },
                error: function() {
                    if (emailValidateSeq !== self._emailValidateSeq) {
                        return;
                    }
                    self.addError('email_validation_error');
                    self.errorInput.text('Error validating email. Please try again.');
                    self.$input.removeAttr('data-email-duplicate');
                },
                complete: function() {
                    if (emailValidateSeq === self._emailValidateSeq) {
                        $submitButton.removeClass('evge-ajax-check-processing');
                    }
                }
            });
        },
        getInputElement($wrap) {
            // Handle different input types
            if ($wrap.find('textarea').length) {
                return $wrap.find('textarea');
            } else if ($wrap.find('select').length) {
                return $wrap.find('select');
            } else if ($wrap.find('input[type="radio"]').length) {
                return $wrap.find('input[type="radio"]');
            } else if ($wrap.find('input[type="checkbox"]').length) {
                return $wrap.find('input[type="checkbox"]');
            }
            return $wrap.find('input');
        },

        getFieldType($wrap) {
            if ($wrap.find('textarea').length) return 'textarea';
            if ($wrap.find('select').length) return 'select';
            if ($wrap.find('input[type="radio"]').length) return 'radio';
            if ($wrap.find('input[type="checkbox"]').length) {
                // Check for data-single-checkbox attribute
                if ($wrap.find('input[type="checkbox"]').attr('data-single-checkbox') === 'true') {
                    return 'single-checkbox';
                }
                return 'checkbox';
            }
            if ($wrap.find('input[type="tel"]').length) return 'phone';
            if ($wrap.find('input[type="file"]').length) return 'file';
            if ($wrap.find('input[type="email"]').length) return 'email';

            // Check if this is an email field by name or data attribute
            let fieldName = this.$input.attr('name') || '';
            if (fieldName.includes('email') || $wrap.attr('data-field-type') === 'email') {
                return 'email';
            }

            let inputType = this.$input.attr('type');
            return inputType || 'text';
        },

        getValidationRules() {
            let rules = {
                type: 'length',
                min: this.required ? 1 : 0,
                max: 'none'
            };

            switch (this.type) {
                case 'file':
                    rules.type = 'file';
                    break;
                case 'email':
                    rules.type = 'email';
                    break;
                case 'phone':
                    rules.type = 'phone';
                    break;
                case 'radio':
                case 'checkbox':
                    rules.type = 'checked';
                    break;
                case 'single-checkbox':
                    rules.type = 'single-checked';
                    break;
            }

            return rules;
        },

        getValue() {
            switch (this.type) {
                case 'file':
                    // For file inputs, return the file value or empty string
                    let fileVal = this.$input.val();
                    return fileVal !== null && fileVal !== undefined ? fileVal : '';
                    
                case 'radio':
                    // Return empty string if nothing checked
                    let checkedRadio = this.$input.filter(':checked').val();
                    return checkedRadio || '';

                case 'checkbox':
                    let values = [];
                    this.$input.filter(':checked').each(function () {
                        let val = $(this).val();
                        // Only add defined, non-null values
                        if (val !== undefined && val !== null) {
                            values.push(val);
                        }
                    });
                    return values;

                case 'single-checkbox':
                    // For single checkbox, return the value if checked, otherwise empty string
                    let checkboxVal = this.$input.val();
                    return this.$input.is(':checked') ? true : '';

                case 'select':
                    let selectVal = this.$input.val();
                    return selectVal !== null && selectVal !== undefined ? selectVal : '';

                default:
                    let inputVal = this.$input.val();
                    return inputVal !== null && inputVal !== undefined ? inputVal.trim() : '';
            }
        },

        isValid() {
            let val = this.getValue();
            if (!this.required && (val === '' || val.length === 0)) {
                return true;
            }

            switch (this.validation.type) {
                case 'file':
                    return this.validateFileUpload(val);
                case 'email':
                    if (evgeRegistrationForm.settings.validateDuplicateEmail) {
                        if (this.$input.attr('data-email-duplicate') === 'true') {
                            return false;
                        }
                    }
                    return window.Evge.RegistrationForm.Validator.validateEmail(val);
                case 'phone':
                    return window.Evge.RegistrationForm.Validator.validatePhone(val);
                case 'number':
                    return window.Evge.RegistrationForm.Validator.validateNumber(val, this.validation.min, this.validation.max);
                case 'checked':
                    return window.Evge.RegistrationForm.Validator.validateChecked(val);
                case 'single-checked':
                    return window.Evge.RegistrationForm.Validator.validateSingleChecked(val);
                case 'select':
                    return window.Evge.RegistrationForm.Validator.validateSelect(val);
                case 'count':
                    return window.Evge.RegistrationForm.Validator.validateCount(val, this.validation.acceptableCounts, this.validation.countWhat);
                case 'length':
                default:
                    return window.Evge.RegistrationForm.Validator.validateLength(val, this.validation.min, this.validation.max);
            }
        },
        
        validateFileUpload(val) {
            // Check if there's a file size error (set by initFileSizeValidation)
            if (this.$input.attr('aria-invalid') === 'true' && this.$context.hasClass('evge-has-error')) {
                return false;
            }
            
            // If field is not required, it's always valid (unless there's a size error)
            if (!this.required) {
                return true;
            }
            
            // Check if there's an original file value (meaning a file was previously submitted)
            const $originalInput = this.$context.find('input[name$="_original"]');
            const hasOriginalFile = $originalInput.length > 0 && $originalInput.val() !== '';
            
            // If there's an original file, the field is valid even if no new file is selected
            if (hasOriginalFile) {
                return true;
            }
            
            // If no original file and field is required, check if a new file is selected
            return val !== '' && val !== null && val !== undefined;
        },

        clearErrors() {
            this.$context.removeClass('evge-has-error');
            this.$input.attr('aria-invalid', false);
            this.errorInput.hide();
            
            // Trigger event for field validation state change
            this.triggerValidationStateChange();
        },

        addError(errorType) {
            this.$context.addClass('evge-has-error');
            this.$input.attr('aria-invalid', 'true');
            this.errorInput
                .attr('role', 'alert')
                .attr('aria-live', 'polite')
                .attr('aria-atomic', 'true')
                .show();
            
            // Trigger event for field validation state change
            this.triggerValidationStateChange();
        },

        triggerValidationStateChange() {
            var isValid = this.isValid();
            
            // Trigger custom event for validation state change
            $(document).trigger('evge_field_validation_changed', {
                isValid: isValid,
                fieldContext: this.$context
            });
        }
    }

    function EvgeValidator() {
        this.validateLength = function (val, min, max) {
            let workingMax = max;
            let workingMin = min;

            // Match PHP validator max/min handling
            if (workingMax === 'none' || workingMax > 50000) {
                workingMax = 50000;
            }

            if (workingMin < 0) {
                workingMin = 0;
            }

            return val.length >= workingMin && val.length <= workingMax;
        };

        this.validateEmail = function (val) {
            // Email validation that requires proper domain structure with TLD
            var regEx = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/;
            return regEx.test(val.trim());
        };

        this.validatePhone = function (val) {
            // Count the number of digits in the string
            let digitCount = (val.match(/\d/g) || []).length;
            return digitCount >= 5;
        };

        this.validateNumber = function (val, minVal, maxVal) {
            let workingMax = maxVal;
            let workingMin = minVal;

            // Match PHP validator number ranges
            if (workingMax === 'no-max' || workingMax > 9999999) {
                workingMax = 9999999;
            }

            if (workingMin === 'no-min' || workingMin < -9999999) {
                workingMin = -9999999;
            }

            let num = parseFloat(val);
            return !isNaN(num) && num >= workingMin && num <= workingMax;
        };

        this.validateCount = function (val, acceptableCounts, countWhat = 'numbers') {
            let strippedSubject = val;

            if (countWhat === 'numbers') {
                strippedSubject = val.replace(/\D/g, '');
            } else if (countWhat === 'letters') {
                strippedSubject = val.replace(/[^a-zA-Z]/g, '');
            }

            // Handle both array and comma-separated string formats
            let counts = Array.isArray(acceptableCounts)
                ? acceptableCounts
                : acceptableCounts.split(',').map(n => parseInt(n.trim()));

            // Special case for letters - always return true (matching PHP)
            if (countWhat === 'letters') {
                return true;
            }

            return counts.includes(strippedSubject.length);
        };

        this.validateChecked = function (val) {
            if (Array.isArray(val)) {
                return val.length > 0;
            }
            return !!val;
        };

        this.validateSingleChecked = function (val) {
            return val === true;
        };

        this.validateSelect = function (val) {
            return val !== '' && val !== null && val !== undefined;
        };
    }
});