// Filename: frontend/js/pages/keys/apiKeyList.js

import { apiKeyManager } from '../../components/apiKeyManager.js';
import { taskCenterManager, toastManager } from '../../components/taskCenter.js';
import { debounce, escapeHTML } from '../../utils/utils.js';
import { handleApiError } from '../../services/errorHandler.js'; 

/**
 * Manages the UI and state for the API Key list within a selected group.
 * This class handles fetching, rendering, and all user interactions with the API key cards.
 */
class ApiKeyList {
    /**
     * @param {HTMLElement} container - The DOM element that will contain the API key list.
     */
    constructor(container) {
        if (!container) {
            throw new Error("ApiKeyListManager requires a valid container element.");
        }

        this.elements = {
            container: container, // This is the scrollable list container now, e.g., #api-list-container
            gridContainer: null,  // Will be dynamically created inside container
            paginationContainer: document.querySelector('.pagination-controls'), // Find the pagination container
            itemsPerPageSelect: document.querySelector('.items-per-page-select'), // Find the dropdown
            selectAllCheckbox: document.getElementById('select-all'),
            batchActionButton: document.querySelector('.batch-action-btn'), // The trigger button
            batchActionPanel: document.querySelector('.batch-action-panel'), // The dropdown panel
            statusFilterSelects: document.querySelectorAll('.status-filter-select'),
            desktopSearchInput: document.getElementById('desktop-search-input'),
            mobileSearchBtn: document.getElementById('mobile-search-btn'),
            desktopQuickActionsPanel: document.getElementById('desktop-quick-actions-panel'),
            mobileQuickActionsPanel: document.getElementById('mobile-quick-actions-panel'),
            desktopMultifunctionPanel: document.getElementById('desktop-multifunction-panel'),
            mobileMultifunctionPanel: document.getElementById('mobile-multifunction-panel'),
        };
        this.state = {
            currentKeys: [], // Now holds only the keys for the current page
            selectedKeyIds: new Set(), 
            isApiKeysLoading: false,
            activeGroupId: null, 
            activeGroupName: '',
            currentPage: 1,
            itemsPerPage: 20, // Default value, will be updated from select
            totalItems: 0,
            totalPages: 1,
            filterStatus: 'all', 
            searchText: '',
        };
        this.debouncedSearch = debounce(() => {
            this.state.currentPage = 1; // Reset to page 1 for new search
            this.loadApiKeys(this.state.activeGroupId, true);
        }, 300);
        this.boundListeners = {
            handleContainerClick: this._handleContainerClick.bind(this),
            handlePaginationClick: this.handlePaginationClick.bind(this),
            handleItemsPerPageChange: this._handleItemsPerPageChange.bind(this),
            handleSelectAllChange: this._handleSelectAllChange.bind(this),
            handleStatusFilterChange: this._handleStatusFilterChange.bind(this),
            handleBatchActionClick: this._handleBatchActionClick.bind(this),
            handleDocumentClickForMenuClose: this._handleDocumentClickForMenuClose.bind(this),
            handleSearchInput: this._handleSearchInput.bind(this),
            handleSearchEnter: this._handleSearchEnter.bind(this),
            showMobileSearchModal: this._showMobileSearchModal.bind(this),
            handleGlobalClick: this._handleGlobalClick.bind(this)
        };

    }

    init() {
        if (!this.elements.container) return;
        this.elements.container.addEventListener('click', this.boundListeners.handleContainerClick);
        this.elements.paginationContainer?.addEventListener('click', this.boundListeners.handlePaginationClick);
        this.elements.selectAllCheckbox?.addEventListener('change', this.boundListeners.handleSelectAllChange);
        this.elements.batchActionPanel?.addEventListener('click', this.boundListeners.handleBatchActionClick);
        this.elements.desktopSearchInput?.addEventListener('input', this.boundListeners.handleSearchInput);
        this.elements.desktopSearchInput?.addEventListener('keydown', this.boundListeners.handleSearchEnter);
        this.elements.mobileSearchBtn?.addEventListener('click', this.boundListeners.showMobileSearchModal);
        document.addEventListener('click', this.boundListeners.handleDocumentClickForMenuClose);
        document.body.addEventListener('click', this.boundListeners.handleGlobalClick);
        const itemsPerPageSelect = this.elements.itemsPerPageSelect?.querySelector('select');
        if (itemsPerPageSelect) {
            itemsPerPageSelect.addEventListener('change', this.boundListeners.handleItemsPerPageChange);
            this.state.itemsPerPage = parseInt(itemsPerPageSelect.value, 10);
        }
        this.elements.statusFilterSelects.forEach(selectContainer => {
            const actualSelect = selectContainer.querySelector('select');
            actualSelect?.addEventListener('change', this.boundListeners.handleStatusFilterChange);
        });
        
        this._renderMultifunctionMenu();
        this._renderQuickActionsMenu();
    }

    destroy() {
        console.log("Destroying ApiKeyList instance and cleaning up listeners.");
        this.elements.container.removeEventListener('click', this.boundListeners.handleContainerClick);
        this.elements.paginationContainer?.removeEventListener('click', this.boundListeners.handlePaginationClick);
        this.elements.selectAllCheckbox?.removeEventListener('change', this.boundListeners.handleSelectAllChange);
        this.elements.batchActionPanel?.removeEventListener('click', this.boundListeners.handleBatchActionClick);
        this.elements.desktopSearchInput?.removeEventListener('input', this.boundListeners.handleSearchInput);
        this.elements.desktopSearchInput?.removeEventListener('keydown', this.boundListeners.handleSearchEnter);
        this.elements.mobileSearchBtn?.removeEventListener('click', this.boundListeners.showMobileSearchModal);
        document.removeEventListener('click', this.boundListeners.handleDocumentClickForMenuClose);
        document.body.removeEventListener('click', this.boundListeners.handleGlobalClick);

        const itemsPerPageSelect = this.elements.itemsPerPageSelect?.querySelector('select');
        if (itemsPerPageSelect) {
            itemsPerPageSelect.removeEventListener('change', this.boundListeners.handleItemsPerPageChange);
        }
        
        this.elements.statusFilterSelects.forEach(selectContainer => {
            const actualSelect = selectContainer.querySelector('select');
            actualSelect?.removeEventListener('change', this.boundListeners.handleStatusFilterChange);
        });

        this.debouncedSearch.cancel?.();

        this.elements.container.innerHTML = '';
    }

    _handleItemsPerPageChange(e) {
        this.state.itemsPerPage = parseInt(e.target.value, 10);
        this.state.currentPage = 1;
        this.loadApiKeys(this.state.activeGroupId, true);
    }
    
    _handleStatusFilterChange(e) {
        this.state.filterStatus = e.target.value;
        this.state.currentPage = 1;
        this.loadApiKeys(this.state.activeGroupId, true);
    }
    _handleDocumentClickForMenuClose(event) {
        if (!event.target.closest('.api-card')) {
            this._closeAllActionMenus();
        }
    }

    _handleGlobalClick(event) {
    this._handleQuickActionClick(event);
    this._handleMultifunctionMenuClick(event);
    this._handleDropdownToggle(event);
    // 其他可能需要全局监听的点击事件...
    }
    /**
     * Updates the active group context for the manager.
     * @param {number} groupId The new active group ID.
     */
    setActiveGroup(groupId, groupName) { // Assuming groupName comes from groupList now
        this.state.activeGroupId = groupId;
        this.state.activeGroupName = groupName || '';
        this.state.currentPage = 1; // Reset to page 1 whenever group changes
    }

