/*
 * 通用表情面板脚本：提供表情插入、贴纸清单加载和消息内容渲染。
 * 作者: @ccfork
 */
(function () {
    const faceEmojis = [
        '😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃', '🫠', '😉',
        '😊', '😇', '☺️', '🥰', '😍', '🤩', '😘', '😗', '😚', '😙', '🥲',
        '😋', '😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🫢', '🫣', '🤫',
        '🤔', '🫡', '🤐', '🤨', '😐', '😑', '😶', '🫥', '😶‍🌫️', '😏', '😒',
        '🙄', '😬', '😮‍💨', '🤥', '🫨', '🙂‍↔️', '🙂‍↕️', '😌', '😔', '😪',
        '🤤', '😴', '🫩', '😷', '🤒', '🤕', '🤢', '🤮', '🤧', '🥵', '🥶',
        '🥴', '😵', '😵‍💫', '🤯', '🤠', '🥳', '🥸', '😎', '🤓', '🧐', '😕',
        '🫤', '😟', '🙁', '☹️', '😮', '😯', '😲', '😳', '🥺', '🥹', '😦',
        '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖', '😣', '😞', '😓',
        '😩', '😫', '🥱', '😤', '😡', '😠', '🤬', '😈', '👿', '💀', '☠️',
        '💩', '🤡', '👹', '👺', '👻', '👽', '👾', '🤖', '😺', '😸', '😹',
        '😻', '😼', '😽', '🙀', '😿', '😾', '🙈', '🙉', '🙊'
    ];
    const emojiMap = {
        '[滑稽]': '😆',
        '[委屈]': '😢',
        '[惊讶]': '😲',
        '[爱心]': '❤️',
        '[玫瑰]': '🌹',
        '[拥抱]': '🤗'
    };
    const packNameMap = { douyin: '抖音', qq: 'QQ', tieba: '贴吧' };
    const packCache = new Map();

    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text == null ? '' : String(text);
        return div.innerHTML;
    }

    function convertEmoji(text) {
        let result = String(text || '');
        Object.entries(emojiMap).forEach(([key, val]) => {
            result = result.split(key).join(val);
        });
        return result;
    }

    function apiWithAction(apiUrl, action) {
        const separator = apiUrl.includes('?') ? '&' : '?';
        return `${apiUrl}${separator}action=${encodeURIComponent(action)}`;
    }

    async function loadStickerPacks(apiUrl) {
        if (packCache.has(apiUrl)) return packCache.get(apiUrl);
        try {
            const response = await fetch(apiWithAction(apiUrl, 'get_sticker_packs'), { cache: 'force-cache' });
            const data = await response.json();
            const packs = data.success ? (data.packs || []) : [];
            packCache.set(apiUrl, packs);
            return packs;
        } catch (error) {
            console.error('加载表情包失败:', error);
            return [];
        }
    }

    function insertAtCursor(input, text) {
        const start = input.selectionStart ?? input.value.length;
        const end = input.selectionEnd ?? input.value.length;
        input.value = `${input.value.slice(0, start)}${text}${input.value.slice(end)}`;
        const nextPosition = start + text.length;
        input.setSelectionRange(nextPosition, nextPosition);
        input.dispatchEvent(new Event('input', { bubbles: true }));
        input.focus();
    }

    function createPanel({ host, trigger, input, apiUrl }) {
        let packs = [];
        let activeTab = 'faces';
        host.innerHTML = `
            <div class="emoji-drawer" hidden>
                <div class="emoji-head">
                    <div class="emoji-tabs" role="tablist" aria-label="表情分类"></div>
                    <button class="emoji-close" type="button" aria-label="关闭表情" title="关闭">×</button>
                </div>
                <div class="emoji-grid" role="tabpanel"></div>
            </div>
        `;

        const drawer = host.querySelector('.emoji-drawer');
        const tabsEl = host.querySelector('.emoji-tabs');
        const gridEl = host.querySelector('.emoji-grid');
        const closeBtn = host.querySelector('.emoji-close');

        function close() {
            drawer.hidden = true;
            trigger?.classList.remove('active');
            trigger?.setAttribute('aria-label', '打开表情');
        }

        function toggle(forceOpen) {
            const shouldOpen = forceOpen ?? drawer.hidden;
            drawer.hidden = !shouldOpen;
            trigger?.classList.toggle('active', shouldOpen);
            trigger?.setAttribute('aria-label', shouldOpen ? '关闭表情' : '打开表情');
        }

        function renderTabs() {
            const tabs = [
                { id: 'faces', name: '人脸' },
                ...packs.map((pack) => ({ id: pack.id, name: pack.label || packNameMap[pack.id] || pack.id }))
            ];
            tabsEl.innerHTML = tabs.map((tab) => `
                <button class="emoji-tab${tab.id === activeTab ? ' active' : ''}" type="button" data-tab="${escapeHtml(tab.id)}" role="tab">
                    ${escapeHtml(tab.name)}
                </button>
            `).join('');
        }

        function renderGrid() {
            const isFaceTab = activeTab === 'faces';
            gridEl.classList.toggle('faces', isFaceTab);
            if (isFaceTab) {
                gridEl.innerHTML = faceEmojis.map((emoji) => `
                    <button class="emoji-btn" type="button" data-insert="${escapeHtml(emoji)}" title="${escapeHtml(emoji)}">${escapeHtml(emoji)}</button>
                `).join('');
                return;
            }

            const pack = packs.find((item) => item.id === activeTab);
            const files = pack?.files || [];
            if (!files.length) {
                gridEl.innerHTML = '<div class="state-block"><span>暂无表情</span></div>';
                return;
            }

            gridEl.innerHTML = files.map((file) => {
                const src = `bq/${encodeURIComponent(pack.id)}/${encodeURIComponent(file.name)}`;
                const title = `${pack.label || pack.id} ${file.index || ''}`.trim();
                const marker = `[bq:${pack.id}:${file.name}]`;
                return `
                    <button class="sticker-btn" type="button" data-insert="${escapeHtml(marker)}" title="${escapeHtml(title)}">
                        <img src="${src}" alt="${escapeHtml(title)}" loading="lazy">
                    </button>
                `;
            }).join('');
        }

        function render() {
            renderTabs();
            renderGrid();
        }

        trigger?.addEventListener('click', () => toggle());
        closeBtn.addEventListener('click', close);
        tabsEl.addEventListener('click', (event) => {
            const tab = event.target.closest('.emoji-tab');
            if (!tab) return;
            activeTab = tab.dataset.tab || 'faces';
            render();
        });
        gridEl.addEventListener('click', (event) => {
            const button = event.target.closest('[data-insert]');
            if (!button) return;
            insertAtCursor(input, button.dataset.insert || '');
        });

        return {
            async init() {
                packs = await loadStickerPacks(apiUrl);
                render();
                return packs;
            },
            close,
            toggle,
            getStickerPacks() {
                return packs;
            }
        };
    }

    function getStickerIndex(stickerPacks) {
        const index = new Map();
        stickerPacks.forEach((pack) => {
            index.set(pack.id, new Set((pack.files || []).map((file) => file.name)));
        });
        return index;
    }

    function renderMessageContent(rawMessage, stickerPacks) {
        const stickerIndex = getStickerIndex(stickerPacks || []);
        const text = convertEmoji(rawMessage);
        const markerPattern = /\[bq:([a-z0-9_-]+):([^\]\s]+)\]/gi;
        const textOnly = text.replace(markerPattern, '').trim();
        const inlineSticker = textOnly !== '';
        let html = '';
        let lastIndex = 0;
        let hasSticker = false;
        let match;

        while ((match = markerPattern.exec(text)) !== null) {
            const [marker, packId, fileName] = match;
            html += escapeHtml(text.slice(lastIndex, match.index));
            if (stickerIndex.get(packId)?.has(fileName)) {
                const src = `bq/${encodeURIComponent(packId)}/${encodeURIComponent(fileName)}`;
                const alt = `${packNameMap[packId] || packId}表情`;
                const cls = inlineSticker ? 'message-sticker inline-sticker' : 'message-sticker';
                html += `<img class="${cls}" src="${src}" alt="${escapeHtml(alt)}" loading="lazy">`;
                hasSticker = true;
            } else {
                html += escapeHtml(marker);
            }
            lastIndex = match.index + marker.length;
        }

        html += escapeHtml(text.slice(lastIndex));
        return { html, hasSoloSticker: hasSticker && !inlineSticker };
    }

    window.ChatEmoji = {
        createPanel,
        convertEmoji,
        loadStickerPacks,
        renderMessageContent
    };
})();
