/**
 * 表情包导入插件
 * 扫描本机 QQ / 微信 自定义表情，支持手动导入文件夹
 * 支持 macOS 和 Windows 平台，提供预览、全选/反选、批量导入功能
 */

const fs = require('fs');
const path = require('path');
const os = require('os');
const { exec, execSync } = require('child_process');

/**
 * 获取 @electron/remote 模块
 * 插件通过 plugin-manager require() 加载，模块解析路径不包含 app.asar，
 * 因此无法直接 require('@electron/remote')，需要通过 module.parent 链查找。
 */
function getElectronRemote() {
    try {
        return require('@electron/remote');
    } catch (e) { /* ignore */ }
    let parentModule = module.parent;
    while (parentModule) {
        try {
            return parentModule.require('@electron/remote');
        } catch (e) {
            parentModule = parentModule.parent;
        }
    }
    if (process.mainModule) {
        try {
            return process.mainModule.require('@electron/remote');
        } catch (e) { /* ignore */ }
    }
    throw new Error('无法加载 @electron/remote 模块');
}

module.exports = {
    name: '表情包导入',
    description: '导入本机 QQ表情包，需要预先在QQ里浏览一遍已收藏的表情，形成缓存才能读取到',
    version: 'dev',
    author: 'zfeng',
    defaultEnabled: false,

    defaultConfig: {},

    // ---- 常量 ----
    STYLE_ID: 'sticker-import-styles',
    NAV_ICON_ID: 'sticker-import-nav-icon',
    PANEL_ID: 'sticker-import-panel',

    // 支持的图片格式
    IMAGE_EXTS: ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'],

    // ---- 表情来源配置 ----
    _getSources() {
        const home = os.homedir();
        return [
            {
                id: 'qq',
                name: 'QQ 表情',
                icon: '🐧',
                cssClass: 'qq',
                description: 'QQ 自定义表情包',
                basePaths: this._findQQPaths(home),
            },
            {
                id: 'wechat',
                name: '微信表情',
                icon: '💬',
                cssClass: 'wechat',
                description: '微信收藏的自定义表情',
                basePaths: this._findWeChatPaths(home),
            },
        ];
    },

    /**
     * 查找 QQ 可能的表情路径（支持 macOS 和 Windows）
     */
    _findQQPaths(home) {
        const paths = [];
        const platform = process.platform;

        if (platform === 'darwin') {
            // ---- macOS: NT QQ ----
            const qqBase = path.join(home, 'Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ');
            try {
                if (fs.existsSync(qqBase)) {
                    const entries = fs.readdirSync(qqBase, { withFileTypes: true });
                    for (const entry of entries) {
                        if (entry.isDirectory() && (entry.name.startsWith('nt_qq') || entry.name === 'global')) {
                            const personalEmojiPath = path.join(qqBase, entry.name, 'nt_data', 'Emoji', 'personal_emoji', 'Ori');
                            if (fs.existsSync(personalEmojiPath)) {
                                paths.push(personalEmojiPath);
                            }
                        }
                    }
                }
            } catch (e) {
                window.PluginManager.log('sticker-import', '[StickerImport] 查找 macOS QQ 路径出错:' + ' ' + e.message, 'warn');
            }
        } else if (platform === 'win32') {
            // ---- Windows: 收集所有可能的 "Tencent Files" 目录 ----
            // 优先读取 QQ 自身的配置文件来获取精确路径，避免穷举搜索
            const tencentFilesCandidates = new Set();

            // 【最优先】从 QQ UserDataInfo.ini 配置文件读取用户设定的存储路径
            // 用户在 QQ "设置 > 存储管理" 中修改的路径会写入此文件
            // 配置文件位置: C:\Users\Public\Documents\Tencent\QQ\UserDataInfo.ini
            // 格式:
            //   [UserDataSet]
            //   UserDataSavePathType=2          (2=自定义路径)
            //   UserDataSavePath=D:\QQData       (用户设定的存储根目录)
            // 此时 Tencent Files 实际位于: D:\QQData\Tencent Files
            const iniPaths = [
                'C:\\Users\\Public\\Documents\\Tencent\\QQ\\UserDataInfo.ini',
                path.join(home, 'AppData', 'Roaming', 'Tencent', 'QQ', 'UserDataInfo.ini'),
            ];
            for (const iniPath of iniPaths) {
                try {
                    if (fs.existsSync(iniPath)) {
                        const iniContent = fs.readFileSync(iniPath, 'utf-8');
                        // 解析 UserDataSavePath 值
                        const match = iniContent.match(/UserDataSavePath(?!Type)\s*=\s*(.+)/i);
                        if (match) {
                            let savePath = match[1].trim();
                            // 展开可能的环境变量
                            savePath = savePath.replace(/%([^%]+)%/g, (_, envVar) => process.env[envVar] || `%${envVar}%`);
                            if (savePath && fs.existsSync(savePath)) {
                                // UserDataSavePath 可能直接就是 Tencent Files 目录，
                                // 也可能是其父目录（需要拼接 "Tencent Files"）
                                if (savePath.endsWith('Tencent Files') || savePath.endsWith('Tencent Files\\') || savePath.endsWith('Tencent Files/')) {
                                    tencentFilesCandidates.add(savePath.replace(/[\\/]+$/, ''));
                                    window.PluginManager.log('sticker-import', '[StickerImport] 从 UserDataInfo.ini 找到 Tencent Files:' + ' ' + savePath);
                                } else {
                                    const tfPath = path.join(savePath, 'Tencent Files');
                                    if (fs.existsSync(tfPath)) {
                                        tencentFilesCandidates.add(tfPath);
                                        window.PluginManager.log('sticker-import', '[StickerImport] 从 UserDataInfo.ini 找到 Tencent Files:' + ' ' + tfPath);
                                    } else {
                                        // 有些情况下 savePath 本身就是数据根目录（直接包含 QQ号 子目录）
                                        tencentFilesCandidates.add(savePath);
                                        window.PluginManager.log('sticker-import', '[StickerImport] 从 UserDataInfo.ini 使用数据根目录:' + ' ' + savePath);
                                    }
                                }
                            }
                        }
                    }
                } catch (e) {
                    window.PluginManager.log('sticker-import', '[StickerImport] 读取 UserDataInfo.ini 出错:' + ' ' + iniPath + ' ' + e.message, 'warn');
                }
            }

            // 【次优先】QQNT 还可能在 AppData 下有 global config
            const qqntConfigPaths = [
                path.join(home, 'AppData', 'Roaming', 'QQ', 'global', 'config.json'),
                path.join(home, 'AppData', 'Local', 'QQ', 'global', 'config.json'),
            ];
            for (const cfgPath of qqntConfigPaths) {
                try {
                    if (fs.existsSync(cfgPath)) {
                        const cfgContent = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
                        const dataPath = cfgContent.userDataSavePath || cfgContent.UserDataSavePath || cfgContent.dataPath;
                        if (dataPath && fs.existsSync(dataPath)) {
                            const tfPath = path.join(dataPath, 'Tencent Files');
                            if (fs.existsSync(tfPath)) {
                                tencentFilesCandidates.add(tfPath);
                                window.PluginManager.log('sticker-import', '[StickerImport] 从 QQNT config.json 找到 Tencent Files:' + ' ' + tfPath);
                            }
                        }
                    }
                } catch (_) { /* 忽略 */ }
            }

            // 【Fallback】如果从配置文件未找到，则回退到传统的 Documents 路径搜索
            if (tencentFilesCandidates.size === 0) {
                window.PluginManager.log('sticker-import', '[StickerImport] 未从 QQ 配置文件中找到路径，回退到 Documents 路径搜索');
                const candidateDocsPaths = new Set();

                // 通过 Electron app.getPath('documents') 获取真实文档目录
                try {
                    const { app } = getElectronRemote();
                    const electronDocsPath = app.getPath('documents');
                    if (electronDocsPath) {
                        candidateDocsPaths.add(electronDocsPath);
                    }
                } catch (_) { /* Electron remote 不可用，跳过 */ }

                // 默认路径 USERPROFILE\Documents
                candidateDocsPaths.add(path.join(home, 'Documents'));

                // 使用 Windows 注册表查询 Personal(文档) Shell Folder 真实路径
                try {
                    const regOutput = execSync(
                        'reg query "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders" /v Personal',
                        { encoding: 'utf-8', timeout: 5000 }
                    ).trim();
                    const match = regOutput.match(/Personal\s+REG_\w+\s+(.+)/i);
                    if (match) {
                        let regPath = match[1].trim();
                        regPath = regPath.replace(/%([^%]+)%/g, (_, envVar) => process.env[envVar] || _);
                        candidateDocsPaths.add(regPath);
                    }
                } catch (_) { /* 注册表查询失败，跳过 */ }

                // 检查常见的其他盘符下的 Documents
                ['D:', 'E:', 'F:'].forEach(drive => {
                    const driveDocs = path.join(drive, 'Documents');
                    if (fs.existsSync(path.join(driveDocs, 'Tencent Files'))) {
                        candidateDocsPaths.add(driveDocs);
                    }
                });

                for (const docsPath of candidateDocsPaths) {
                    const tfPath = path.join(docsPath, 'Tencent Files');
                    if (fs.existsSync(tfPath)) {
                        tencentFilesCandidates.add(tfPath);
                    }
                }
            }

            // 遍历所有候选 Tencent Files 路径，提取表情目录
            for (const tencentFilesBase of tencentFilesCandidates) {
                try {
                    if (!fs.existsSync(tencentFilesBase)) continue;
                    const userDirs = fs.readdirSync(tencentFilesBase, { withFileTypes: true })
                        .filter(d => d.isDirectory());
                    for (const userDir of userDirs) {
                        const userPath = path.join(tencentFilesBase, userDir.name);
                        try {
                            const subDirs = fs.readdirSync(userPath, { withFileTypes: true })
                                .filter(d => d.isDirectory());
                            for (const sub of subDirs) {
                                // QQNT: nt_qq 或 nt_qq_* 目录下的个人表情
                                if (sub.name.startsWith('nt_qq') || sub.name === 'global') {
                                    const personalEmojiPath = path.join(userPath, sub.name, 'nt_data', 'Emoji', 'personal_emoji', 'Ori');
                                    if (fs.existsSync(personalEmojiPath)) {
                                        paths.push(personalEmojiPath);
                                    }
                                }
                            }
                        } catch (_) { /* 忽略无权限的目录 */ }

                        // 旧版 QQ: CustomFace 目录
                        const customFacePath = path.join(userPath, 'CustomFace');
                        if (fs.existsSync(customFacePath)) {
                            paths.push(customFacePath);
                        }
                        // 旧版 QQ: Image\Image 目录（部分版本存放接收的表情图片）
                        const imagePath = path.join(userPath, 'Image', 'Image');
                        if (fs.existsSync(imagePath)) {
                            paths.push(imagePath);
                        }
                    }
                } catch (e) {
                    window.PluginManager.log('sticker-import', '[StickerImport] 查找 Windows QQ 路径出错:' + ' ' + tencentFilesBase + ' ' + e.message, 'warn');
                }
            }

            // ---- Windows: 旧版 QQ 安装目录下的表情（较少见）----
            // 部分用户的 QQ 文件保存在 AppData\Roaming\Tencent
            const appDataQQ = path.join(home, 'AppData', 'Roaming', 'Tencent', 'QQ');
            try {
                if (fs.existsSync(appDataQQ)) {
                    const entries = fs.readdirSync(appDataQQ, { withFileTypes: true })
                        .filter(d => d.isDirectory());
                    for (const entry of entries) {
                        const customFacePath = path.join(appDataQQ, entry.name, 'CustomFace');
                        if (fs.existsSync(customFacePath)) {
                            paths.push(customFacePath);
                        }
                    }
                }
            } catch (e) {
                window.PluginManager.log('sticker-import', '[StickerImport] 查找 Windows AppData QQ 路径出错:' + ' ' + e.message, 'warn');
            }
        }

        // 去重
        return [...new Set(paths)];
    },

    /**
     * 查找微信可能的表情路径
     */
    _findWeChatPaths(home) {
        const paths = [];

        // 1. 新版本 WeChat (xwechat_files)
        const xwBase = path.join(home, 'Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files');
        try {
            if (fs.existsSync(xwBase)) {
                const users = fs.readdirSync(xwBase, { withFileTypes: true })
                    .filter(d => d.isDirectory() && !['Backup', 'all_users'].includes(d.name))
                    .map(d => d.name);
                
                for (const user of users) {
                    const cacheDir = path.join(xwBase, user, 'cache');
                    if (fs.existsSync(cacheDir)) {
                        const months = fs.readdirSync(cacheDir, { withFileTypes: true })
                            .filter(d => d.isDirectory())
                            .map(d => d.name);
                        for (const month of months) {
                            const emoticonDir = path.join(cacheDir, month, 'Emoticon');
                            if (fs.existsSync(emoticonDir)) {
                                paths.push(emoticonDir);
                            }
                        }
                    }
                }
            }
        } catch (e) {
            window.PluginManager.log('sticker-import', '[StickerImport] 查找 WeChat xwechat_files 路径出错:' + ' ' + e.message, 'warn');
        }

        // 2. 老版本 WeChat
        const wechatBase = path.join(home, 'Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat');
        try {
            if (fs.existsSync(wechatBase)) {
                // 遍历版本目录
                const versions = fs.readdirSync(wechatBase, { withFileTypes: true })
                    .filter(d => d.isDirectory())
                    .map(d => d.name);

                for (const ver of versions) {
                    const verDir = path.join(wechatBase, ver);
                    // 遍历用户 hash 目录
                    const subs = fs.readdirSync(verDir, { withFileTypes: true })
                        .filter(d => d.isDirectory() && d.name !== 'MMappedKV' && d.name !== 'NewMMappedKV' && d.name !== 'CGI' && d.name !== 'KeyValue');

                    for (const sub of subs) {
                        ['Stickers', 'Emotion'].forEach(subPath => {
                            const dir = path.join(verDir, sub.name, subPath);
                            if (fs.existsSync(dir)) {
                                paths.push(dir);
                            }
                        });
                    }
                }
            }
        } catch (e) {
            window.PluginManager.log('sticker-import', '[StickerImport] 查找 WeChat 老版本路径出错:' + ' ' + e.message, 'warn');
        }

        return paths;
    },

    /**
     * 通过 magic bytes 检测文件的真实图片格式
     * @param {string} filePath
     * @returns {string|null} 真实扩展名（如 '.gif'），无法识别时返回 null
     */
    _detectRealExt(filePath) {
        try {
            const buf = Buffer.alloc(12);
            const fd = fs.openSync(filePath, 'r');
            fs.readSync(fd, buf, 0, 12, 0);
            fs.closeSync(fd);

            if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46) return '.gif';   // GIF87a / GIF89a
            if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4E && buf[3] === 0x47) return '.png';
            if (buf[0] === 0xFF && buf[1] === 0xD8 && buf[2] === 0xFF) return '.jpg';
            if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46
                && buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50) return '.webp';
        } catch (_) { /* 忽略 */ }
        return null;
    },

    /**
     * 递归扫描目录中的图片文件
     */
    _scanImages(dirPath, maxDepth = 3, currentDepth = 0) {
        const results = [];
        if (currentDepth > maxDepth) return results;

        try {
            if (!fs.existsSync(dirPath)) return results;

            const entries = fs.readdirSync(dirPath, { withFileTypes: true });
            for (const entry of entries) {
                if (entry.name.startsWith('.')) continue;
                const fullPath = path.join(dirPath, entry.name);

                if (entry.isFile()) {
                    const ext = path.extname(entry.name).toLowerCase();
                    if (this.IMAGE_EXTS.includes(ext)) {
                        // 过滤太小的文件（可能不是有效表情）
                        try {
                            const stat = fs.statSync(fullPath);
                            if (stat.size > 500) { // 大于 500 字节
                                // 通过 magic bytes 检测真实格式，纠正扩展名不匹配的情况
                                const realExt = this._detectRealExt(fullPath);
                                results.push({
                                    path: fullPath,
                                    name: entry.name,
                                    size: stat.size,
                                    ext: realExt || ext,
                                    realExt: realExt,
                                });
                            }
                        } catch (_) { /* 忽略无法 stat 的文件 */ }
                    }
                    // 对于无扩展名的文件，检查是否为图片（微信表情可能没有扩展名）
                    if (!ext || ext === '') {
                        try {
                            const realExt = this._detectRealExt(fullPath);
                            if (realExt) {
                                const stat = fs.statSync(fullPath);
                                if (stat.size > 500) {
                                    results.push({
                                        path: fullPath,
                                        name: entry.name,
                                        size: stat.size,
                                        ext: realExt,
                                        realExt: realExt,
                                    });
                                }
                            }
                        } catch (_) { /* 忽略 */ }
                    }
                } else if (entry.isDirectory()) {
                    const subResults = this._scanImages(fullPath, maxDepth, currentDepth + 1);
                    results.push(...subResults);
                }
            }
        } catch (e) {
            window.PluginManager.log('sticker-import', `[StickerImport] 扫描目录失败: ${dirPath}` + ' ' + e.message, 'warn');
        }

        return results;
    },

    // ---- 导航栏图标 SVG ----
    _getNavIconSvg(color) {
        return `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="${color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>`;
    },

    _getNavIconBase64(color) {
        return btoa(this._getNavIconSvg(color));
    },

    // ---- 注入样式 ----
    _injectStyles() {
        if (document.getElementById(this.STYLE_ID)) return;
        try {
            const cssPath = path.join(__dirname, 'styles.css');
            const cssContent = fs.readFileSync(cssPath, 'utf-8');
            const style = document.createElement('style');
            style.id = this.STYLE_ID;
            style.textContent = cssContent;
            document.head.appendChild(style);
        } catch (e) {
            window.PluginManager.log('sticker-import', '[StickerImport] 加载样式文件失败:' + ' ' + e, 'error');
        }
    },

    // ---- 创建主面板 ----
    _createPanel(instance) {
        const panel = document.createElement('div');
        panel.className = 'sticker-import-panel';
        panel.id = this.PANEL_ID;

        // 头部
        const header = document.createElement('div');
        header.className = 'sticker-import-header';
        header.innerHTML = `
            <div class="sticker-import-header-title">
                😀 表情包导入
            </div>
            <div class="sticker-import-header-actions">
                <button class="sticker-import-btn sticker-import-btn-secondary" id="sticker-import-refresh-btn" title="刷新扫描">
                    🔄 刷新
                </button>
            </div>
        `;
        panel.appendChild(header);

        // Tab 栏
        const tabs = document.createElement('div');
        tabs.className = 'sticker-import-tabs';
        tabs.innerHTML = `
            <div class="sticker-import-tab active" data-tab="scan">📂 扫描导入</div>
            <div class="sticker-import-tab" data-tab="imported">📦 已导入</div>
        `;
        panel.appendChild(tabs);

        // 内容区
        const body = document.createElement('div');
        body.className = 'sticker-import-body';
        body.id = 'sticker-import-body';
        panel.appendChild(body);

        // 底部工具栏（根据当前 Tab 动态切换内容）
        const batchToolbar = document.createElement('div');
        batchToolbar.className = 'sticker-batch-toolbar';
        batchToolbar.id = 'sticker-batch-toolbar';
        panel.appendChild(batchToolbar);

        document.body.appendChild(panel);

        // 事件绑定
        this._bindPanelEvents(panel, instance);

        return panel;
    },

    // ---- 面板事件绑定 ----
    _bindPanelEvents(panel, instance) {
        const self = this;

        // Tab 切换
        panel.querySelectorAll('.sticker-import-tab').forEach(tab => {
            tab.addEventListener('click', () => {
                panel.querySelectorAll('.sticker-import-tab').forEach(t => t.classList.remove('active'));
                tab.classList.add('active');
                const tabName = tab.dataset.tab;
                instance.currentTab = tabName;
                if (tabName === 'scan') {
                    self._renderScanTab(instance);
                } else if (tabName === 'imported') {
                    self._renderImportedTab(instance);
                }
                self._updateBottomToolbar(instance);
            });
        });

        // 刷新按钮
        panel.querySelector('#sticker-import-refresh-btn').addEventListener('click', () => {
            self._performScan(instance);
        });

        // 初始化底部工具栏
        self._updateBottomToolbar(instance);
    },

    // ---- 渲染扫描Tab ----
    _renderScanTab(instance) {
        const body = document.getElementById('sticker-import-body');
        if (!body) return;
        body.innerHTML = '';

        const sources = this._getSources();

        // 渲染各来源卡片
        sources.forEach(source => {
            const card = this._createSourceCard(source, instance);
            body.appendChild(card);
        });
    },

    // ---- 创建来源卡片 ----
    _createSourceCard(source, instance) {
        // 微信使用上传卡片
        if (source.id === 'wechat') {
            return this._createWeChatUploadCard(source, instance);
        }

        const card = document.createElement('div');
        card.className = 'sticker-source-card';
        card.id = `sticker-source-${source.id}`;

        // 检测路径是否存在
        const existingPaths = source.basePaths.filter(p => {
            try { return fs.existsSync(p); } catch (_) { return false; }
        });
        const hasPath = existingPaths.length > 0;

        // 来源信息
        const headerDiv = document.createElement('div');
        headerDiv.className = 'sticker-source-header';

        const infoDiv = document.createElement('div');
        infoDiv.className = 'sticker-source-info';
        infoDiv.innerHTML = `
            <div class="sticker-source-icon ${source.cssClass}">${source.icon}</div>
            <div>
                <div class="sticker-source-name">${source.name}</div>
                <div class="sticker-source-desc">${source.description}</div>
            </div>
        `;

        const statusSpan = document.createElement('span');
        statusSpan.className = `sticker-source-status ${hasPath ? 'found' : 'not-found'}`;
        statusSpan.textContent = hasPath ? '✓ 已检测' : '未安装';

        headerDiv.appendChild(infoDiv);
        headerDiv.appendChild(statusSpan);
        card.appendChild(headerDiv);

        // 路径显示
        if (hasPath) {
            const pathDiv = document.createElement('div');
            pathDiv.className = 'sticker-source-path';
            pathDiv.textContent = existingPaths[0];
            card.appendChild(pathDiv);
        }

        // 操作按钮
        const actionsDiv = document.createElement('div');
        actionsDiv.className = 'sticker-source-actions';

        if (hasPath) {
            const scanBtn = document.createElement('button');
            scanBtn.className = 'sticker-source-btn scan';
            scanBtn.textContent = '🔍 扫描表情';
            scanBtn.addEventListener('click', () => {
                this._scanSource(source, existingPaths, instance, card);
            });
            actionsDiv.appendChild(scanBtn);

            const openBtn = document.createElement('button');
            openBtn.className = 'sticker-source-btn open-dir';
            openBtn.textContent = '📂 打开目录';
            openBtn.addEventListener('click', () => {
                try {
                    const { shell } = require('electron');
                    shell.openPath(existingPaths[0]);
                } catch (e) {
                    window.PluginManager.log('sticker-import', '[StickerImport] 打开目录失败:' + ' ' + e, 'error');
                }
            });
            actionsDiv.appendChild(openBtn);
        }

        card.appendChild(actionsDiv);

        // 扫描结果展示区
        const resultsDiv = document.createElement('div');
        resultsDiv.id = `sticker-results-${source.id}`;
        card.appendChild(resultsDiv);

        return card;
    },

    // ---- 创建微信上传卡片 ----
    _createWeChatUploadCard(source, instance) {
        const card = document.createElement('div');
        card.className = 'sticker-source-card';
        card.id = `sticker-source-${source.id}`;

        // 来源信息头
        const headerDiv = document.createElement('div');
        headerDiv.className = 'sticker-source-header';
        headerDiv.innerHTML = `
            <div class="sticker-source-info">
                <div class="sticker-source-icon ${source.cssClass}">${source.icon}</div>
                <div>
                    <div class="sticker-source-name">${source.name}</div>
                    <div class="sticker-source-desc">选择或拖入图片 / 文件夹导入表情</div>
                </div>
            </div>
        `;
        card.appendChild(headerDiv);

        // 拖拽上传区
        const dropZone = document.createElement('div');
        dropZone.className = 'wechat-upload-zone';
        dropZone.innerHTML = `
            <div class="wechat-upload-icon">📥</div>
            <div class="wechat-upload-text">拖拽图片或文件夹到此处</div>
            <div class="wechat-upload-hint">支持 PNG / JPG / GIF / WebP 格式</div>
            <div class="wechat-upload-actions">
                <button class="sticker-source-btn scan wechat-select-files-btn">🖼️ 选择图片</button>
                <button class="sticker-source-btn open-dir wechat-select-folder-btn">📁 选择文件夹</button>
            </div>
        `;
        card.appendChild(dropZone);

        // 结果展示区
        const resultsDiv = document.createElement('div');
        resultsDiv.id = 'sticker-wechat-upload-result';
        card.appendChild(resultsDiv);

        // ---- 拖拽事件 ----
        ['dragenter', 'dragover'].forEach(evt => {
            dropZone.addEventListener(evt, (e) => {
                e.preventDefault();
                e.stopPropagation();
                dropZone.classList.add('drag-over');
            });
        });
        ['dragleave', 'drop'].forEach(evt => {
            dropZone.addEventListener(evt, (e) => {
                e.preventDefault();
                e.stopPropagation();
                dropZone.classList.remove('drag-over');
            });
        });

        dropZone.addEventListener('drop', (e) => {
            const files = e.dataTransfer.files;
            if (!files || files.length === 0) return;
            const filePaths = [];
            for (let i = 0; i < files.length; i++) {
                filePaths.push(files[i].path);
            }
            this._importWeChatFiles(filePaths, resultsDiv);
        });

        // ---- 选择图片按钮 ----
        dropZone.querySelector('.wechat-select-files-btn').addEventListener('click', () => {
            try {
                const { dialog } = getElectronRemote();
                const result = dialog.showOpenDialogSync({
                    title: '选择要导入的表情图片',
                    properties: ['openFile', 'multiSelections'],
                    filters: [{ name: '图片文件', extensions: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp'] }],
                });
                if (result && result.length > 0) {
                    this._importWeChatFiles(result, resultsDiv);
                }
            } catch (e) {
                window.PluginManager.log('sticker-import', '[StickerImport] 选择文件失败:' + ' ' + e, 'error');
                this._showToast('选择文件失败: ' + e.message, 'error');
            }
        });

        // ---- 选择文件夹按钮 ----
        dropZone.querySelector('.wechat-select-folder-btn').addEventListener('click', () => {
            try {
                const { dialog } = getElectronRemote();
                const result = dialog.showOpenDialogSync({
                    title: '选择表情文件夹',
                    properties: ['openDirectory'],
                    message: '请选择包含表情图片的文件夹',
                });
                if (result && result.length > 0) {
                    this._importWeChatFiles(result, resultsDiv);
                }
            } catch (e) {
                window.PluginManager.log('sticker-import', '[StickerImport] 选择文件夹失败:' + ' ' + e, 'error');
                this._showToast('选择文件夹失败: ' + e.message, 'error');
            }
        });

        return card;
    },

    // ---- 导入微信表情文件（选择/拖拽的文件或文件夹）----
    _importWeChatFiles(filePaths, resultsDiv) {
        const targetDir = path.join(__dirname, 'cache', 'stickers', 'wechat');
        if (!fs.existsSync(targetDir)) {
            fs.mkdirSync(targetDir, { recursive: true });
        }

        let allImages = [];

        for (const fp of filePaths) {
            try {
                const stat = fs.statSync(fp);
                if (stat.isDirectory()) {
                    // 文件夹：递归扫描
                    const images = this._scanImages(fp, 3);
                    allImages = allImages.concat(images);
                } else if (stat.isFile()) {
                    // 单个文件：检查是否为图片
                    const ext = path.extname(fp).toLowerCase();
                    if (this.IMAGE_EXTS.includes(ext) && stat.size > 500) {
                        allImages.push({
                            path: fp,
                            name: path.basename(fp),
                            size: stat.size,
                            ext: ext,
                        });
                    }
                }
            } catch (e) {
                window.PluginManager.log('sticker-import', '[StickerImport] 读取文件失败:' + ' ' + fp + ' ' + e.message, 'warn');
            }
        }

        if (allImages.length === 0) {
            this._showToast('未找到有效的表情图片', 'error');
            return;
        }

        // 去重
        const seen = new Set();
        allImages = allImages.filter(img => {
            const key = img.name + '_' + img.size;
            if (seen.has(key)) return false;
            seen.add(key);
            return true;
        });

        // 复制到插件目录
        let copyCount = 0;
        for (const img of allImages) {
            try {
                let fileName = img.name;
                if (!path.extname(fileName) && img.ext) {
                    fileName = fileName + img.ext;
                }
                const destPath = path.join(targetDir, fileName);
                if (fs.existsSync(destPath)) continue;
                fs.copyFileSync(img.path, destPath);
                copyCount++;
            } catch (e) {
                window.PluginManager.log('sticker-import', '[StickerImport] 复制表情失败:' + ' ' + e.message, 'warn');
            }
        }

        // 显示结果
        if (resultsDiv) {
            if (copyCount > 0) {
                resultsDiv.innerHTML = `
                    <div class="sticker-message success">
                        ✅ 成功导入 ${copyCount} 个微信表情！（共发现 ${allImages.length} 个）
                    </div>
                `;
            } else {
                resultsDiv.innerHTML = `
                    <div class="sticker-message info">
                        💡 所有表情均已导入过，无新增表情。
                    </div>
                `;
            }
        }

        this._showToast(
            copyCount > 0 ? `✅ 成功导入 ${copyCount} 个微信表情` : '所有表情均已导入，无新增',
            copyCount > 0 ? 'success' : 'info'
        );
    },

    // ---- 检测是否为新版微信（xwechat_files，文件加密）----
    _isNewVersionWeChat() {
        const xwBase = path.join(os.homedir(), 'Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files');
        try {
            if (!fs.existsSync(xwBase)) return false;
            const users = fs.readdirSync(xwBase, { withFileTypes: true })
                .filter(d => d.isDirectory() && !['Backup', 'all_users'].includes(d.name));
            return users.length > 0;
        } catch (_) {
            return false;
        }
    },

    // ---- 检测 wxemoticon 是否已安装 ----
    _isWxemoticonInstalled() {
        try {
            execSync('which wxemoticon', { encoding: 'utf-8', env: { ...process.env, PATH: `${process.env.PATH}:${path.join(os.homedir(), '.local', 'bin')}` } });
            return true;
        } catch (_) {
            const localBin = path.join(os.homedir(), '.local', 'bin', 'wxemoticon');
            return fs.existsSync(localBin);
        }
    },

    // ---- 获取 wxemoticon 可执行文件路径 ----
    _getWxemoticonPath() {
        try {
            return execSync('which wxemoticon', { encoding: 'utf-8', env: { ...process.env, PATH: `${process.env.PATH}:${path.join(os.homedir(), '.local', 'bin')}` } }).trim();
        } catch (_) {
            const localBin = path.join(os.homedir(), '.local', 'bin', 'wxemoticon');
            if (fs.existsSync(localBin)) return localBin;
            return null;
        }
    },

    // ---- 弹窗提示安装 wxemoticon ----
    _showWxemoticonInstallDialog(onInstalled) {
        const existing = document.getElementById('wxemoticon-install-dialog');
        if (existing) existing.remove();

        const overlay = document.createElement('div');
        overlay.className = 'sticker-preview-overlay wxemoticon-overlay';
        overlay.id = 'wxemoticon-install-dialog';

        const localScript = path.join(__dirname, 'install-wxemoticon.sh');

        const dialog = document.createElement('div');
        dialog.className = 'wxemoticon-dialog';
        dialog.innerHTML = `
            <div class="wxemoticon-dialog-title">🔧 需要安装 wxemoticon 工具</div>
            <div class="wxemoticon-dialog-content">
                <p>微信 4.x 版本的表情包文件已加密，需要借助 <b>wxemoticon</b> 工具来导出收藏的表情。</p>

                <div class="wxemoticon-install-section">
                    <button class="sticker-import-btn sticker-import-btn-primary wxemoticon-auto-install-btn" style="width:100%;justify-content:center;padding:10px 0;font-size:13px;margin-top:12px;">
                        🚀 一键安装（国内加速）
                    </button>
                    <div class="wxemoticon-install-progress" style="display:none;margin-top:10px;">
                        <div class="sticker-loading" style="padding:12px;">
                            <div class="sticker-loading-spinner"></div>
                            <div class="sticker-loading-text">正在安装 wxemoticon...</div>
                        </div>
                    </div>
                    <div class="wxemoticon-install-result" style="display:none;margin-top:10px;"></div>
                </div>

                <details class="wxemoticon-manual-section" style="margin-top:12px;">
                    <summary style="font-size:12px;color:#999;cursor:pointer;user-select:none;">📋 手动安装（复制命令到终端执行）</summary>
                    <div class="wxemoticon-dialog-code" style="margin-top:8px;">
                        <code>bash "${localScript}"</code>
                        <button class="wxemoticon-copy-btn" title="复制命令">📋</button>
                    </div>
                </details>

                <p class="wxemoticon-dialog-hint">💡 安装完成后，点击「我已安装」继续导出。</p>
            </div>
            <div class="wxemoticon-dialog-actions">
                <button class="sticker-import-btn sticker-import-btn-secondary wxemoticon-cancel-btn">取消</button>
                <button class="sticker-import-btn sticker-import-btn-primary wxemoticon-confirm-btn">✅ 我已安装</button>
            </div>
        `;

        overlay.appendChild(dialog);
        document.body.appendChild(overlay);
        requestAnimationFrame(() => overlay.classList.add('visible'));

        // 一键安装按钮
        const autoInstallBtn = dialog.querySelector('.wxemoticon-auto-install-btn');
        const progressDiv = dialog.querySelector('.wxemoticon-install-progress');
        const resultDiv = dialog.querySelector('.wxemoticon-install-result');

        autoInstallBtn.addEventListener('click', () => {
            autoInstallBtn.style.display = 'none';
            progressDiv.style.display = 'block';
            resultDiv.style.display = 'none';

            const envPath = `${process.env.PATH}:${path.join(os.homedir(), '.local', 'bin')}`;
            exec(`bash "${localScript}"`, {
                timeout: 120000,
                env: { ...process.env, PATH: envPath },
            }, (error, stdout, stderr) => {
                progressDiv.style.display = 'none';
                resultDiv.style.display = 'block';

                if (error) {
                    window.PluginManager.log('sticker-import', '[StickerImport] wxemoticon 安装失败:' + ' ' + error.message, 'error');
                    window.PluginManager.log('sticker-import', '[StickerImport] stderr:' + ' ' + stderr, 'error');
                    resultDiv.innerHTML = `
                        <div class="sticker-message error" style="margin:0;">
                            ❌ 自动安装失败<br>
                            <span style="font-size:11px;color:#999;display:block;margin-top:4px;">${(stderr || error.message).substring(0, 150)}</span>
                            <span style="font-size:11px;display:block;margin-top:4px;">请尝试手动安装（展开下方命令）</span>
                        </div>
                    `;
                    autoInstallBtn.style.display = 'flex';
                    autoInstallBtn.textContent = '🔄 重试安装';
                } else {
                    window.PluginManager.log('sticker-import', '[StickerImport] wxemoticon 安装成功:' + ' ' + stdout + ' ' + stderr);
                    resultDiv.innerHTML = `
                        <div class="sticker-message success" style="margin:0;">
                            ✅ wxemoticon 安装成功！点击「我已安装」继续。
                        </div>
                    `;
                }
            });
        });

        // 复制手动命令按钮
        dialog.querySelector('.wxemoticon-copy-btn').addEventListener('click', () => {
            const cmd = `bash "${localScript}"`;
            try {
                require('electron').clipboard.writeText(cmd);
                this._showToast('✅ 已复制到剪贴板', 'success');
            } catch (e) {
                window.PluginManager.log('sticker-import', '[StickerImport] 复制失败:' + ' ' + e, 'error');
            }
        });

        // 取消
        dialog.querySelector('.wxemoticon-cancel-btn').addEventListener('click', () => {
            overlay.classList.remove('visible');
            setTimeout(() => overlay.remove(), 200);
        });

        // 点击背景关闭
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) {
                overlay.classList.remove('visible');
                setTimeout(() => overlay.remove(), 200);
            }
        });

        // 确认已安装
        dialog.querySelector('.wxemoticon-confirm-btn').addEventListener('click', () => {
            if (this._isWxemoticonInstalled()) {
                overlay.classList.remove('visible');
                setTimeout(() => overlay.remove(), 200);
                onInstalled();
            } else {
                this._showToast('❌ 未检测到 wxemoticon 命令，请确认已正确安装', 'error');
            }
        });
    },

    // ---- 通过 wxemoticon 导出微信表情 ----
    _exportViaWxemoticon(instance, card) {
        const statusEl = card.querySelector('.sticker-source-status');
        const scanBtn = card.querySelector('.sticker-source-btn.scan');
        const resultsDiv = card.querySelector('#sticker-results-wechat');

        if (statusEl) {
            statusEl.className = 'sticker-source-status scanning';
            statusEl.textContent = '⏳ 导出中...';
        }
        if (scanBtn) scanBtn.disabled = true;

        if (resultsDiv) {
            resultsDiv.innerHTML = `
                <div class="sticker-loading">
                    <div class="sticker-loading-spinner"></div>
                    <div class="sticker-loading-text">
                        正在通过 wxemoticon 导出微信表情包...<br>
                        <span style="font-size:11px;color:#999;margin-top:4px;display:block;">⏱️ 首次运行可能需要等待较长时间，请耐心等待</span>
                    </div>
                </div>
            `;
        }

        const wxemoticonPath = this._getWxemoticonPath();
        const targetDir = path.join(__dirname, 'cache', 'stickers', 'wechat');
        if (!fs.existsSync(targetDir)) {
            fs.mkdirSync(targetDir, { recursive: true });
        }

        const envPath = `${process.env.PATH}:${path.join(os.homedir(), '.local', 'bin')}`;
        const exportCmd = `"${wxemoticonPath}" export --no-interactive --flat --skip-existing`;

        window.PluginManager.log('sticker-import', '[StickerImport] Running wxemoticon:' + ' ' + exportCmd);

        exec(exportCmd, {
            timeout: 300000,
            env: { ...process.env, PATH: envPath },
            maxBuffer: 10 * 1024 * 1024,
        }, (error, stdout, stderr) => {
            window.PluginManager.log('sticker-import', '[StickerImport] wxemoticon stdout:' + ' ' + stdout);
            if (stderr) window.PluginManager.log('sticker-import', '[StickerImport] wxemoticon stderr:' + ' ' + stderr);

            if (error) {
                window.PluginManager.log('sticker-import', '[StickerImport] wxemoticon export failed:' + ' ' + error.message, 'error');
                if (statusEl) {
                    statusEl.className = 'sticker-source-status not-found';
                    statusEl.textContent = '导出失败';
                }
                if (scanBtn) scanBtn.disabled = false;
                if (resultsDiv) {
                    resultsDiv.innerHTML = `
                        <div class="sticker-message error">
                            ❌ wxemoticon 导出失败<br>
                            <span style="font-size:11px;color:#999;display:block;margin-top:4px;">${error.message.substring(0, 200)}</span>
                            <span style="font-size:11px;display:block;margin-top:8px;">💡 建议先在终端手动执行 <code>wxemoticon export</code> 确认工具正常后再试</span>
                        </div>
                    `;
                }
                return;
            }

            // 查找最新的导出目录
            const downloadsDir = path.join(os.homedir(), 'Downloads');
            let exportDir = null;
            try {
                const dirs = fs.readdirSync(downloadsDir, { withFileTypes: true })
                    .filter(d => d.isDirectory() && d.name.startsWith('微信表情包_导出_'))
                    .map(d => ({
                        name: d.name,
                        fullPath: path.join(downloadsDir, d.name),
                        mtime: fs.statSync(path.join(downloadsDir, d.name)).mtimeMs,
                    }))
                    .sort((a, b) => b.mtime - a.mtime);
                if (dirs.length > 0) {
                    exportDir = dirs[0].fullPath;
                }
            } catch (e) {
                window.PluginManager.log('sticker-import', '[StickerImport] 查找导出目录出错:' + ' ' + e.message, 'warn');
            }

            if (!exportDir) {
                if (statusEl) {
                    statusEl.className = 'sticker-source-status not-found';
                    statusEl.textContent = '未找到';
                }
                if (scanBtn) scanBtn.disabled = false;
                if (resultsDiv) {
                    resultsDiv.innerHTML = `
                        <div class="sticker-message error">
                            ❌ 导出可能已完成，但未在 ~/Downloads/ 找到导出目录。
                        </div>
                    `;
                }
                return;
            }

            window.PluginManager.log('sticker-import', '[StickerImport] 找到导出目录:' + ' ' + exportDir);

            // 扫描导出目录
            const images = this._scanImages(exportDir, 3);

            // 复制到插件目录
            let copyCount = 0;
            for (const img of images) {
                try {
                    let fileName = img.name;
                    const fileExt = path.extname(fileName).toLowerCase();
                    if (!fileExt && img.ext) {
                        fileName = fileName + img.ext;
                    } else if (fileExt && img.realExt && img.realExt !== fileExt) {
                        fileName = fileName.replace(/\.[^.]+$/, img.realExt);
                    }
                    const destPath = path.join(targetDir, fileName);
                    if (fs.existsSync(destPath)) continue;
                    fs.copyFileSync(img.path, destPath);
                    copyCount++;
                } catch (e) {
                    window.PluginManager.log('sticker-import', '[StickerImport] 复制表情失败:' + ' ' + e.message, 'warn');
                }
            }

            // 更新 UI 状态
            if (statusEl) {
                statusEl.className = `sticker-source-status ${images.length > 0 ? 'found' : 'not-found'}`;
                statusEl.textContent = images.length > 0 ? `✓ 导入 ${copyCount} 个` : '未发现表情';
            }
            if (scanBtn) scanBtn.disabled = false;

            if (resultsDiv) {
                if (copyCount > 0) {
                    resultsDiv.innerHTML = `
                        <div class="sticker-message success">
                            ✅ 成功导入 ${copyCount} 个微信表情包！（共发现 ${images.length} 个）
                        </div>
                    `;
                } else if (images.length > 0) {
                    resultsDiv.innerHTML = `
                        <div class="sticker-message info">
                            💡 所有表情均已导入过，无新增表情（共 ${images.length} 个）。
                        </div>
                    `;
                } else {
                    resultsDiv.innerHTML = `
                        <div class="sticker-message info">
                            💡 未在导出目录中发现表情图片。
                        </div>
                    `;
                }
            }

            this._showToast(
                copyCount > 0 ? `✅ 成功导入 ${copyCount} 个微信表情` : '所有表情均已导入，无新增',
                copyCount > 0 ? 'success' : 'info'
            );
        });
    },

    // ---- 扫描单个来源 ----
    _scanSource(source, existingPaths, instance, card) {
        const resultsDiv = card.querySelector(`#sticker-results-${source.id}`);
        if (!resultsDiv) return;

        // 显示加载状态
        const statusEl = card.querySelector('.sticker-source-status');
        if (statusEl) {
            statusEl.className = 'sticker-source-status scanning';
            statusEl.textContent = '⏳ 扫描中...';
        }

        // 禁用扫描按钮
        const scanBtn = card.querySelector('.sticker-source-btn.scan');
        if (scanBtn) scanBtn.disabled = true;

        resultsDiv.innerHTML = `
            <div class="sticker-loading">
                <div class="sticker-loading-spinner"></div>
                <div class="sticker-loading-text">正在扫描 ${source.name} 表情文件...</div>
            </div>
        `;

        // 异步扫描
        setTimeout(() => {
            let allImages = [];
            for (const p of existingPaths) {
                const images = this._scanImages(p);
                allImages = allImages.concat(images);
            }

            // 去重（按文件名）
            const seen = new Set();
            allImages = allImages.filter(img => {
                const key = img.name + '_' + img.size;
                if (seen.has(key)) return false;
                seen.add(key);
                return true;
            });


            // 更新状态
            if (statusEl) {
                statusEl.className = `sticker-source-status ${allImages.length > 0 ? 'found' : 'not-found'}`;
                statusEl.textContent = allImages.length > 0 ? `✓ 发现 ${allImages.length} 个` : '未发现表情';
            }
            if (scanBtn) scanBtn.disabled = false;

            // 存储扫描结果
            instance.scanResults[source.id] = allImages;
            instance.selectedStickers[source.id] = new Set();

            // 渲染结果
            this._renderScanResults(source.id, allImages, instance, resultsDiv);
        }, 300);
    },

    // ---- 渲染扫描结果 ----
    _renderScanResults(sourceId, images, instance, container) {
        container.innerHTML = '';

        if (images.length === 0) {
            container.innerHTML = `
                <div class="sticker-message info">
                    💡 未找到表情文件。可尝试手动选择表情目录。
                </div>
            `;
            return;
        }

        // 全选/反选 栏
        const selectBar = document.createElement('div');
        selectBar.className = 'sticker-select-bar';
        selectBar.innerHTML = `
            <div class="sticker-select-bar-left">
                <label>
                    <input type="checkbox" id="sticker-select-all-${sourceId}"> 全选
                </label>
                <span class="sticker-count-badge">${images.length} 个表情</span>
            </div>
        `;

        const selectAllCheckbox = selectBar.querySelector(`#sticker-select-all-${sourceId}`);
        selectAllCheckbox.addEventListener('change', () => {
            const checked = selectAllCheckbox.checked;
            if (checked) {
                images.forEach((_, idx) => instance.selectedStickers[sourceId].add(idx));
            } else {
                instance.selectedStickers[sourceId].clear();
            }
            this._refreshStickerSelection(sourceId, instance);
            this._updateBatchToolbar(instance);
        });

        container.appendChild(selectBar);

        // 表情网格
        const grid = document.createElement('div');
        grid.className = 'sticker-grid';
        grid.id = `sticker-grid-${sourceId}`;

        const maxShow = 200; // 最多展示200个
        const showImages = images.slice(0, maxShow);

        showImages.forEach((img, idx) => {
            const item = document.createElement('div');
            item.className = 'sticker-grid-item';
            item.dataset.idx = idx;
            item.dataset.source = sourceId;

            const imgEl = document.createElement('img');
            imgEl.loading = 'lazy';
            // 使用 file:// 协议或 base64 加载本地图片
            imgEl.src = `file://${img.path}`;
            imgEl.alt = img.name;
            imgEl.onerror = () => {
                imgEl.replaceWith(Object.assign(document.createElement('div'), {
                    className: 'sticker-error',
                    textContent: '🖼️',
                }));
            };

            item.appendChild(imgEl);

            // 点击选中/取消
            item.addEventListener('click', (e) => {
                e.stopPropagation();
                const selected = instance.selectedStickers[sourceId];
                if (selected.has(idx)) {
                    selected.delete(idx);
                    item.classList.remove('selected');
                } else {
                    selected.add(idx);
                    item.classList.add('selected');
                }
                this._updateBatchToolbar(instance);
                // 更新全选 checkbox
                selectAllCheckbox.checked = selected.size === images.length;
            });

            // 双击预览
            item.addEventListener('dblclick', (e) => {
                e.stopPropagation();
                this._showPreview(img.path);
            });

            grid.appendChild(item);
        });

        // 包裹在可滚动容器中，限制高度
        const gridWrapper = document.createElement('div');
        gridWrapper.className = 'sticker-grid-scroll-wrapper';
        gridWrapper.appendChild(grid);
        container.appendChild(gridWrapper);

        if (images.length > maxShow) {
            const moreHint = document.createElement('div');
            moreHint.className = 'sticker-message info';
            moreHint.textContent = `💡 共 ${images.length} 个表情，当前展示前 ${maxShow} 个`;
            container.appendChild(moreHint);
        }
    },

    // ---- 刷新选择状态 ----
    _refreshStickerSelection(sourceId, instance) {
        const grid = document.getElementById(`sticker-grid-${sourceId}`);
        if (!grid) return;

        grid.querySelectorAll('.sticker-grid-item').forEach(item => {
            const idx = parseInt(item.dataset.idx, 10);
            if (instance.selectedStickers[sourceId] && instance.selectedStickers[sourceId].has(idx)) {
                item.classList.add('selected');
            } else {
                item.classList.remove('selected');
            }
        });
    },

    // ---- 更新底部工具栏（根据当前 Tab 切换内容）----
    _updateBottomToolbar(instance) {
        const toolbar = document.getElementById('sticker-batch-toolbar');
        if (!toolbar) return;
        const self = this;

        if (instance.currentTab === 'imported') {
            // 已导入 Tab：显示表情统计 + 清空全部按钮
            const qqStickers = this._getLocalStickers('qq');
            const wxStickers = this._getLocalStickers('wechat');
            const total = qqStickers.length + wxStickers.length;
            toolbar.innerHTML = `
                <span class="sticker-batch-info">共 ${total} 个表情</span>
                <div class="sticker-batch-actions">
                    <button class="sticker-import-btn sticker-import-btn-resync" id="sticker-clear-all-btn" ${total === 0 ? 'disabled' : ''}>
                        🗑️ 清空全部
                    </button>
                </div>
            `;
            const clearBtn = toolbar.querySelector('#sticker-clear-all-btn');
            if (clearBtn) {
                clearBtn.addEventListener('click', () => {
                    self._clearAllStickers(instance);
                });
            }
        } else {
            // 扫描导入 Tab：显示已选数量 + 导入选中按钮
            let total = 0;
            for (const [, selected] of Object.entries(instance.selectedStickers)) {
                total += selected.size;
            }
            toolbar.innerHTML = `
                <span class="sticker-batch-info" id="sticker-batch-count">已选 ${total} 个</span>
                <div class="sticker-batch-actions">
                    <button class="sticker-import-btn sticker-import-btn-primary" id="sticker-batch-import-btn" ${total === 0 ? 'disabled' : ''}>
                        📥 导入选中
                    </button>
                </div>
            `;
            const importBtn = toolbar.querySelector('#sticker-batch-import-btn');
            if (importBtn) {
                importBtn.addEventListener('click', () => {
                    self._batchImport(instance);
                });
            }
        }
    },

    // ---- 更新批量工具栏选中数量 ----
    _updateBatchToolbar(instance) {
        if (instance.currentTab !== 'scan') return;
        const countEl = document.getElementById('sticker-batch-count');
        const importBtn = document.getElementById('sticker-batch-import-btn');
        if (!countEl) return;

        let total = 0;
        for (const [, selected] of Object.entries(instance.selectedStickers)) {
            total += selected.size;
        }

        countEl.textContent = `已选 ${total} 个`;
        if (importBtn) {
            importBtn.disabled = total === 0;
        }
    },

    // ---- 预览表情 ----
    _showPreview(imagePath) {
        // 移除已有预览层
        const existing = document.getElementById('sticker-preview-overlay');
        if (existing) existing.remove();

        const overlay = document.createElement('div');
        overlay.className = 'sticker-preview-overlay';
        overlay.id = 'sticker-preview-overlay';

        const content = document.createElement('div');
        content.className = 'sticker-preview-content';
        const img = document.createElement('img');
        img.src = `file://${imagePath}`;
        img.alt = '表情预览';
        content.appendChild(img);

        overlay.appendChild(content);
        document.body.appendChild(overlay);

        requestAnimationFrame(() => overlay.classList.add('visible'));

        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) {
                overlay.classList.remove('visible');
                setTimeout(() => overlay.remove(), 200);
            }
        });
    },

    // ---- 手动选择文件夹 ----
    _selectFolder(instance) {
        try {
            const { dialog } = getElectronRemote();
            const result = dialog.showOpenDialogSync({
                title: '选择表情包文件夹',
                properties: ['openDirectory'],
                message: '请选择包含表情图片的文件夹',
            });

            if (result && result.length > 0) {
                const folderPath = result[0];
                window.PluginManager.log('sticker-import', '[StickerImport] 手动选择文件夹:' + ' ' + folderPath);

                // 扫描选中的文件夹
                const images = this._scanImages(folderPath, 5);
                if (images.length === 0) {
                    this._showToast('未在该文件夹中找到表情图片', 'error');
                    return;
                }

                // 创建一个虚拟来源卡片
                const sourceId = 'manual_' + Date.now();
                instance.scanResults[sourceId] = images;
                instance.selectedStickers[sourceId] = new Set();

                const body = document.getElementById('sticker-import-body');
                if (!body) return;

                // 在手动导入区域前插入结果卡片
                const card = document.createElement('div');
                card.className = 'sticker-source-card';

                const headerDiv = document.createElement('div');
                headerDiv.className = 'sticker-source-header';
                headerDiv.innerHTML = `
                    <div class="sticker-source-info">
                        <div class="sticker-source-icon folder">📁</div>
                        <div>
                            <div class="sticker-source-name">自定义文件夹</div>
                            <div class="sticker-source-desc">${path.basename(folderPath)}</div>
                        </div>
                    </div>
                    <span class="sticker-source-status found">✓ 发现 ${images.length} 个</span>
                `;
                card.appendChild(headerDiv);

                const pathDiv = document.createElement('div');
                pathDiv.className = 'sticker-source-path';
                pathDiv.textContent = folderPath;
                card.appendChild(pathDiv);

                const resultsDiv = document.createElement('div');
                resultsDiv.id = `sticker-results-${sourceId}`;
                card.appendChild(resultsDiv);

                // 插入到手动导入区域前
                const manualEl = body.querySelector('.sticker-manual-import');
                if (manualEl) {
                    body.insertBefore(card, manualEl);
                } else {
                    body.appendChild(card);
                }

                this._renderScanResults(sourceId, images, instance, resultsDiv);
                this._showToast(`发现 ${images.length} 个表情文件`, 'success');
            }
        } catch (e) {
            window.PluginManager.log('sticker-import', '[StickerImport] 选择文件夹失败:' + ' ' + e, 'error');
            this._showToast('选择文件夹失败: ' + e.message, 'error');
        }
    },

    // ---- 批量导入 ----
    async _batchImport(instance) {
        // 按 sourceId 分组导入
        let totalCount = 0;
        let hasSelection = false;

        for (const [sourceId, selected] of Object.entries(instance.selectedStickers)) {
            if (selected.size === 0) continue;
            hasSelection = true;
            const images = instance.scanResults[sourceId];
            if (!images) continue;
            const stickersToImport = [];
            for (const idx of selected) {
                if (images[idx]) stickersToImport.push(images[idx]);
            }
            if (stickersToImport.length === 0) continue;

            const results = await this._callImportAPI(stickersToImport, sourceId);
            if (results.success) totalCount += results.count;
        }

        if (!hasSelection) {
            alert('请先选择要导入的表情');
            return;
        }

        if (totalCount > 0) {
            alert(`✅ 成功导入 ${totalCount} 个表情！\n\n请到「已导入」面板或表情面板中查看。`);
        } else {
            alert('所有表情均已导入过，无新增表情。');
        }

        // 清除选择状态
        for (const sourceId of Object.keys(instance.selectedStickers)) {
            instance.selectedStickers[sourceId].clear();
            this._refreshStickerSelection(sourceId, instance);
        }
        this._updateBatchToolbar(instance);
    },

    /**
     * 将表情文件复制到插件的 cache/stickers 目录
     * 
     * @param {Array<{path: string, name: string, size: number, ext: string}>} stickers - 要导入的表情列表
     * @param {string} sourceId - 来源 ID（qq / wechat / manual_xxx）
     * @returns {Promise<{success: boolean, count?: number, message?: string}>}
     */
    async _callImportAPI(stickers, sourceId) {
        // 根据 sourceId 决定目标文件夹
        let targetFolder = 'qq';
        if (sourceId.startsWith('wechat') || sourceId === 'wechat') {
            targetFolder = 'wechat';
        } else if (sourceId.startsWith('qq') || sourceId === 'qq') {
            targetFolder = 'qq';
        }
        const targetDir = path.join(__dirname, 'cache', 'stickers', targetFolder);
        if (!fs.existsSync(targetDir)) {
            fs.mkdirSync(targetDir, { recursive: true });
        }

        window.PluginManager.log('sticker-import', `[StickerImport] 📁 复制 ${stickers.length} 个表情到 ${targetDir}`);

        let copyCount = 0;
        for (const sticker of stickers) {
            try {
                // 确保文件名有扩展名
                let fileName = sticker.name;
                const fileExt = path.extname(fileName).toLowerCase();
                if (!fileExt && sticker.ext) {
                    fileName = fileName + sticker.ext;
                } else if (fileExt && sticker.realExt && sticker.realExt !== fileExt) {
                    // 文件扩展名与实际格式不匹配时，用真实扩展名替换
                    fileName = fileName.replace(/\.[^.]+$/, sticker.realExt);
                }
                const destPath = path.join(targetDir, fileName);
                // 跳过已存在的同名文件
                if (fs.existsSync(destPath)) continue;
                fs.copyFileSync(sticker.path, destPath);
                copyCount++;
            } catch (e) {
                window.PluginManager.log('sticker-import', `[StickerImport] 复制文件失败: ${sticker.path}` + ' ' + e.message, 'warn');
            }
        }

        return { success: true, count: copyCount };
    },

    // ---- 读取本地 cache/stickers 文件夹 ----
    _getLocalStickers(type) {
        const dir = path.join(__dirname, 'cache', 'stickers', type);
        if (!fs.existsSync(dir)) return [];
        const results = [];
        try {
            const files = fs.readdirSync(dir, { withFileTypes: true });
            for (const f of files) {
                if (!f.isFile() || f.name.startsWith('.')) continue;
                const ext = path.extname(f.name).toLowerCase();
                if (this.IMAGE_EXTS.includes(ext) || !ext) {
                    results.push({
                        path: path.join(dir, f.name),
                        name: f.name,
                    });
                }
            }
        } catch (e) {
            window.PluginManager.log('sticker-import', `[StickerImport] 读取本地表情目录失败: ${dir}` + ' ' + e.message, 'warn');
        }
        return results;
    },

    /**
     * 修复存量表情文件的扩展名
     * 扫描 cache/stickers 目录，通过 magic bytes 检测文件真实格式，
     * 将扩展名不匹配的文件重命名
     */
    _fixExistingStickerExtensions() {
        const stickersDir = path.join(__dirname, 'cache', 'stickers');
        let fixCount = 0;
        for (const sub of ['qq', 'wechat']) {
            const dir = path.join(stickersDir, sub);
            if (!fs.existsSync(dir)) continue;
            try {
                const files = fs.readdirSync(dir, { withFileTypes: true });
                for (const f of files) {
                    if (!f.isFile() || f.name.startsWith('.')) continue;
                    const fullPath = path.join(dir, f.name);
                    const ext = path.extname(f.name).toLowerCase();
                    if (!ext || !this.IMAGE_EXTS.includes(ext)) continue;

                    const realExt = this._detectRealExt(fullPath);
                    if (realExt && realExt !== ext) {
                        const newName = f.name.replace(/\.[^.]+$/, realExt);
                        const newPath = path.join(dir, newName);
                        // 如果目标文件名已存在则跳过（避免覆盖）
                        if (fs.existsSync(newPath)) {
                            window.PluginManager.log('sticker-import', `[StickerImport] 跳过重命名（目标已存在）: ${f.name} -> ${newName}`, 'warn');
                            continue;
                        }
                        fs.renameSync(fullPath, newPath);
                        fixCount++;
                        window.PluginManager.log('sticker-import', `[StickerImport] 修正扩展名: ${f.name} -> ${newName}`);
                    }
                }
            } catch (e) {
                window.PluginManager.log('sticker-import', `[StickerImport] 修复存量扩展名失败: ${dir}` + ' ' + e.message, 'warn');
            }
        }
        if (fixCount > 0) {
            window.PluginManager.log('sticker-import', `[StickerImport] 共修正 ${fixCount} 个文件的扩展名`);
        }
    },

    // ---- 在表情面板中渲染表情网格 ----
    _renderEmojiPanelStickers(type) {
        const emojiBox = document.querySelector('.rong-emoji-box');
        if (!emojiBox) return;

        const stickers = this._getLocalStickers(type);

        // 隐藏原有面板
        const origPanel = emojiBox.querySelector('.rong-emoji-panel');
        if (origPanel) origPanel.style.display = 'none';
        const collectPanel = emojiBox.querySelector('.rong-emoji-collect-panel');
        if (collectPanel) collectPanel.style.display = 'none';

        // 移除旧的自定义面板
        const oldCustom = emojiBox.querySelector('.sticker-emoji-custom-panel');
        if (oldCustom) oldCustom.remove();

        // 创建自定义面板（复用 rong-emoji-panel 的默认样式来获得正确高度）
        const panel = document.createElement('div');
        panel.className = 'rong-emoji-panel sticker-emoji-custom-panel';
        panel.style.cssText = 'overflow-y: auto; overflow-x: hidden;';

        const ul = document.createElement('ul');
        ul.style.cssText = 'display: grid; grid-template-columns: repeat(5, 1fr); list-style: none; padding: 8px; margin: 0; gap: 6px; align-content: flex-start;';

        if (stickers.length === 0) {
            const emptyLi = document.createElement('li');
            emptyLi.style.cssText = 'width: 100%; text-align: center; padding: 20px 0; color: #999; font-size: 12px;';
            emptyLi.textContent = `暂无${type === 'qq' ? 'QQ' : '微信'}表情，请在「表情管理」中导入`;
            ul.appendChild(emptyLi);
        } else {
            stickers.forEach(sticker => {
                const li = document.createElement('li');
                li.title = sticker.name;
                li.style.cssText = 'width: 100%; position: relative; padding-bottom: 100%; cursor: pointer; border-radius: 6px; overflow: hidden; transition: transform 0.15s;';
                li.addEventListener('mouseenter', () => { li.style.transform = 'scale(1.15)'; li.style.zIndex = '1'; });
                li.addEventListener('mouseleave', () => { li.style.transform = ''; li.style.zIndex = ''; });
                const img = document.createElement('img');
                img.src = `file://${sticker.path}`;
                img.style.cssText = 'position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; display: block;';
                img.alt = sticker.name;
                img.onerror = () => { li.style.display = 'none'; };

                // 点击发送表情（作为图片消息）
                li.addEventListener('click', () => {
                    this._sendStickerAsImage(sticker.path);
                });

                li.appendChild(img);
                ul.appendChild(li);
            });
        }

        panel.appendChild(ul);
        // 插入到 emojiBox 的第一个子元素位置
        emojiBox.insertBefore(panel, emojiBox.firstChild);
    },

    // ---- 发送表情为图片消息 ----
    _sendStickerAsImage(imagePath) {
        try {
            // 读取图片文件
            const ext = path.extname(imagePath).toLowerCase().replace('.', '') || 'png';
            const mimeType = ext === 'jpg' ? 'image/jpeg' : `image/${ext}`;
            const imageBuffer = fs.readFileSync(imagePath);
            const fileName = path.basename(imagePath);

            // 关闭表情面板
            const emojiBox = document.querySelector('.rong-emoji-box');
            if (emojiBox) emojiBox.style.display = 'none';

            // 找到工具栏上的文件 input（原生文件上传入口）
            const fileInput = document.querySelector('.rong-toolbar-item input[type="file"]');
            if (!fileInput) {
                this._showToast('未找到文件发送入口', 'error');
                return;
            }

            // 从 Buffer 创建 File 对象
            const blob = new Blob([imageBuffer], { type: mimeType });
            const file = new File([blob], fileName, { type: mimeType, lastModified: Date.now() });
            // Electron 需要 path 属性指向本地文件路径（解决 GIF 的 path.join 报错）
            Object.defineProperty(file, 'path', { value: imagePath, writable: false });

            // 临时跳过文件发送确认弹框（自动确认）
            const origConfirm = RongIM.dialog && RongIM.dialog.fileSendConfirm;
            if (RongIM.dialog) {
                RongIM.dialog.fileSendConfirm = function(_param, _ctx, callback) {
                    if (callback) callback();
                };
            }

            // 通过 DataTransfer 设置到 input 上
            const dataTransfer = new DataTransfer();
            dataTransfer.items.add(file);
            fileInput.files = dataTransfer.files;

            // 触发 change 事件，走应用原生的文件发送流程
            const changeEvent = new Event('change', { bubbles: true });
            fileInput.dispatchEvent(changeEvent);

            // 恢复原始确认弹框
            setTimeout(() => {
                if (origConfirm && RongIM.dialog) {
                    RongIM.dialog.fileSendConfirm = origConfirm;
                }
            }, 500);

            window.PluginManager.log('sticker-import', '[StickerImport] ✅ 已触发文件发送:' + ' ' + fileName);
        } catch (e) {
            window.PluginManager.log('sticker-import', '[StickerImport] 发送表情失败:' + ' ' + e, 'error');
            this._showToast('发送表情失败: ' + e.message, 'error');
        }
    },

    // ---- 注入表情面板标签 ----
    _injectEmojiPanelTabs(instance) {
        const self = this;
        const CHECK_INTERVAL = 500;
        const MAX_ATTEMPTS = 120;
        let attempts = 0;
        let injected = false;

        const doInject = () => {
            if (injected) return;

            const tabList = document.querySelector('.rong-emoji-list-ul');
            if (!tabList) {
                attempts++;
                if (attempts < MAX_ATTEMPTS) {
                    setTimeout(doInject, CHECK_INTERVAL);
                }
                return;
            }

            // 已经注入过则跳过
            if (tabList.querySelector('.sticker-tab-qq')) return;
            injected = true;

            // 创建 QQ表情 标签
            const qqTab = document.createElement('li');
            qqTab.title = 'QQ表情';
            qqTab.className = 'rong-emoji-classify-item sticker-tab-qq l';
            qqTab.textContent = '🐧';
            qqTab.style.cssText = 'cursor: pointer; font-size: 25px; display: flex; align-items: center; justify-content: center;';

            // 创建 微信表情 标签
            const wxTab = document.createElement('li');
            wxTab.title = '微信表情';
            wxTab.className = 'rong-emoji-classify-item sticker-tab-wechat l';
            wxTab.textContent = '💬';
            wxTab.style.cssText = 'cursor: pointer; font-size: 25px; display: flex; align-items: center; justify-content: center;';

            // 创建 表情管理 标签
            const manageTab = document.createElement('li');
            manageTab.title = '表情管理';
            manageTab.className = 'rong-emoji-classify-item sticker-tab-manage l';
            manageTab.textContent = '⚙️';
            manageTab.style.cssText = 'cursor: pointer; font-size: 25px; display: flex; align-items: center; justify-content: center;';

            // 点击事件 - 切换 active 状态
            const setActive = (clickedTab) => {
                tabList.querySelectorAll('.rong-emoji-classify-item').forEach(t => t.classList.remove('active'));
                clickedTab.classList.add('active');
            };

            qqTab.addEventListener('click', (e) => {
                e.stopPropagation();
                setActive(qqTab);
                self._renderEmojiPanelStickers('qq');
            });

            wxTab.addEventListener('click', (e) => {
                e.stopPropagation();
                setActive(wxTab);
                self._renderEmojiPanelStickers('wechat');
            });

            manageTab.addEventListener('click', (e) => {
                e.stopPropagation();
                setActive(manageTab);
                // 打开管理面板
                if (instance.panel) {
                    if (instance.panel.classList.contains('visible')) {
                        self._hidePanel(instance.panel);
                    } else {
                        if (instance.currentTab === 'scan') {
                            self._renderScanTab(instance);
                        } else {
                            self._renderImportedTab(instance);
                        }
                        self._showPanel(instance.panel);
                    }
                }
            });

            // 给原有的 tab 添加事件，点击时恢复原面板
            tabList.querySelectorAll('.rong-emoji-classify-item:not(.sticker-tab-qq):not(.sticker-tab-wechat):not(.sticker-tab-manage)').forEach(origTab => {
                origTab.addEventListener('click', () => {
                    // 移除新增标签的选中状态
                    [qqTab, wxTab, manageTab].forEach(t => t.classList.remove('active'));
                    // 移除自定义面板
                    const emojiBox = document.querySelector('.rong-emoji-box');
                    if (emojiBox) {
                        const customPanel = emojiBox.querySelector('.sticker-emoji-custom-panel');
                        if (customPanel) customPanel.remove();
                        // 恢复原面板
                        const origPanel = emojiBox.querySelector('.rong-emoji-panel:not(.sticker-emoji-custom-panel):not(.rong-emoji-collect-panel)');
                        if (origPanel) origPanel.style.display = '';
                        // 恢复收藏面板（可能被隐藏了）
                        const collectPanel = emojiBox.querySelector('.rong-emoji-collect-panel');
                        if (collectPanel) collectPanel.style.display = '';
                    }
                });
            });

            tabList.appendChild(qqTab);
            tabList.appendChild(wxTab);
            tabList.appendChild(manageTab);

            window.PluginManager.log('sticker-import', '[StickerImport] ✅ 表情面板标签已注入');
        };

        // 使用 MutationObserver 监听表情面板出现
        const observer = new MutationObserver(() => {
            const tabList = document.querySelector('.rong-emoji-list-ul');
            if (tabList && !tabList.querySelector('.sticker-tab-qq')) {
                injected = false;
                doInject();
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
        instance._emojiObserver = observer;

        doInject();
    },

    // ---- 渲染已导入Tab（管理面板中的） ----
    _renderImportedTab(instance) {
        const body = document.getElementById('sticker-import-body');
        if (!body) return;
        body.innerHTML = '';

        // 分别读取 qq 和 wechat 文件夹
        const qqStickers = this._getLocalStickers('qq');
        const wxStickers = this._getLocalStickers('wechat');
        const total = qqStickers.length + wxStickers.length;

        if (total === 0) {
            body.innerHTML = `
                <div class="sticker-imported-empty">
                    <div class="sticker-imported-empty-icon">📭</div>
                    <div class="sticker-imported-empty-text">还没有导入过表情包</div>
                    <div class="sticker-imported-empty-text" style="margin-top: 6px; font-size: 11px;">切换到「扫描导入」标签开始~</div>
                </div>
            `;
            return;
        }

        // QQ 表情区
        if (qqStickers.length > 0) {
            const section = document.createElement('div');
            section.innerHTML = `<div class="sticker-message info">🐧 QQ 表情 (${qqStickers.length} 个)</div>`;
            const grid = document.createElement('div');
            grid.className = 'sticker-grid';
            qqStickers.forEach(item => {
                const gridItem = document.createElement('div');
                gridItem.className = 'sticker-grid-item';
                const img = document.createElement('img');
                img.loading = 'lazy';
                img.src = `file://${item.path}`;
                img.alt = item.name;
                img.onerror = () => { gridItem.style.display = 'none'; };
                gridItem.appendChild(img);
                gridItem.addEventListener('dblclick', (e) => { e.stopPropagation(); this._showPreview(item.path); });
                gridItem.title = item.name;
                grid.appendChild(gridItem);
            });
            section.appendChild(grid);
            body.appendChild(section);
        }

        // 微信表情区
        if (wxStickers.length > 0) {
            const section = document.createElement('div');
            section.style.marginTop = '12px';
            section.innerHTML = `<div class="sticker-message info">💬 微信表情 (${wxStickers.length} 个)</div>`;
            const grid = document.createElement('div');
            grid.className = 'sticker-grid';
            wxStickers.forEach(item => {
                const gridItem = document.createElement('div');
                gridItem.className = 'sticker-grid-item';
                const img = document.createElement('img');
                img.loading = 'lazy';
                img.src = `file://${item.path}`;
                img.alt = item.name;
                img.onerror = () => { gridItem.style.display = 'none'; };
                gridItem.appendChild(img);
                gridItem.addEventListener('dblclick', (e) => { e.stopPropagation(); this._showPreview(item.path); });
                gridItem.title = item.name;
                grid.appendChild(gridItem);
            });
            section.appendChild(grid);
            body.appendChild(section);
        }

    },

    // ---- 清空全部：删除所有已导入的表情 ----
    _clearAllStickers(instance) {
        const stickersDir = path.join(__dirname, 'cache', 'stickers');

        // 确认操作
        const confirmMsg = '确定要清空全部已导入的表情吗？\n\n此操作将删除所有 QQ 和微信的已导入表情，不可恢复。';
        if (!confirm(confirmMsg)) return;

        this._showToast('🗑️ 正在清空已导入表情...', 'info');

        // 清空 cache/stickers 目录下所有文件
        try {
            let deletedCount = 0;
            ['qq', 'wechat'].forEach(sub => {
                const dir = path.join(stickersDir, sub);
                if (fs.existsSync(dir)) {
                    const files = fs.readdirSync(dir);
                    for (const file of files) {
                        if (file.startsWith('.')) continue;
                        try {
                            fs.unlinkSync(path.join(dir, file));
                            deletedCount++;
                        } catch (_) { /* ignore */ }
                    }
                }
            });
            this._showToast(`✅ 已清空 ${deletedCount} 个表情`, 'success');
        } catch (e) {
            window.PluginManager.log('sticker-import', '[StickerImport] 清空表情失败:' + ' ' + e, 'error');
            this._showToast('清空表情失败: ' + e.message, 'error');
            return;
        }

        // 重新渲染已导入 Tab 和底部工具栏
        this._renderImportedTab(instance);
        this._updateBottomToolbar(instance);
    },

    // ---- 执行全量扫描 ----
    _performScan(instance) {
        // 清空之前的结果
        instance.scanResults = {};
        instance.selectedStickers = {};
        this._renderScanTab(instance);
        this._updateBatchToolbar(instance);
        this._showToast('已刷新扫描', 'info');
    },

    // ---- Toast 提示 ----
    _showToast(message, type = 'info') {
        const existing = document.getElementById('sticker-toast');
        if (existing) existing.remove();

        const toast = document.createElement('div');
        toast.id = 'sticker-toast';
        toast.className = `sticker-message ${type}`;
        toast.style.cssText = `
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 99999;
            padding: 10px 20px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            opacity: 0;
            transition: opacity 0.3s ease;
            font-size: 13px;
            max-width: 400px;
        `;
        toast.textContent = message;
        document.body.appendChild(toast);

        requestAnimationFrame(() => { toast.style.opacity = '1'; });

        setTimeout(() => {
            toast.style.opacity = '0';
            setTimeout(() => toast.remove(), 300);
        }, 2500);
    },

    // ---- 显示/隐藏面板 ----
    _showPanel(panel) {
        panel.classList.add('visible');
    },

    _hidePanel(panel) {
        panel.classList.remove('visible');
    },

    // ---- 注册导航栏图标 ----
    _registerNavIcon(instance) {
        const NAV_ICON_ID = this.NAV_ICON_ID;
        const CHECK_INTERVAL = 1000;
        const MAX_ATTEMPTS = 60;
        let attempts = 0;
        const self = this;

        const checkAndAddIcon = () => {
            if (document.getElementById(NAV_ICON_ID)) return;

            const navTab = document.querySelector('body > div.rong-im > div > div.rong-nav > ul.rong-nav-tab');

            if (!navTab) {
                attempts++;
                if (attempts < MAX_ATTEMPTS) {
                    setTimeout(checkAndAddIcon, CHECK_INTERVAL);
                }
                return;
            }

            const navItem = document.createElement('li');
            navItem.id = NAV_ICON_ID;
            navItem.className = 'rong-nav-tab-item';
            navItem.style.position = 'relative';
            navItem.innerHTML = `
                <em class="item-content" style="cursor: pointer;">
                    <span class="rong-nav-icon" style="
                        background-image: url('data:image/svg+xml;base64,${self._getNavIconBase64('#808CA4')}');
                    "></span>
                    <span class="rong-nav-icon hover" style="
                        background-image: url('data:image/svg+xml;base64,${self._getNavIconBase64('#3A6EE7')}');
                    "></span>
                    <span class="rong-nav-text">表情导入</span>
                </em>
            `;

            // _registerNavIcon is no longer used - entry point is now only the emoji panel ⚙️ button
            window.PluginManager.log('sticker-import', '[StickerImport] _registerNavIcon is deprecated, skipped.');
        };

        // Do not call checkAndAddIcon - nav icon is no longer registered
    },

    // ========== 生命周期 ==========

    async init(config, pluginManager) {
        window.PluginManager.log('sticker-import', '[StickerImport] 🚀 初始化表情包导入插件...');

        const instance = {
            panel: null,
            _onDocumentClick: null,
            _emojiObserver: null,
            currentTab: 'scan',
            scanResults: {},       // { sourceId: [images] }
            selectedStickers: {},  // { sourceId: Set<index> }
        };

        // 确保 cache/stickers 目录存在
        const stickersDir = path.join(__dirname, 'cache', 'stickers');
        ['qq', 'wechat'].forEach(sub => {
            const dir = path.join(stickersDir, sub);
            if (!fs.existsSync(dir)) {
                fs.mkdirSync(dir, { recursive: true });
            }
        });

        // 修复存量文件中扩展名与实际格式不匹配的问题（如 GIF 文件被命名为 .jpg）
        this._fixExistingStickerExtensions();

        try {
            // 注入样式
            this._injectStyles();

            // 创建管理面板（不再注册到左侧导航栏，入口只在表情面板的⚙️按钮）
            const panel = this._createPanel(instance);
            instance.panel = panel;

            // 点击面板外部关闭
            const onDocumentClick = (e) => {
                if (panel.classList.contains('visible') && !panel.contains(e.target)) {
                    // 不要在点击表情面板管理按钮时关闭（那边自己处理切换）
                    const manageTab = document.querySelector('.sticker-tab-manage');
                    if (manageTab && manageTab.contains(e.target)) return;
                    this._hidePanel(panel);
                }
            };
            document.addEventListener('click', onDocumentClick);
            instance._onDocumentClick = onDocumentClick;

            // 注入表情面板标签（QQ表情、微信表情、表情管理）
            this._injectEmojiPanelTabs(instance);

            window.PluginManager.log('sticker-import', '[StickerImport] ✅ 插件已启动');
            return instance;
        } catch (error) {
            window.PluginManager.log('sticker-import', '[StickerImport] ❌ 初始化失败:' + ' ' + error, 'error');
            return { success: false, message: error.message };
        }
    },

    async destroy(instance) {
        window.PluginManager.log('sticker-import', '[StickerImport] 🔄 销毁表情包导入插件...');

        // 清理 MutationObserver
        if (instance._emojiObserver) {
            instance._emojiObserver.disconnect();
            instance._emojiObserver = null;
        }

        if (instance._onDocumentClick) {
            document.removeEventListener('click', instance._onDocumentClick);
            instance._onDocumentClick = null;
        }
        if (instance.panel) {
            instance.panel.remove();
            instance.panel = null;
        }

        // 清理注入到表情面板的标签
        const tabList = document.querySelector('.rong-emoji-list-ul');
        if (tabList) {
            ['.sticker-tab-qq', '.sticker-tab-wechat', '.sticker-tab-manage'].forEach(sel => {
                const tab = tabList.querySelector(sel);
                if (tab) tab.remove();
            });
        }
        // 清理自定义面板
        const customPanel = document.querySelector('.sticker-emoji-custom-panel');
        if (customPanel) customPanel.remove();

        // 清理预览层
        const preview = document.getElementById('sticker-preview-overlay');
        if (preview) preview.remove();

        // 清理 Toast
        const toast = document.getElementById('sticker-toast');
        if (toast) toast.remove();

        // 清理样式
        const style = document.getElementById(this.STYLE_ID);
        if (style) style.remove();

        window.PluginManager.log('sticker-import', '[StickerImport] ✅ 插件已销毁');
    },
};