    /**
     * Fetches and renders API keys for the specified group.
     * @param {number} groupId - The ID of the group to load keys for.
     * @param {boolean} [force=false] - If true, bypasses the cache and fetches from the server.
     */
    async loadApiKeys(groupId, force = false) {
        this.state.selectedKeyIds.clear();
        if (!groupId) {
            this.state.currentKeys = [];
            this.render();
            return;
        }
        this.state.isApiKeysLoading = true;
        this.render(); 
        try {
            const { currentPage, itemsPerPage, filterStatus, searchText } = this.state;
            const params = {
            page: currentPage,
            limit: itemsPerPage,
        };
            if (filterStatus !== 'all') {
            params.status = filterStatus;
        }

        if (searchText && searchText.trim() !== '') {
            params.keyword = searchText.trim();
        }

        const pageData = await apiKeyManager.getKeysForGroup(groupId, params);
            // Update state with new pagination info from the API response
            this.state.currentKeys = pageData.items;
            this.state.totalItems = pageData.total;
            this.state.totalPages = pageData.pages;
            this.state.currentPage = pageData.page;
        } catch (error) {
            toastManager.show(`加载API Keys失败: ${error.message || '未知错误'}`, 'error');
            this.state.currentKeys = [];
            this.state.totalItems = 0;
            this.state.totalPages = 1;
        } finally {
            this.state.isApiKeysLoading = false;
            this.render();
        }
    }

    /**
     * Renders the list of API keys based on the current state.
     */
    render() {
        if (this.state.isApiKeysLoading && this.state.currentKeys.length === 0) {
            this.elements.container.innerHTML = '<div class="flex items-center justify-center h-full text-zinc-500"><p>正在加载 API Keys...</p></div>';
            if (this.elements.paginationContainer) this.elements.paginationContainer.innerHTML = '';
            return;
        }
        if (!this.state.activeGroupId) {
             this.elements.container.innerHTML = '<div class="flex items-center justify-center h-full text-zinc-500"><p>请先在左侧选择一个分组</p></div>';
             if (this.elements.paginationContainer) this.elements.paginationContainer.innerHTML = '';
             return;
        }
        if (this.state.currentKeys.length === 0) {
            this.elements.container.innerHTML = '<div class="flex items-center justify-center h-full text-zinc-500"><p>该分组下还没有 API Key。</p></div>';
            if (this.elements.paginationContainer) this.elements.paginationContainer.innerHTML = '';
            return;
        }
        // Render the list of keys
        const listHtml = this.state.currentKeys.map(apiKey => this._createApiKeyCardHtml(apiKey)).join('');
        this.elements.container.innerHTML = `<div class="grid grid-cols-1 md:grid-cols-2 gap-3">${listHtml}</div>`;
        this.elements.gridContainer = this.elements.container.firstChild; // Reference the grid
        // Render the pagination controls
        if (this.elements.paginationContainer) {
            this.elements.paginationContainer.innerHTML = this._createPaginationHtml();
        }
        this._updateAllStatusIndicators();
        this._syncCardCheckboxes();
        this._syncSelectionUI();
    }

    // [NEW] Handles clicks on pagination buttons
    handlePaginationClick(event) {
        const button = event.target.closest('button[data-page]');
        if (!button || button.disabled) return;
        const newPage = parseInt(button.dataset.page, 10);
        if (newPage !== this.state.currentPage) {
            this.state.currentPage = newPage;
            this.loadApiKeys(this.state.activeGroupId, true);
        }
    }
    // [NEW] Generates the HTML for the pagination controls
    _createPaginationHtml() {
        const { currentPage, totalPages } = this.state;
        if (totalPages < 1) return '';
        const baseButtonClasses = "pagination-button px-3 py-1 rounded text-sm transition-colors duration-150 ease-in-out disabled:opacity-50 disabled:cursor-not-allowed";
        const activeClasses = "bg-zinc-500 text-white font-semibold";
        const inactiveClasses = "hover:bg-zinc-200 dark:hover:bg-zinc-700";
        let html = '';
        const prevDisabled = currentPage <= 1 ? 'disabled' : '';
        const nextDisabled = currentPage >= totalPages ? 'disabled' : '';
        // Previous button
        html += `<button class="${baseButtonClasses} ${inactiveClasses}" data-page="${currentPage - 1}" ${prevDisabled}>
                    <i class="fas fa-chevron-left"></i>
                </button>`;
        // Page number buttons
        const pagesToShow = this._getPaginationPages(currentPage, totalPages);
        pagesToShow.forEach(page => {
            if (page === '...') {
                html += `<span class="px-3 py-1 text-zinc-400 dark:text-zinc-500 text-sm">...</span>`;
            } else {
                const pageClasses = page === currentPage ? activeClasses : inactiveClasses;
                html += `<button class="${baseButtonClasses} ${pageClasses}" data-page="${page}">${page}</button>`;
            }
        });
        html += `<button class="${baseButtonClasses} ${inactiveClasses}" data-page="${currentPage + 1}" ${nextDisabled}>
                    <i class="fas fa-chevron-right"></i>
                </button>`;
        return html;
    }
    // [NEW] Helper to determine which page numbers to show in pagination (e.g., 1 ... 5 6 7 ... 12)
    _getPaginationPages(current, total, width = 2) {
        if (total <= (width * 2) + 3) {
            return Array.from({ length: total }, (_, i) => i + 1);
        }
        const pages = [1];
        if (current > width + 2) pages.push('...');
        
        for (let i = Math.max(2, current - width); i <= Math.min(total - 1, current + width); i++) {
            pages.push(i);
        }
        if (current < total - width - 1) pages.push('...');
        pages.push(total);
        return pages;
    }

    /**
     * Handles all actions originating from within an API key card.
     * @param {Event} event - The click event.
     */
    async handleCardAction(event) {
        const button = event.target.closest('button[data-action]');
        if (!button) return;
        const action = button.dataset.action;
        const card = button.closest('.api-card');
        if (!card) return;

        // 专门处理移动端菜单的显示/隐藏逻辑
        if (action === 'toggle-menu') {
            const menu = card.querySelector('[data-menu="actions"]');
            if (menu) {
                const isMenuOpen = !menu.classList.contains('hidden');
                this._closeAllActionMenus(card); // 关闭其他菜单
                if (!isMenuOpen) {
                    menu.classList.remove('hidden'); // 打开当前菜单
                }
            }
            return;
        }
        this._closeAllActionMenus();
        const keyId = parseInt(card.dataset.keyId, 10);
        const groupId = this.state.activeGroupId;

        // 如果是移动端菜单里的操作，执行后需要关闭菜单
        const menuToClose = card.querySelector('[data-menu="actions"]');
        if (menuToClose && !menuToClose.classList.contains('hidden')) {
            this._closeAllActionMenus();
        }

        const apiKeyData = this.state.currentKeys.find(key => key.id === keyId);
        if (!apiKeyData) {
            toastManager.show('错误: 找不到该Key的数据。可能是列表已过期，请尝试刷新。', 'error');
            return;
        }
        const fullApiKey = apiKeyData.api_key;

        switch (action) {
            case 'toggle-visibility':
            case 'copy-key':
                this._handleLocalCardActions(action, button, card, fullApiKey);
                break;

            case 'set-status': {
                const newStatus = button.dataset.newStatus;
                if (!newStatus) return;
                try {
                    await apiKeyManager.updateKeyStatusInGroup(groupId, keyId, newStatus);
                    toastManager.show(`Key 状态已成功更新为 ${newStatus}`, 'success');
                } catch (error) {
                    handleApiError(error, toastManager);
                }finally {
                 await this.loadApiKeys(groupId, true);
                }
                break;
            }
            
            case 'revalidate': {
                const revalidateTask = this._createRevalidateTaskDefinition(groupId, fullApiKey);
                taskCenterManager.startTask(revalidateTask);
                break;
            }
            
            case 'delete-key': {
            const result = await Swal.fire({
                target: '#main-content-wrapper',
                width: '20rem',
                backdrop: `rgba(0,0,0,0.5)`,
                heightAuto: false,
                customClass: {
                    popup: `swal2-custom-style ${document.documentElement.classList.contains('dark') ? 'swal2-dark' : ''}`
                },
                
                title: '确认删除',
                html: `确定要从 <b>当前分组</b> 中移除这个Key吗？`,
                //icon: 'warning',
                
                showCancelButton: true,
                confirmButtonText: '确认',
                cancelButtonText: '取消',
                reverseButtons: true,
                confirmButtonColor: '#d33', 
                cancelButtonColor: '#6b7280', 
                focusConfirm: false,
                focusCancel: false,

            });
            if (!result.isConfirmed) {
                return;
            }
            try {
                const unlinkResult = await apiKeyManager.unlinkKeysFromGroup(groupId, [fullApiKey]);
                if (!unlinkResult.success) { 
                    throw new Error(unlinkResult.message || '后端未能移除Key。');
                }
                toastManager.show(`成功移除 1 个Key。`, 'success');
                await this.loadApiKeys(groupId, true);
            } catch (error) {
                const errorMessage = error && error.message ? error.message : ERROR_MESSAGES['DEFAULT'];
                toastManager.show(`移除Key失败: ${errorMessage}`, 'error');
                await this.loadApiKeys(groupId, true);
            }
            break;
            }
        }
    }
    
