/**
 * WordPress Native User List Widget JavaScript
 * 
 * Manages search, filter, pagination, and infinite scroll functionality for the WP User List widget.
 * Organized into a modular class structure with enhanced error handling and meta field support.
 */
(($) => {
    'use strict';

    class WPUserList {
        /**
         * Default configuration
         */
        static config = {
            selectors: {
                container: '.wp-user-list-container',
                searchInput: '.wp-search-input',
                searchButton: '.wp-search-button',
                globalSearch: '.wp-global-search',
                filterToggle: '.wp-filter-toggle-btn',
                filterControl: '.wp-filter-control',
                applyFilters: '.wp-apply-filters',
                clearFilters: '.wp-clear-filters',
                paginationLink: '.wp-pagination-link',
                loadMoreButton: '.wp-load-more-button',
                infiniteScroll: '.wp-infinite-scroll-trigger',
                results: '.wp-filter-results',
                pagination: '.wp-pagination',
                spinner: '.wp-infinite-scroll-spinner'
            },
            defaults: {
                perPage: 12,
                debounceDelay: 500,
                defaultAvatar: 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&f=y'
            }
        };

        /**
         * Initialize the widget
         */
        static init() {
            $(document).ready(() => {
                this.bindEvents();
            });
        }

        /**
         * Bind all event listeners
         */
        static bindEvents() {
            const { selectors, defaults } = this.config;

            // Search input with debounce
            $(selectors.searchInput).on('input', this.debounce((e) => {
                const $input = $(e.target);
                this.performSearch($input.val(), this.getWidgetId($input));
            }, defaults.debounceDelay));

            // Search button
            $(selectors.searchButton).on('click', (e) => {
                const $button = $(e.target);
                const searchTerm = $button.siblings(selectors.searchInput).val();
                this.performSearch(searchTerm, this.getWidgetId($button));
            });

            // Global search
            $(selectors.globalSearch).on('input', this.debounce((e) => {
                const $input = $(e.target);
                this.performSearch($input.val(), this.getWidgetId($input));
            }, defaults.debounceDelay));

            // Filter toggle
            $(document).on('click', selectors.filterToggle, (e) => {
                this.toggleFilterPanel($(e.currentTarget));
            });

            // Filter controls
            $(document).on('change', selectors.filterControl, (e) => {
                this.applyFilters(this.getWidgetId($(e.target)));
            });

            // Apply filters button
            $(document).on('click', selectors.applyFilters, (e) => {
                this.applyFilters(this.getWidgetId($(e.target)));
            });

            // Clear filters
            $(document).on('click', selectors.clearFilters, (e) => {
                const $button = $(e.currentTarget);
                this.clearFilters($button.closest(selectors.container), this.getWidgetId($button));
            });

            // Pagination
            $(document).on('click', selectors.paginationLink, (e) => {
                e.preventDefault();
                const $link = $(e.currentTarget);
                this.loadPage($link.data('page'), this.getWidgetId($link));
            });

            // Load more
            $(document).on('click', selectors.loadMoreButton, (e) => {
                const $button = $(e.currentTarget);
                this.loadMoreUsers($button.data('page'), this.getWidgetId($button));
            });

            // Infinite scroll
            $(window).on('scroll', this.debounce(() => {
                $(selectors.infiniteScroll).each((_, el) => {
                    if (this.isElementInViewport($(el))) {
                        this.loadMoreUsers($(el).data('page'), $(el).data('widget-id'));
                    }
                });
            }, 100));
        }

        /**
         * Get widget ID from element
         * @param {jQuery} $element
         * @returns {string}
         */
        static getWidgetId($element) {
            return $element.closest(this.config.selectors.container).data('widget-id') || '';
        }

        /**
         * Toggle filter panel visibility
         * @param {jQuery} $button
         */
        static toggleFilterPanel($button) {
            const $filterPanel = $button.closest('.wp-filter-interface').find('.wp-filter-panel');
            const $arrow = $button.find('.wp-filter-arrow');
            const isExpanded = $button.attr('aria-expanded') === 'true';

            $filterPanel.toggleClass('wp-filter-panel-hidden', isExpanded);
            $button.attr('aria-expanded', !isExpanded);
            $arrow.toggleClass('fa-chevron-down', isExpanded).toggleClass('fa-chevron-up', !isExpanded);
        }

        /**
         * Perform search
         * @param {string} searchTerm
         * @param {string} widgetId
         */
        static performSearch(searchTerm, widgetId) {
            if (!widgetId) {
                console.error('Widget ID not found');
                return;
            }

            const $container = $(`${this.config.selectors.container}[data-widget-id="${widgetId}"]`);
            const $results = $container.find(this.config.selectors.results);

            this.showLoading($results, 'Searching...');
            this.makeAjaxRequest({
                widgetId,
                search: searchTerm,
                filters: this.collectFilters($container),
                page: 1
            }, $container);
        }

        /**
         * Apply filters
         * @param {string} widgetId
         */
        static applyFilters(widgetId) {
            if (!widgetId) {
                console.error('Widget ID not found');
                return;
            }

            const $container = $(`${this.config.selectors.container}[data-widget-id="${widgetId}"]`);
            const $results = $container.find(this.config.selectors.results);
            const searchTerm = $container.find(this.config.selectors.globalSearch).val() || 
                             $container.find(this.config.selectors.searchInput).val() || '';

            this.showLoading($results, 'Applying filters...');
            this.makeAjaxRequest({
                widgetId,
                search: searchTerm,
                filters: this.collectFilters($container),
                page: 1
            }, $container);
        }

        /**
         * Clear all filters
         * @param {jQuery} $container
         * @param {string} widgetId
         */
        static clearFilters($container, widgetId) {
            $container.find(this.config.selectors.filterControl).each((_, el) => {
                const $el = $(el);
                if ($el.is('select')) {
                    $el.val('');
                } else if ($el.is('input[type="checkbox"], input[type="radio"]')) {
                    $el.prop('checked', false);
                }
            });

            $container.find(`${this.config.selectors.globalSearch}, ${this.config.selectors.searchInput}`).val('');
            this.applyFilters(widgetId);
        }

        /**
         * Load specific page
         * @param {number} page
         * @param {string} widgetId
         */
        static loadPage(page, widgetId) {
            if (!widgetId || isNaN(page)) {
                console.error('Invalid page or widget ID');
                return;
            }

            const $container = $(`${this.config.selectors.container}[data-widget-id="${widgetId}"]`);
            const $results = $container.find(this.config.selectors.results);

            this.showLoading($results, 'Loading...');
            this.makeAjaxRequest({
                widgetId,
                page,
                search: $container.find(this.config.selectors.globalSearch).val() || 
                       $container.find(this.config.selectors.searchInput).val() || '',
                filters: this.collectFilters($container)
            }, $container);
        }

        /**
         * Load more users
         * @param {number} currentPage
         * @param {string} widgetId
         */
        static loadMoreUsers(currentPage, widgetId) {
            if (!widgetId || isNaN(currentPage)) {
                console.error('Invalid page or widget ID');
                return;
            }

            const $container = $(`${this.config.selectors.container}[data-widget-id="${widgetId}"]`);
            const $loadMore = $container.find(this.config.selectors.loadMoreButton);
            const $infiniteScroll = $container.find(this.config.selectors.infiniteScroll);

            this.toggleLoadingState($loadMore, $infiniteScroll, true);
            this.makeAjaxRequest({
                widgetId,
                page: currentPage + 1,
                search: $container.find(this.config.selectors.globalSearch).val() || 
                       $container.find(this.config.selectors.searchInput).val() || '',
                filters: this.collectFilters($container)
            }, $container, true);
        }

        /**
         * Collect filter values
         * @param {jQuery} $container
         * @returns {Object}
         */
        static collectFilters($container) {
            const filters = {};
            $container.find(this.config.selectors.filterControl).each((_, el) => {
                const $el = $(el);
                const metaKey = $el.closest('.wp-filter-item').data('meta-key');
                const value = $el.val();

                if (value && value !== '') {
                    filters[metaKey] = filters[metaKey] || [];
                    filters[metaKey].push(value);
                }
            });
            return filters;
        }

        /**
         * Make AJAX request
         * @param {Object} params
         * @param {jQuery} $container
         * @param {boolean} append
         */
        static makeAjaxRequest(params, $container, append = false) {
            const { selectors, defaults } = this.config;
            const $results = $container.find(selectors.results);

            $.ajax({
                url: wp_user_list_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'wp_filter_users',
                    widget_id: params.widgetId,
                    post_id: parseInt(String(window['wp_elementor_ajax_' + params.widgetId]?.post_id ?? $container.data('post-id') ?? 0), 10) || 0,
                    page: params.page || 1,
                    per_page: $container.data('per-page') || defaults.perPage,
                    search: params.search || '',
                    filters: params.filters || {},
                    widget_settings: JSON.stringify(window['wp_elementor_ajax_' + params.widgetId]?.widget_settings || {}),
                    nonce: typeof wp_user_list_ajax !== 'undefined' ? wp_user_list_ajax.nonce : (window['wp_elementor_ajax_' + params.widgetId]?.nonce || '')
                },
                success: (response) => {
                    if (response.success) {
                        append ? 
                            this.appendUsers(response.data, $container) :
                            this.updateUserList(response.data, $container);
                        this.updatePagination(response.data, $container);
                        this.updateLoadMoreButton(response.data, $container);
                    } else {
                        $results.html(`<div class="wp-error">Error: ${response.data?.message || 'Unknown error'}</div>`);
                    }
                },
                error: () => {
                    $results.html('<div class="wp-error">An error occurred during the request.</div>');
                },
                complete: () => {
                    this.toggleLoadingState(
                        $container.find(selectors.loadMoreButton),
                        $container.find(selectors.infiniteScroll),
                        false
                    );
                }
            });
        }

        /**
         * Show loading state
         * @param {jQuery} $results
         * @param {string} message
         */
        static showLoading($results, message) {
            $results.html(`<div class="wp-loading">${message}</div>`);
        }

        /**
         * Toggle loading state for load more/infinite scroll
         * @param {jQuery} $loadMore
         * @param {jQuery} $infiniteScroll
         * @param {boolean} isLoading
         */
        static toggleLoadingState($loadMore, $infiniteScroll, isLoading) {
            if ($loadMore.length) {
                $loadMore.text(isLoading ? 'Loading...' : 'Load More Users')
                    .prop('disabled', isLoading);
            }
            if ($infiniteScroll.length) {
                $infiniteScroll.find(this.config.selectors.spinner).toggle(isLoading);
            }
        }

        /**
         * Update user list
         * @param {Object} data
         * @param {jQuery} $container
         */
        static updateUserList(data, $container) {
            const $results = $container.find(this.config.selectors.results);
            
            if (data.users?.length > 0) {
                const html = `<div class="wp-team-carousel-wrapper grid grid-cols-3">
                    ${data.users.map(user => this.generateUserCard(user, data.widget_settings)).join('')}
                </div>`;
                $results.html(html);
            } else {
                $results.html('<div class="wp-no-results">No users found.</div>');
            }
        }

        /**
         * Append users for load more
         * @param {Object} data
         * @param {jQuery} $container
         */
        static appendUsers(data, $container) {
            const $grid = $container.find('.wp-team-carousel-wrapper');
            if (data.users?.length > 0) {
                data.users.forEach(user => {
                    $grid.append(this.generateUserCard(user, data.widget_settings));
                });
            }
        }

        /**
         * Generate user card HTML
         * @param {Object} user
         * @param {Object} settings
         * @returns {string}
         */
        static generateUserCard(user, settings = {}) {
            const defaults = {
                um_elementor_show_user_image: 1,
                um_elementor_show_member_name: 1,
                um_elementor_show_user_meta: 1,
                um_elementor_user_meta_fields: ['first_name', 'last_name', 'user_email'],
                title_alignment: 'text-center',
                user_layout: 1
            };
            const config = { ...defaults, ...settings };
            const displayName = this.escapeHtml(user.display_name || user.user_login || 'Unknown User');
            const avatarUrl = user.user_avatar || this.config.defaults.defaultAvatar;
            const profileUrl = user.profile_url || '#';

            let html = '<div class="wp-item-user"><div class="um-widget-member-holder">';

            if (config.um_elementor_show_user_image) {
                html += `<div class="um-elementor-member-image ${config.title_alignment}">
                    <a title="${displayName}" href="${profileUrl}">
                        <img src="${avatarUrl}" alt="${displayName}" width="100" height="100">
                    </a></div>`;
            }

            html += '<div class="um-elementor-member-content">';

            if (config.um_elementor_show_member_name) {
                html += `<div class="um-elementor-member-name ${config.title_alignment}">
                    <p><a title="${displayName}" href="${profileUrl}">${displayName}</a></p>
                </div>`;
            }

            if (config.um_elementor_show_user_meta && user.user_meta && config.um_elementor_user_meta_fields?.length) {
                html += `<div class="um-custom-field-container ${config.title_alignment}">`;
                
                config.um_elementor_user_meta_fields.forEach(fieldKey => {
                    const value = user.user_meta[fieldKey];
                    if (value && value !== '') {
                        const label = this.formatFieldLabel(fieldKey);
                        html += `<span class="um-meta-field">
                            <span class="um-custom-label">${label}:</span> 
                            <span class="um-custom-field">${this.formatMetaValue(value, fieldKey)}</span>
                        </span>`;
                    }
                });

                html += '</div>';
            }

            return html + '</div></div></div>';
        }

        /**
         * Format field label
         * @param {string} fieldKey
         * @returns {string}
         */
        static formatFieldLabel(fieldKey) {
            return this.escapeHtml(fieldKey.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()));
        }

        /**
         * Format meta value based on field type
         * @param {string} value
         * @param {string} fieldKey
         * @returns {string}
         */
        static formatMetaValue(value, fieldKey) {
            const fieldTypes = {
                user_email: 'email',
                user_url: 'url',
                age: 'number',
                active: 'boolean'
            };
            const type = fieldTypes[fieldKey] || 'text';

            switch (type) {
                case 'email':
                    return `<a href="mailto:${value}">${this.escapeHtml(value)}</a>`;
                case 'url':
                    return `<a href="${value}" target="_blank">${this.escapeHtml(value)}</a>`;
                case 'number':
                    return parseFloat(value).toLocaleString();
                case 'boolean':
                    return value === '1' || value === true ? 'Yes' : 'No';
                default:
                    return this.escapeHtml(value);
            }
        }

        /**
         * Update pagination
         * @param {Object} data
         * @param {jQuery} $container
         */
        static updatePagination(data, $container) {
            const $pagination = $container.find(this.config.selectors.pagination);
            
            if (data.total_pages > 1) {
                let html = '';
                if (data.current_page > 1) {
                    html += `<a href="#" class="wp-pagination-link wp-pagination-prev" data-page="${data.current_page - 1}">Previous</a>`;
                }

                const startPage = Math.max(1, data.current_page - 2);
                const endPage = Math.min(data.total_pages, data.current_page + 2);

                for (let i = startPage; i <= endPage; i++) {
                    const currentClass = i === data.current_page ? 'current' : '';
                    html += `<a href="#" class="wp-pagination-link ${currentClass}" data-page="${i}">${i}</a>`;
                }

                if (data.current_page < data.total_pages) {
                    html += `<a href="#" class="wp-pagination-link wp-pagination-next" data-page="${data.current_page + 1}">Next</a>`;
                }

                $pagination.html(html);
            } else {
                $pagination.html('<span class="wp-pagination-single">Page 1 of 1</span>');
            }
        }

        /**
         * Update load more button
         * @param {Object} data
         * @param {jQuery} $container
         */
        static updateLoadMoreButton(data, $container) {
            const { selectors } = this.config;
            const $loadMore = $container.find(selectors.loadMoreButton);
            const $infiniteScroll = $container.find(selectors.infiniteScroll);

            if (data.current_page >= data.total_pages) {
                $loadMore.text('No More Users').prop('disabled', true);
                $infiniteScroll.hide();
            } else {
                $loadMore.data('page', data.current_page);
                $infiniteScroll.data('page', data.current_page);
            }
        }

        /**
         * Escape HTML to prevent XSS
         * @param {string} str
         * @returns {string}
         */
        static escapeHtml(str) {
            return str.replace(/[&<>"']/g, m => ({
                '&': '&amp;',
                '<': '&lt;',
                '>': '&gt;',
                '"': '&quot;',
                "'": '&#039;'
            }[m]));
        }

        /**
         * Debounce function
         * @param {Function} func
         * @param {number} wait
         * @returns {Function}
         */
        static debounce(func, wait) {
            let timeout;
            return (...args) => {
                clearTimeout(timeout);
                timeout = setTimeout(() => func(...args), wait);
            };
        }

        /**
         * Check if element is in viewport
         * @param {jQuery} $el
         * @returns {boolean}
         */
        static isElementInViewport($el) {
            const rect = $el[0].getBoundingClientRect();
            return (
                rect.top >= 0 &&
                rect.left >= 0 &&
                rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
                rect.right <= (window.innerWidth || document.documentElement.clientWidth)
            );
        }
    }

    // Initialize the widget
    WPUserList.init();

})(jQuery);