    // --- Private Helper Methods (copied from original file) ---

    _createRevalidateTaskDefinition(groupId, fullApiKey) {
        return {
            start: () => apiKeyManager.revalidateKeys(groupId, [fullApiKey]),
            poll: (taskId) => apiKeyManager.getTaskStatus(taskId, { noCache: true }),
            onSuccess: (data) => {
                toastManager.show(`Key验证完成`, 'success');
                this.loadApiKeys(groupId, true);
            },
            onError: (data) => {
                toastManager.show(`验证任务失败: ${data.error || '未知错误'}`, 'error');
            },
            renderToastNarrative: (data, oldData, toastManager) => {
                const toastId = `task-${data.id}`;
                const progress = data.total > 0 ? (data.processed / data.total) * 100 : 0;
                toastManager.showProgressToast(toastId, `正在验证Key`, '处理中', progress);
            },
            
            renderTaskCenterItem: (data, timestamp, formatTimeAgo) => {
                const timeAgo = formatTimeAgo(timestamp);
                const maskedKey = escapeHTML(`${fullApiKey.substring(0, 4)}...${fullApiKey.substring(fullApiKey.length - 4)}`);
                let contentHtml = '';
                const isDone = !data.is_running;
                
                if (isDone) {
                    // --- Task is complete ---
                    if (data.error) {
                        // --- Case 1: The task itself failed to run ---
                        const safeError = escapeHTML(data.error);
                        contentHtml = `
                            <div class="task-item-main">
                                <div class="task-item-icon-summary text-red-500"><i class="fas fa-exclamation-triangle"></i></div>
                                <div class="task-item-content flex-grow">
                                    <p class="task-item-title">验证任务出错: ${maskedKey}</p>
                                    <p class="task-item-status text-red-500 truncate" title="${safeError}">${safeError}</p>
                                </div>
                            </div>`;
                    } else {
                        // --- Case 2: The task ran, and we have results ---
                        const result = data.result?.results?.[0]; // Get the first (and only) result
                        const isSuccess = result?.status === 'valid';
                        const iconClass = isSuccess ? 'text-green-500 fas fa-check-circle' : 'text-red-500 fas fa-times-circle';
                        const title = isSuccess ? '验证成功' : '验证失败';
                        // Safely get the message, providing a default if it's missing.
                        const safeMessage = escapeHTML(result?.message || '没有详细信息。');
                        contentHtml = `
                            <div class="task-item-main">
                                <div class="task-item-icon-summary"><i class="${iconClass}"></i></div>
                                <div class="task-item-content flex-grow">
                                    <p class="task-item-title">${title}: ${maskedKey}</p>
                                    <p class="task-item-status truncate" title="${safeMessage}">${safeMessage}</p>
                                </div>
                            </div>
                        `;
                    }
                } else {
                    // --- Case 3: Task is still running ---
                    contentHtml = `
                        <div class="task-item-main gap-3">
                            <div class="task-item-icon task-item-icon-running"><i class="fas fa-spinner animate-spin"></i></div>
                            <div class="task-item-content flex-grow">
                                <p class="task-item-title">正在验证: ${maskedKey}</p>
                                <p class="task-item-status">运行中... (${data.processed}/${data.total})</p>
                            </div>
                        </div>`;
                }
                return `${contentHtml}<div class="task-item-timestamp">${timeAgo}</div>`;
            },
        };
    }

       /**
     * Creates the HTML for a single API key card, adapting to the flat APIKeyDetails structure.
     * @param {object} item - The APIKeyDetails object from the API.
     * @returns {string} The HTML string for the card.
     */
   _createApiKeyCardHtml(item) {
       if (!item || !item.api_key) return ''; // 安全检查

       // --- 数据准备 ---
       const maskedKey = escapeHTML(`${item.api_key.substring(0, 4)}......${item.api_key.substring(item.api_key.length - 4)}`);
       const status = escapeHTML(item.status);
       const errorCount = escapeHTML(item.consecutive_error_count); 
       const keyId = escapeHTML(item.id);
       const mappingId = escapeHTML(`${item.api_key_id}-${item.key_group_id}`); 

       // --- 行为挂钩 ---
       const setActiveAction = `data-action="set-status" data-new-status="ACTIVE"`;
       const revalidateAction = `data-action="revalidate"`;
       const disableAction = `data-action="set-status" data-new-status="DISABLED"`;
       const deleteAction = `data-action="delete-key"`;

       // --- 模板渲染 ---
       return `
            <div class="api-card group relative flex items-center gap-x-3 rounded-lg p-3 bg-white dark:bg-zinc-800/50 border border-zinc-200 dark:border-zinc-700/60" 
                data-status="${status}" 
                data-key-id="${keyId}" 
                data-mapping-id="${mappingId}">
                <input type="checkbox" class="api-key-checkbox h-4 w-4 rounded border-zinc-300 text-blue-600 focus:ring-blue-500 shrink-0">
                <span data-status-indicator class="w-2 h-2 rounded-full shrink-0"></span>
                <div class="flex-grow min-w-0">
                    <p class="font-mono text-xs font-semibold truncate">${maskedKey}</p>
                    <p class="text-xs text-zinc-400 mt-1">失败: ${errorCount} 次</p>
                </div>
                <!-- [DESKTOP ONLY] 快速操作按钮 - 在移动端(<lg)隐藏 -->
                <div class="hidden lg:flex items-center gap-x-2 text-zinc-400 text-xs z-10">
                    <button class="hover:text-blue-500" data-action="toggle-visibility" title="查看完整Key"><i class="fas fa-eye"></i></button>
                    <button class="hover:text-blue-500" data-action="copy-key" title="复制Key"><i class="fas fa-copy"></i></button>
                </div>
                <!-- [DESKTOP ONLY] Hover Menu - 在移动端(<lg)隐藏 -->
                <div class="hidden lg:flex absolute right-14 top-1/2 -translate-y-1/2 items-center bg-zinc-200 dark:bg-zinc-700 rounded-full shadow-md opacity-0 lg:group-hover:opacity-100 transition-opacity duration-200 z-20">
                    <button class="px-2 py-1 hover:text-green-500" ${setActiveAction} title="设为可用"><i class="fas fa-check-circle"></i></button>
                    <button class="px-2 py-1 hover:text-blue-500" ${revalidateAction} title="重新验证"><i class="fas fa-sync-alt"></i></button>
                    <button class="px-2 py-1 hover:text-yellow-500" ${disableAction} title="禁用"><i class="fas fa-ban"></i></button>
                    <button class="px-2 py-1 hover:text-red-500" ${deleteAction} title="从分组中移除"><i class="fas fa-trash-alt"></i></button>
                </div>
                <!-- [MOBILE ONLY] Kebab Menu and Dropdown - 在桌面端(>=lg)隐藏 -->
                <div class="relative lg:hidden">
                    <button data-action="toggle-menu" class="flex items-center justify-center h-8 w-8 rounded-full hover:bg-zinc-100 dark:hover:bg-zinc-700 text-zinc-500" title="更多操作">
                        <i class="fas fa-ellipsis-v"></i>
                    </button>
                    <div data-menu="actions" class="absolute right-0 mt-2 w-48 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-md shadow-lg z-30 hidden">
                        <!-- [NEW] "查看"和"复制"操作已移入移动端菜单 -->
                        <button class="w-full text-left px-4 py-2 text-sm text-zinc-700 dark:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-x-3" data-action="toggle-visibility"><i class="fas fa-eye w-4"></i> 查看/隐藏 Key</button>
                        <button class="w-full text-left px-4 py-2 text-sm text-zinc-700 dark:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-x-3" data-action="copy-key"><i class="fas fa-copy w-4"></i> 复制 Key</button>
                        <div class="border-t border-zinc-200 dark:border-zinc-700 my-1"></div>
                        <button class="w-full text-left px-4 py-2 text-sm text-green-600 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-x-3" ${setActiveAction}><i class="fas fa-check-circle w-4"></i> 设为可用</button>
                        <button class="w-full text-left px-4 py-2 text-sm text-blue-600 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-x-3" ${revalidateAction}><i class="fas fa-sync-alt w-4"></i> 重新验证</button>
                        <button class="w-full text-left px-4 py-2 text-sm text-yellow-600 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-x-3" ${disableAction}><i class="fas fa-ban w-4"></i> 禁用</button>
                        <div class="border-t border-zinc-200 dark:border-zinc-700 my-1"></div>
                        <button class="w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-x-3" ${deleteAction}><i class="fas fa-trash-alt w-4"></i> 从分组中移除</button>
                    </div>
                </div>
            </div>
        `;
    }

    _handleLocalCardActions(action, button, card, fullApiKey) {
        switch (action) {
            case 'toggle-visibility': {
                const safeApiKey = escapeHTML(fullApiKey);
                    Swal.fire({
                        target: '#main-content-wrapper',
                        width: '24rem', // 适配移动端宽度
                        backdrop: `rgba(0,0,0,0.5)`,
                        heightAuto: false,
                        
                        customClass: {
                            popup: `swal2-custom-style rounded-xl ${document.documentElement.classList.contains('dark') ? 'swal2-dark' : ''}`,
                            htmlContainer: 'm-0 text-left', // 移除默认边距
                        },
                        
                        showConfirmButton: false,
                        showCloseButton: false,
                        html: `
                            <div class="mt-2 flex items-center justify-between rounded-md bg-zinc-100 dark:bg-zinc-700 p-3 font-mono text-sm text-zinc-800 dark:text-zinc-200">
                                <code class="break-all">${safeApiKey}</code>
                                <button id="swal-copy-key-btn" class="ml-4 p-2 rounded-md hover:bg-zinc-200 dark:hover:bg-zinc-600 text-zinc-500 dark:text-zinc-300" title="复制">
                                    <i class="far fa-copy"></i>
                                </button>
                            </div>
                        `,
                        didOpen: (modal) => {
                            const copyBtn = modal.querySelector('#swal-copy-key-btn');
                            if (copyBtn) {
                                copyBtn.addEventListener('click', () => {
                                    navigator.clipboard.writeText(fullApiKey).then(() => {
                                        toastManager.show('API Key 已复制到剪贴板。', 'success');
                                        copyBtn.innerHTML = '<i class="fas fa-check text-green-500"></i>';
                                        setTimeout(() => {
                                            copyBtn.innerHTML = '<i class="far fa-copy"></i>';
                                        }, 1500);
                                    }).catch(err => {
                                        toastManager.show(`复制失败: ${err.message}`, 'error');
                                    });
                                });
                            }
                        },
                    });
                break; 
            }
            case 'copy-key': {
                if (!fullApiKey) {
                    toastManager.show('无法找到完整的Key用于复制。', 'error');
                    break;
                }
                navigator.clipboard.writeText(fullApiKey).then(() => {
                    toastManager.show('API Key 已复制到剪贴板。', 'success');
                }).catch(err => {
                    toastManager.show(`复制失败: ${err.message}`, 'error');
                });
                break;
            }
        }
    }
    
    _updateAllStatusIndicators() {
        const allCards = this.elements.container.querySelectorAll('.api-card[data-status]');
        allCards.forEach(card => this._updateApiKeyStatusIndicator(card));
    }

    _updateApiKeyStatusIndicator(cardElement) {
        const status = cardElement.dataset.status;
        if (!status) return;
        const indicator = cardElement.querySelector('[data-status-indicator]');
        if (!indicator) return;
        const statusColors = {
            'ACTIVE': 'bg-green-500',
            'PENDING': 'bg-gray-400',
            'COOLDOWN': 'bg-yellow-500',
            'DISABLED': 'bg-orange-500',
            'BANNED': 'bg-red-500',
        };
        
        // A more robust way to remove old classes
        Object.values(statusColors).forEach(colorClass => indicator.classList.remove(colorClass));
        
        if (statusColors[status]) {
            indicator.classList.add(statusColors[status]);
        }
    }
    /**
     * [NEW HELPER] Closes all action menus, optionally ignoring one card.
     * @param {HTMLElement} [ignoreCard=null] - The card whose menu should not be closed.
     */
    _closeAllActionMenus(ignoreCard = null) {
        this.elements.container.querySelectorAll('.api-card').forEach(card => {
            if (card === ignoreCard) return;
            const menu = card.querySelector('[data-menu="actions"]');
            if (menu) {
                menu.classList.add('hidden');
            }
        });
    }
    /**
     * [NEW] A central click handler for the entire list container.
     * It delegates clicks on checkboxes or action buttons to specific handlers.
     */
    _handleContainerClick(event) {
        const target = event.target;
        // Case 1: A checkbox was clicked
        if (target.matches('.api-key-checkbox')) {
            this._handleSelectionChange(target);
            return;
        }
        // Case 2: An action button (e.g., copy, delete) was clicked
        const actionButton = target.closest('button[data-action]');
        if (actionButton) {
            // We pass the original event object to handleCardAction
            this.handleCardAction(event);
            return;
        }
    }
    /**
     * [NEW] Handles a click on an individual API key's checkbox.
     * @param {HTMLInputElement} checkbox - The checkbox element that was clicked.
     */
    _handleSelectionChange(checkbox) {
        const card = checkbox.closest('.api-card');
        if (!card) return;
        const keyId = parseInt(card.dataset.keyId, 10);
        if (isNaN(keyId)) return;
        if (checkbox.checked) {
            this.state.selectedKeyIds.add(keyId);
        } else {
            this.state.selectedKeyIds.delete(keyId);
        }
        this._syncSelectionUI();
    }
    /**
     * [NEW] Synchronizes the UI elements based on the current selection state.
     * This includes the "Select All" checkbox and batch action buttons.
     */
    _syncSelectionUI() {
        if (!this.elements.selectAllCheckbox || !this.elements.batchActionButton) return;
        const selectedCount = this.state.selectedKeyIds.size;
        const visibleKeysCount = this.state.currentKeys.length;
        // Update the main "Select All" checkbox state
        if (selectedCount === 0) {
            this.elements.selectAllCheckbox.checked = false;
            this.elements.selectAllCheckbox.indeterminate = false;
        } else if (selectedCount < visibleKeysCount) {
            this.elements.selectAllCheckbox.checked = false;
            this.elements.selectAllCheckbox.indeterminate = true;
        } else if (selectedCount === visibleKeysCount && visibleKeysCount > 0) {
            this.elements.selectAllCheckbox.checked = true;
            this.elements.selectAllCheckbox.indeterminate = false;
        }
        // Enable or disable batch action buttons
        const isDisabled = selectedCount === 0;
        if (isDisabled) {
            this.elements.batchActionButton.classList.add('is-disabled');
        } else {
            this.elements.batchActionButton.classList.remove('is-disabled');
        }
        
        // Optionally, update a counter in the UI
        this.elements.batchActionButton.style.pointerEvents = isDisabled ? 'none' : 'auto';
        this.elements.batchActionButton.style.opacity = isDisabled ? '0.5' : '1';
        const counter = this.elements.batchActionButton.querySelector('span');
        if (counter) {
            counter.textContent = isDisabled ? '批量操作' : `已选 ${selectedCount} 项`;
        }
    }
    /**
     * [NEW] Handles the change event of the main "Select All" checkbox.
     * @param {Event} event - The change event object.
     */
    _handleSelectAllChange(event) {
        const isChecked = event.target.checked;
        
        // Update the state based on all currently visible keys
        this.state.currentKeys.forEach(key => {
            if (isChecked) {
                this.state.selectedKeyIds.add(key.id);
            } else {
                this.state.selectedKeyIds.delete(key.id);
            }
        });
        // Sync the entire UI to reflect the new state
        this._syncCardCheckboxes();
        this._syncSelectionUI();
    }
    /**
     * [NEW] Ensures that the checked status of each individual card's checkbox
     * matches the selection state stored in `this.state.selectedKeyIds`.
     */
    _syncCardCheckboxes() {
        if (!this.elements.gridContainer) return;
        const checkboxes = this.elements.gridContainer.querySelectorAll('.api-key-checkbox');
        checkboxes.forEach(checkbox => {
            const card = checkbox.closest('.api-card');
            if (card) {
                const keyId = parseInt(card.dataset.keyId, 10);
                if (!isNaN(keyId)) {
                    checkbox.checked = this.state.selectedKeyIds.has(keyId);
                }
            }
        });
    }

    /**
     * [NEW] Handles clicks within the batch action dropdown panel.
     * Uses event delegation to determine which action was triggered.
     * This version is adapted for the final HTML structure.
     * @param {Event} event - The click event.
     */
    _handleBatchActionClick(event) {
        // [MODIFIED] The selector is the same, but we are now certain it's a button.
        const button = event.target.closest('button[data-batch-action]');
        if (!button) return;
        event.preventDefault(); 
        
        // Auto-close the custom select panel after an action.
        const customSelectContainer = button.closest('.custom-select');
        if (customSelectContainer) {
            const panel = customSelectContainer.querySelector('.custom-select-panel');
            if (panel) panel.classList.add('hidden');
        }
        const action = button.dataset.batchAction;
        const selectedIds = Array.from(this.state.selectedKeyIds);
        if (selectedIds.length === 0) {
            toastManager.show('没有选中任何Key。', 'warning');
            return;
        }
        switch (action) {
            case 'copy-to-clipboard':
                this._batchCopyToClipboard(selectedIds);
                break;
            case 'set-status-active':
                this._batchSetStatus('ACTIVE', selectedIds);
                break;
            case 'set-status-disabled':
                this._batchSetStatus('DISABLED', selectedIds);
                break;
            case 'revalidate':
                this._batchRevalidate(selectedIds);
                break;
            case 'delete':
                this._batchDelete(selectedIds);
                break;
            default:
                console.warn(`Unknown batch action: ${action}`);
        }
    }
    /**
     * [NEW] Helper for batch updating the status of selected keys.
     * @param {string} newStatus - The new status ('ACTIVE' or 'DISABLED').
     * @param {number[]} keyIds - An array of selected key IDs.
     */
    async _batchSetStatus(newStatus, keyIds) {
    const groupId = this.state.activeGroupId;
    const actionText = newStatus === 'ACTIVE' ? '启用' : '禁用';
    
    toastManager.show(`正在批量${actionText} ${keyIds.length} 个Key...`, 'info', 3000);
    try {
        const promises = keyIds.map(id => apiKeyManager.updateKeyStatusInGroup(groupId, id, newStatus));
        const results = await Promise.allSettled(promises);
        const fulfilledCount = results.filter(r => r.status === 'fulfilled').length;
        const rejectedCount = results.length - fulfilledCount;
        let toastMessage = `批量${actionText}操作完成。`;
        let toastType = 'success';
        if (rejectedCount > 0) {
            toastMessage = `操作完成: ${fulfilledCount} 个成功，${rejectedCount} 个失败（可能由于Key状态限制）。列表已更新。`;
            // If some succeeded, we can still consider it a partial success visually.
            toastType = fulfilledCount > 0 ? 'warning' : 'error';
        }
        
        toastManager.show(toastMessage, toastType);
    } catch (error) {

        toastManager.show(`批量${actionText}时发生网络错误: ${error.message || '未知错误'}`, 'error');
    } finally {

        await this.loadApiKeys(groupId, true); 
    }
}
    /**
     * [NEW] Helper for batch revalidating selected keys.
     * @param {number[]} keyIds - An array of selected key IDs.
     */
    _batchRevalidate(keyIds) {
        const groupId = this.state.activeGroupId;
        
        // Find the full key strings for the selected IDs
        const currentKeysMap = new Map(this.state.currentKeys.map(key => [key.id, key.api_key]));
        const keysToRevalidate = keyIds.map(id => currentKeysMap.get(id)).filter(Boolean);

        if (keysToRevalidate.length === 0) {
            toastManager.show('找不到匹配的Key进行验证。请刷新列表后重试。', 'error');
            return;
        }
        
        const revalidateTask = {
            start: () => apiKeyManager.revalidateKeys(groupId, keysToRevalidate),
            poll: (taskId) => apiKeyManager.getTaskStatus(taskId, { noCache: true }),
            onSuccess: (data) => {
                toastManager.show(`批量验证完成。`, 'success');
                this.loadApiKeys(groupId, true);
            },
            onError: (data) => {
                toastManager.show(`批量验证任务失败: ${data.error || '未知错误'}`, 'error');
            },
            renderToastNarrative: (data, oldData, toastManager) => {
                const toastId = `task-${data.id}`;
                const progress = data.total > 0 ? (data.processed / data.total) * 100 : 0;
                toastManager.showProgressToast(toastId, `正在批量验证Key`, `处理中 (${data.processed}/${data.total})`, progress);
            },

            // [MODIFIED] This is the core fix. A new, detailed renderer for the task center.
            renderTaskCenterItem: (data, timestamp, formatTimeAgo) => {
                const timeAgo = formatTimeAgo(timestamp);
                let contentHtml = '';

                // --- State 1: Task is RUNNING ---
                if (data.is_running) {
                    contentHtml = `
                        <div class="task-item-main gap-3">
                            <div class="task-item-icon task-item-icon-running"><i class="fas fa-spinner animate-spin"></i></div>
                            <div class="task-item-content flex-grow">
                                <p class="task-item-title">批量验证 ${data.total} 个Key</p>
                                <p class="task-item-status">运行中... (${data.processed}/${data.total})</p>
                            </div>
                        </div>`;
                } 
                // --- State 2: Task is COMPLETE ---
                else {
                    // Case 2a: The entire task failed before processing keys
                    if (data.error) {
                        contentHtml = `
                            <div class="task-item-main">
                                <div class="task-item-icon-summary text-red-500"><i class="fas fa-exclamation-triangle"></i></div>
                                <div class="task-item-content flex-grow">
                                    <p class="task-item-title">批量验证任务出错</p>
                                    <p class="task-item-status text-red-500 truncate" title="${data.error}">${data.error}</p>
                                </div>
                            </div>`;
                    } 
                    // Case 2b: The task ran and we have detailed results
                    else {
                        const results = data.result?.results || [];
                        const validCount = results.filter(r => r.status === 'valid').length;
                        const invalidCount = results.length - validCount;

                        const summaryTitle = `验证完成: ${validCount}个有效, ${invalidCount}个无效`;
                        
                        const overallIconClass = invalidCount > 0 
                            ? 'text-yellow-500 fas fa-exclamation-triangle' // Partial success/warning
                            : 'text-green-500 fas fa-check-circle'; // Full success

                        // Generate the detailed list of results for the expandable area
                        const detailsHtml = results.map(result => {
                            const maskedKey = escapeHTML(`${result.key.substring(0, 4)}...${result.key.substring(result.key.length - 4)}`);
                            const safeMessage = escapeHTML(result.message); 
                            if (result.status === 'valid') {
                                return `
                                    <div class="flex items-start text-xs">
                                        <i class="fas fa-check-circle text-green-500 mt-0.5 mr-2"></i>
                                        <div class="flex-grow">
                                            <p class="font-mono">${maskedKey}</p>
                                            <p class="text-zinc-400">${safeMessage}</p>
                                        </div>
                                    </div>`;
                            } else {
                                return `
                                    <div class="flex items-start text-xs">
                                        <i class="fas fa-times-circle text-red-500 mt-0.5 mr-2"></i>
                                        <div class="flex-grow">
                                            <p class="font-mono">${maskedKey}</p>
                                            <p class="text-zinc-400">${safeMessage}</p>
                                        </div>
                                    </div>`;
                            }
                        }).join('');

                        contentHtml = `
                            <div class="task-item-main">
                                <div class="task-item-icon-summary"><i class="${overallIconClass}"></i></div>
                                <div class="task-item-content flex-grow">
                                    <div class="flex justify-between items-center cursor-pointer" data-task-toggle>
                                        <p class="task-item-title">${summaryTitle}</p>
                                        <i class="fas fa-chevron-down task-toggle-icon"></i>
                                    </div>
                                    <div class="task-details-content collapsed" data-task-content>
                                        <div class="task-details-body space-y-2 mt-2">
                                            ${detailsHtml}
                                        </div>
                                    </div>
                                </div>
                            </div>`;
                    }
                }
                return `${contentHtml}<div class="task-item-timestamp">${timeAgo}</div>`;
            },
        };

        taskCenterManager.startTask(revalidateTask);
    }

    /**
     * [NEW] Helper for batch deleting (unlinking) selected keys from the group.
     * @param {number[]} keyIds - An array of selected key IDs.
     */
    async _batchDelete(keyIds) {
        const groupId = this.state.activeGroupId;
        const selectedCount = keyIds.length;
        const result = await Swal.fire({
            target: '#main-content-wrapper',
            width: '20rem',
            backdrop: `rgba(0,0,0,0.5)`,
            heightAuto: false,
            customClass: {
                popup: `swal2-custom-style ${document.documentElement.classList.contains('dark') ? 'swal2-dark' : ''}`
            },
            title: '确认批量移除',
            html: `确定要从 <b>当前分组</b> 中移除选中的 <b>${selectedCount}</b> 个Key吗？`,
            showCancelButton: true,
            confirmButtonText: '确认',
            cancelButtonText: '取消',
            reverseButtons: true,
            confirmButtonColor: '#d33',
            cancelButtonColor: '#6b7280',
        });
        if (!result.isConfirmed) return;
        
        const keysToDelete = this.state.currentKeys
            .filter(key => keyIds.includes(key.id))
            .map(key => key.api_key);
        if (keysToDelete.length === 0) {
            toastManager.show('找不到匹配的Key进行移除。请刷新列表后重试。', 'error');
            return;
        }
        try {
            const unlinkResult = await apiKeyManager.unlinkKeysFromGroup(groupId, keysToDelete);
            if (!unlinkResult.success) { 
                throw new Error(unlinkResult.message || '后端未能移除Keys。');
            }
            toastManager.show(`成功移除 ${keysToDelete.length} 个Key。`, 'success');
            await this.loadApiKeys(groupId, true); // Refresh list
        } catch (error) {
            const errorMessage = error && error.message ? error.message : '未知错误';
            toastManager.show(`批量移除Key失败: ${errorMessage}`, 'error');
            await this.loadApiKeys(groupId, true); // Refresh anyway
        }
    }
    /**
     * [NEW] Helper for copying all selected API keys to the clipboard.
     * @param {number[]} keyIds - An array of selected key IDs.
     */
    _batchCopyToClipboard(keyIds) {
        // Step 1: Find the full API key strings for the selected IDs from the current state.
        const currentKeysMap = new Map(this.state.currentKeys.map(key => [key.id, key.api_key]));
        const keysToCopy = keyIds.map(id => currentKeysMap.get(id)).filter(Boolean);
        if (keysToCopy.length === 0) {
            toastManager.show('没有找到可复制的Key。列表可能已过期，请刷新。', 'warning');
            return;
        }
        // Step 2: Join the keys into a single string, separated by newlines.
        // This format is easy to paste into text editors or other tools.
        const textToCopy = keysToCopy.join('\n');
        // Step 3: Use the Clipboard API to write the text.
        navigator.clipboard.writeText(textToCopy).then(() => {
            // Success feedback
            toastManager.show(`成功复制 ${keysToCopy.length} 个Key到剪贴板。`, 'success');
        }).catch(err => {
            // Error feedback
            console.error('Failed to copy keys to clipboard:', err);
            toastManager.show(`复制失败: ${err.message}`, 'error');
        });
    }


    /** [NEW] Shows the mobile search modal using SweetAlert2. */
    _showMobileSearchModal() {
        Swal.fire({
            target: '#main-content-wrapper',
            width: '24rem',
            backdrop: `rgba(0,0,0,0.5)`,
            heightAuto: false,
            customClass: {
                popup: `swal2-custom-style rounded-xl ${document.documentElement.classList.contains('dark') ? 'swal2-dark' : ''}`,
                htmlContainer: 'm-0', 
            },
            showConfirmButton: false, // We don't need a confirm button
            showCloseButton: false,   // A close button is user-friendly
            html: `
                <div class="flex items-center justify-between rounded-md bg-zinc-100 dark:bg-zinc-700 p-3 mt-2 font-mono text-sm text-zinc-800 dark:text-zinc-200">
                    <input type="text" id="swal-search-input" placeholder="搜索 API Key..." class="w-full focus:outline-none focus:ring-0 rounded-lg dark:placeholder-zinc-400" 
                           autocomplete="off">
                </div>
            `,
            didOpen: (modal) => {
                const input = modal.querySelector('#swal-search-input');
                if (!input) return;
                input.value = this.state.searchText;
                input.focus();
                
                // Add event listeners directly to the Swal input
                input.addEventListener('input', this._handleSearchInput.bind(this));
                input.addEventListener('keydown', (event) => {
                    this._handleSearchEnter.bind(this)(event); // Ensure correct 'this' context
                    if (event.key === 'Enter') {
                        event.preventDefault(); 
                        Swal.close();
                    }
                });
            }
        });
    }

    /** [NEW] Central handler for any search input event. */
    _handleSearchInput(event) {
        this._updateSearchStateAndSyncInputs(event.target.value);
        this.debouncedSearch();
    }

    /** Synchronizes search text across UI state and inputs. */
    _updateSearchStateAndSyncInputs(value) {
        this.state.searchText = value;
        // Sync desktop input if the event didn't come from it
        if (this.elements.desktopSearchInput && document.activeElement !== this.elements.desktopSearchInput) {
            this.elements.desktopSearchInput.value = value;
        }
    }
    /** [MODIFIED] Handles 'Enter' key press for immediate search. Bug fixed. */
    _handleSearchEnter(event) {
        if (event.key === 'Enter') {
            event.preventDefault();
            this.debouncedSearch.cancel?.(); // Cancel any pending debounced search
            
            this.state.currentPage = 1;
            this.loadApiKeys(this.state.activeGroupId, true);
        }
    }
    
    /** Hides the mobile search overlay */
    _hideMobileSearch() {
        this.elements.mobileSearchOverlay?.classList.add('hidden');
    }


     /**
     * A private helper that returns the complete quick actions configuration.
     * This is the single, authoritative source for all quick action menu data.
     * @returns {Array<Object>} The configuration array for quick actions.
     */
    _getQuickActionsConfiguration() {
        return [
            { action: 'revalidate-invalid', text: '验证所有无效Key', icon: 'fa-rocket text-blue-500', requiresConfirm: false },
            { action: 'revalidate-all', text: '验证所有Key', icon: 'fa-sync-alt text-blue-500', requiresConfirm: true, confirmText: '此操作将对当前分组下的 **所有Key** 发起一次API请求以验证其有效性，可能会消耗大量额度。确定要继续吗？' },
            { action: 'restore-cooldown', text: '恢复所有冷却中Key', icon: 'fa-undo text-green-500', requiresConfirm: false },
            { type: 'divider' },
            { action: 'cleanup-banned', text: '删除所有失效Key', icon: 'fa-trash-alt', danger: true, requiresConfirm: true, confirmText: '确定要从当前分组中移除 **所有** 状态为 `BANNED` 的Key吗？此操作不可恢复。' }
        ];
    }

    /**
     * Renders the content of the quick actions dropdown menu into both desktop and mobile panels.
     */
    _renderQuickActionsMenu() {
        const actions = this._getQuickActionsConfiguration(); // Fetch config from the helper
        const menuHtml = actions.map(item => {
            if (item.type === 'divider') {
                return '<div class="menu-divider"></div>';
            }
            return `
                <button data-quick-action="${item.action}" class="menu-item ${item.danger ? 'menu-item-danger' : ''} whitespace-nowrap">
                    <i class="fas ${item.icon} menu-item-icon"></i>
                    <span>${item.text}</span>
                </button>
            `;
        }).join('');
        const menuWrapper = `<div class="py-1">${menuHtml}</div>`;
        if (this.elements.desktopQuickActionsPanel) {
            this.elements.desktopQuickActionsPanel.innerHTML = menuWrapper;
        }
        if (this.elements.mobileQuickActionsPanel) {
            this.elements.mobileQuickActionsPanel.innerHTML = menuWrapper;
        }
    }

    /**
     * [NEW] Handles clicks on any quick action button.
     * @param {Event} event The click event.
     */
    async _handleQuickActionClick(event) {
        const button = event.target.closest('button[data-quick-action]');
        if (!button) return;
        event.preventDefault();
        // Assume the custom-select component handles closing itself.
        const action = button.dataset.quickAction;
        const groupId = this.state.activeGroupId;
        if (!groupId) {
            toastManager.show('请先选择一个分组。', 'warning');
            return;
        }
        // Find the action config to check for confirmation
        const actionConfig = this._getQuickActionConfig(action);
        if (actionConfig && actionConfig.requiresConfirm) {
            const result = await Swal.fire({
                target: '#main-content-wrapper',
                title: '请确认操作',
                html: actionConfig.confirmText,
                icon: 'warning',
                showCancelButton: true,
                confirmButtonText: '确认执行',
                cancelButtonText: '取消',
                reverseButtons: true,
                confirmButtonColor: '#d33',
                customClass: { popup: `swal2-custom-style ${document.documentElement.classList.contains('dark') ? 'swal2-dark' : ''}` },
            });
            if (!result.isConfirmed) return;
        }
        this._executeQuickAction(action, groupId);
    }
    
    /** [NEW] Helper to retrieve action configuration. */
    _getQuickActionConfig(action) {
        const actions = this._getQuickActionsConfiguration(); 
        return actions.find(a => a.action === action);
    }
    /**
     * [MODIFIED] Executes the quick action by building the correct payload for the unified bulk-actions endpoint.
     * @param {string} action The action to execute.
     * @param {number} groupId The current group ID.
     */
    async _executeQuickAction(action, groupId) {
        let taskDefinition;
        let payload; // We will build a payload object instead of calling different functions
        let taskTitle;
        switch (action) {
            case 'revalidate-invalid':
                taskTitle = '验证分组无效Key';
                payload = { 
                    action: 'revalidate', 
                    filter: { 
                        // Backend should interpret 'invalid' as this set of statuses
                        status: ['disabled', 'cooldown', 'banned'] 
                    } 
                };
                break;
            case 'revalidate-all':
                taskTitle = '验证分组所有Key';
                payload = { 
                    action: 'revalidate', 
                    filter: { status: ['all'] } 
                };
                break;
            case 'restore-cooldown':
                taskTitle = '恢复冷却中Key';
                payload = { 
                    action: 'set_status', 
                    new_status: 'active', // The target status
                    filter: { status: ['cooldown'] } 
                };
                break;
            case 'cleanup-banned':
                taskTitle = '清理失效Key';
                payload = { 
                    action: 'delete', 
                    filter: { status: ['banned'] } 
                };
                break;
            default:
                toastManager.show(`未知的快速处置操作: ${action}`, 'error');
                return;
        }
        try {
            const response = await apiKeyManager.startGroupBulkActionTask(groupId, payload);
            if (response && response.id) {
                const startPromise = Promise.resolve(response);
                const taskDefinition = this._createGroupTaskDefinition(taskTitle, startPromise, groupId, action);
                taskCenterManager.startTask(taskDefinition);
            } else {
                if (response && response.result && response.result.message) {
                    toastManager.show(response.result.message, 'info');
                } else {
                    toastManager.show('操作已完成，但无任何项目被更改。', 'info');
                }
            }
        } catch (error) {
            handleApiError(error, toastManager, { prefix: `${taskTitle}时发生意外错误: ` });
        }
    }
    /**
     * [NEW] Generic task definition factory for group-level operations.
     */
    _createGroupTaskDefinition(title, startPromise, groupId, action) {
        return {
            start: () => startPromise,
            poll: (taskId) => apiKeyManager.getTaskStatus(taskId, { noCache: true }),
            onSuccess: (data) => {
                toastManager.show(`${title}任务完成。`, 'success');
                this.loadApiKeys(groupId, true);
            },
            onError: (data) => {
                const displayMessage = escapeHTML(data.error || '任务执行时发生未知错误');
                toastManager.show(`${title}任务失败: ${displayMessage}`, 'error');
            },
            renderToastNarrative: (data, oldData, toastManager) => {
                const toastId = `task-${data.id}`;
                const progress = data.total > 0 ? (data.processed / data.total) * 100 : 0;
                toastManager.showProgressToast(toastId, title, `处理中 (${data.processed}/${data.total})`, progress);
            },
            renderTaskCenterItem: (data, timestamp, formatTimeAgo) => {
                const timeAgo = formatTimeAgo(timestamp);
                let contentHtml = '';
                if (data.is_running) {
                    contentHtml = `
                        <div class="task-item-main gap-3">
                            <div class="task-item-icon task-item-icon-running"><i class="fas fa-spinner animate-spin"></i></div>
                            <div class="task-item-content flex-grow">
                                <p class="task-item-title">${title}</p>
                                <p class="task-item-status">运行中... (${data.processed}/${data.total})</p>
                            </div>
                        </div>`;
                } else if (data.error) {
                    const safeError = escapeHTML(data.error);
                    contentHtml = `
                        <div class="task-item-main">
                            <div class="task-item-icon-summary text-red-500"><i class="fas fa-exclamation-triangle"></i></div>
                            <div class="task-item-content flex-grow">
                                <p class="task-item-title">${title}任务出错</p>
                                <p class="task-item-status text-red-500 truncate" title="${safeError}">${safeError}</p>
                            </div>
                        </div>`;
                } else {
                    let summary = '任务已完成。';
                    const result = data.result || {};
                    const iconClass = 'fas fa-check-circle text-green-500';
                    switch (action) {
                        case 'cleanup-banned':
                            // For this action, backend returns `unlinked_count`
                            summary = `成功清理 ${result.unlinked_count || 0} 个失效Key。`;
                            break;
                        case 'restore-cooldown':
                            // For this action, backend returns `updated_count` and `skipped_count`
                             if (result.skipped_count > 0) {
                                summary = `完成: ${result.updated_count || 0} 个已恢复, ${result.skipped_count} 个被跳过。`;
                            } else {
                                summary = `成功恢复 ${result.updated_count || 0} 个冷却中Key。`;
                            }
                            break;
                        case 'revalidate-invalid':
                        case 'revalidate-all':
                            // For revalidation, we can show a summary of valid/invalid
                            const results = result.results || [];
                            const validCount = results.filter(r => r.status === 'valid').length;
                            const invalidCount = results.length - validCount;
                            summary = `验证完成: ${validCount}个有效, ${invalidCount}个无效。`;
                            break;
                        default:
                            summary = `处理了 ${data.processed} 个Key。`;
                    }
                    const safeSummary = escapeHTML(summary);
                    contentHtml = `
                        <div class="task-item-main">
                            <div class="task-item-icon-summary"><i class="${iconClass}"></i></div>
                            <div class="task-item-content flex-grow">
                                <p class="task-item-title">${title}</p>
                                <p class="task-item-status truncate" title="${safeSummary}">${safeSummary}</p>
                            </div>
                        </div>`;
                }
                return `${contentHtml}<div class="task-item-timestamp">${timeAgo}</div>`;
            }
        };
    }

    /**
     * [NEW] A generic handler to open/close any custom dropdown menu.
     * It uses data attributes to link triggers to panels.
     * @param {Event} event The click event.
     */
    _handleDropdownToggle(event) {
        const toggleButton = event.target.closest('[data-toggle="custom-select"]');
        
        // If the click is outside any toggle button, close all panels.
        if (!toggleButton) {
            this._closeAllDropdowns();
            return;
        }
        const targetSelector = toggleButton.dataset.target;
        const targetPanel = document.querySelector(targetSelector);
        if (!targetPanel) return;
        const isPanelOpen = !targetPanel.classList.contains('hidden');
        // First, close all other panels.
        this._closeAllDropdowns(targetPanel);
        // Then, toggle the state of the target panel.
        if (!isPanelOpen) {
            targetPanel.classList.remove('hidden');
        }
    }
    /**
     * [NEW] A helper utility to close all custom dropdown panels.
     * @param {HTMLElement} [excludePanel=null] An optional panel to exclude from closing.
     */
    _closeAllDropdowns(excludePanel = null) {
        const allPanels = document.querySelectorAll('.custom-select-panel');
        allPanels.forEach(panel => {
            if (panel !== excludePanel) {
                panel.classList.add('hidden');
            }
        });
    }

    /**
     * [NEW] Renders the content of the multifunction (export) dropdown menu.
     */
    _renderMultifunctionMenu() {
        const actions = [
            { action: 'export-all', text: '导出所有Key', icon: 'fa-file-export' },
            { action: 'export-valid', text: '导出有效Key', icon: 'fa-file-alt' },
            { action: 'export-invalid', text: '导出无效Key', icon: 'fa-file-excel' }
        ];
        const menuHtml = actions.map(item => {
            return `
                <button data-multifunction-action="${item.action}" class="menu-item whitespace-nowrap">
                    <i class="fas ${item.icon} menu-item-icon"></i>
                    <span>${item.text}</span>
                </button>
            `;
        }).join('');
        const menuWrapper = `<div class="py-1">${menuHtml}</div>`;
        if (this.elements.desktopMultifunctionPanel) {
            this.elements.desktopMultifunctionPanel.innerHTML = menuWrapper;
        }
        if (this.elements.mobileMultifunctionPanel) {
            this.elements.mobileMultifunctionPanel.innerHTML = menuWrapper;
        }
    }
    /**
     * [NEW] Handles clicks on any multifunction menu button.
     * @param {Event} event The click event.
     */
    async _handleMultifunctionMenuClick(event) {
        const button = event.target.closest('button[data-multifunction-action]');
        if (!button) return;
        event.preventDefault();
        this._closeAllDropdowns(); 
        const action = button.dataset.multifunctionAction;
        const groupId = this.state.activeGroupId;
        if (!groupId) {
            toastManager.show('请先选择一个分组。', 'warning');
            return;
        }
        let statuses = [];
        let filename = `${this.state.activeGroupName}_keys.txt`;
        switch (action) {
            case 'export-all':
                statuses = ['all'];
                filename = `${this.state.activeGroupName}_all_keys.txt`;
                break;
            case 'export-valid':
                statuses = ['active', 'cooldown'];
                filename = `${this.state.activeGroupName}_valid_keys.txt`;
                break;
            case 'export-invalid':
                statuses = ['disabled', 'banned'];
                filename = `${this.state.activeGroupName}_invalid_keys.txt`;
                break;
            default:
                return;
        }
        
        toastManager.show('正在准备导出数据...', 'info');
        try {
            const keys = await apiKeyManager.exportKeysForGroup(groupId, statuses);
            if (keys.length === 0) {
                toastManager.show('没有找到符合条件的Key可供导出。', 'warning');
                return;
            }
            this._triggerTextFileDownload(keys.join('\n'), filename);
            toastManager.show(`成功导出 ${keys.length} 个Key。`, 'success');
        } catch (error) {
            toastManager.show(`导出失败: ${error.message}`, 'error');
        }
    }
    /**
     * [NEW] A utility function to trigger a text file download in the browser.
     * @param {string} content The content of the file.
     * @param {string} filename The desired name of the file.
     */
    _triggerTextFileDownload(content, filename) {
        const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }
}

export default ApiKeyList;
