/**
 * 表情包导出器
 * 负责导出QQ中添加的表情包资源包
 */

import fs from 'fs';
import path from 'path';
import https from 'https';
import { fileURLToPath } from 'url';
import { NapCatCore } from 'NapCatQQ/src/core/index.js';

// ES模块中__dirname的替代方案
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

/**
 * 表情包类型枚举
 */
export enum StickerPackType {
    /** 收藏的单个表情 */
    FAVORITE_EMOJI = 'favorite_emoji',
    /** 市场表情包 */
    MARKET_PACK = 'market_pack',
    /** 系统表情包 */
    SYSTEM_PACK = 'system_pack'
}

/**
 * 表情包信息
 */
export interface StickerPackInfo {
    /** 表情包ID */
    packId: string;
    /** 表情包名称 */
    packName: string;
    /** 表情包类型 */
    packType: StickerPackType;
    /** 表情包描述 */
    description?: string;
    /** 表情数量 */
    stickerCount: number;
    /** 表情列表 */
    stickers: StickerInfo[];
    /** 原始数据（用于调试） */
    rawData?: any;
}

/**
 * 单个表情信息
 */
export interface StickerInfo {
    /** 表情ID */
    stickerId: string;
    /** 表情名称 */
    name: string;
    /** 表情路径 */
    path: string;
    /** 是否已下载 */
    downloaded: boolean;
    /** MD5哈希 */
    md5?: string;
    /** 文件大小 */
    fileSize?: number;
}

/**
 * 导出结果
 */
export interface ExportResult {
    /** 是否成功 */
    success: boolean;
    /** 导出的表情包数量 */
    packCount: number;
    /** 导出的表情总数 */
    stickerCount: number;
    /** 导出路径 */
    exportPath: string;
    /** 导出ID */
    exportId?: string;
    /** 错误信息 */
    error?: string;
}

/**
 * 导出记录
 */
export interface ExportRecord {
    /** 记录ID */
    id: string;
    /** 导出类型 */
    type: 'single' | 'all';
    /** 表情包ID（单个导出时） */
    packId?: string;
    /** 表情包名称 */
    packName?: string;
    /** 导出的表情包数量 */
    packCount: number;
    /** 导出的表情总数 */
    stickerCount: number;
    /** 导出路径 */
    exportPath: string;
    /** 导出时间 */
    exportTime: Date;
    /** 是否成功 */
    success: boolean;
    /** 错误信息 */
    error?: string;
}

/**
 * 表情包导出器
 */
export class StickerPackExporter {
    private readonly core: NapCatCore;
    private readonly exportBasePath: string;
    private readonly recordsPath: string;
    private exportRecords: ExportRecord[] = [];

    constructor(core: NapCatCore) {
        this.core = core;
        const userProfile = process.env['USERPROFILE'] || process.env['HOME'] || '.';
        this.exportBasePath = path.join(userProfile, '.qq-chat-exporter', 'sticker-packs');
        this.recordsPath = path.join(userProfile, '.qq-chat-exporter', 'sticker-export-records.json');
        
        // 确保导出目录存在
        if (!fs.existsSync(this.exportBasePath)) {
            fs.mkdirSync(this.exportBasePath, { recursive: true });
        }
        
        // 加载导出记录
        this.loadRecords();
    }
    
    /**
     * 加载导出记录
     */
    private loadRecords(): void {
        try {
            if (fs.existsSync(this.recordsPath)) {
                const data = fs.readFileSync(this.recordsPath, 'utf-8');
                this.exportRecords = JSON.parse(data);
            }
        } catch (error) {
            console.error('[StickerPackExporter] 加载导出记录失败:', error);
            this.exportRecords = [];
        }
    }
    
    /**
     * 保存导出记录
     */
    private saveRecords(): void {
        try {
            fs.writeFileSync(this.recordsPath, JSON.stringify(this.exportRecords, null, 2), 'utf-8');
        } catch (error) {
            console.error('[StickerPackExporter] 保存导出记录失败:', error);
        }
    }
    
    /**
     * 添加导出记录
     */
    private addRecord(record: ExportRecord): void {
        this.exportRecords.unshift(record);
        // 只保留最近100条记录
        if (this.exportRecords.length > 100) {
            this.exportRecords = this.exportRecords.slice(0, 100);
        }
        this.saveRecords();
    }
    
    /**
     * 获取导出记录
     */
    getExportRecords(limit: number = 50): ExportRecord[] {
        return this.exportRecords.slice(0, limit);
    }

    /**
     * 下载文件
     */
    private downloadFile(url: string, destPath: string): Promise<boolean> {
        return new Promise((resolve) => {
            try {
                const file = fs.createWriteStream(destPath);
                https.get(url, (response) => {
                    if (response.statusCode === 200) {
                        response.pipe(file);
                        file.on('finish', () => {
                            file.close();
                            resolve(true);
                        });
                    } else {
                        file.close();
                        fs.unlinkSync(destPath);
                        resolve(false);
                    }
                }).on('error', (err) => {
                    file.close();
                    if (fs.existsSync(destPath)) {
                        fs.unlinkSync(destPath);
                    }
                    console.error(`[StickerPackExporter] 下载错误: ${err.message}`);
                    resolve(false);
                });
            } catch (error) {
                console.error(`[StickerPackExporter] 下载异常:`, error);
                resolve(false);
            }
        });
    }

    /**
     * issue #313：根据文件头 magic bytes 判定真实图片格式。
     *
     * QQ 收藏表情在本地缓存里常以 `.jfif` / 无扩展名 / 错的 `.jpg` 落盘，但实际
     * 字节是 GIF/PNG/WebP。直接拿源路径的 `path.extname` 作为目标扩展名会把
     * 动图误标成 `.jpg`，下游软件按 jpg 解码就显示为「损坏」。
     *
     * 这里只读前 12 个字节即可覆盖 GIF / PNG / JPEG / WebP / BMP / APNG 等
     * QQ 表情常用格式，识别失败时返回 null，调用侧再回退原扩展名或 `.gif`。
     */
    private detectFileExtensionByMagic(filePath: string): string | null {
        let fd: number | null = null;
        try {
            fd = fs.openSync(filePath, 'r');
            const buf = Buffer.alloc(12);
            const n = fs.readSync(fd, buf, 0, 12, 0);
            if (n < 4) return null;

            // GIF87a / GIF89a：47 49 46 38
            if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x38) {
                return '.gif';
            }
            // PNG：89 50 4E 47
            if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4E && buf[3] === 0x47) {
                return '.png';
            }
            // JPEG：FF D8 FF
            if (buf[0] === 0xFF && buf[1] === 0xD8 && buf[2] === 0xFF) {
                return '.jpg';
            }
            // WebP：RIFF .... WEBP
            if (
                n >= 12 &&
                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';
            }
            // BMP：42 4D
            if (buf[0] === 0x42 && buf[1] === 0x4D) {
                return '.bmp';
            }
            return null;
        } catch {
            return null;
        } finally {
            if (fd !== null) {
                try { fs.closeSync(fd); } catch {}
            }
        }
    }

    /**
     * issue #313：把已经下载/复制好的 sticker 文件按真实格式重命名扩展名。
     * 仅当真实扩展名与当前扩展名不一致时才 rename，且会把同名目标先删除以
     * 避免 Windows 下 EEXIST。
     */
    private async normalizeStickerExtension(currentPath: string): Promise<string> {
        if (!fs.existsSync(currentPath)) return currentPath;
        const detected = this.detectFileExtensionByMagic(currentPath);
        if (!detected) return currentPath;
        const currentExt = path.extname(currentPath).toLowerCase();
        if (currentExt === detected) return currentPath;
        const baseNoExt = currentPath.slice(0, currentPath.length - currentExt.length);
        const targetPath = `${baseNoExt}${detected}`;
        try {
            if (fs.existsSync(targetPath)) {
                fs.unlinkSync(targetPath);
            }
            fs.renameSync(currentPath, targetPath);
            return targetPath;
        } catch (err) {
            console.warn(`[StickerPackExporter] 修正扩展名失败 ${currentPath} → ${targetPath}:`, err);
            return currentPath;
        }
    }

    /**
     * issue #313：先按源路径或 URL 推一个候选扩展名（仅用作临时落盘文件名），
     * 真正的扩展名在文件就绪后由 normalizeStickerExtension 校正。
     */
    private guessInitialStickerExtension(source: string): string {
        try {
            if (source.startsWith('http://') || source.startsWith('https://')) {
                const u = new URL(source);
                const ext = path.extname(u.pathname);
                if (ext) return ext;
            } else {
                const ext = path.extname(source);
                if (ext) return ext;
            }
        } catch {
            // ignore
        }
        return '.gif';
    }

    private getMsgService(): any {
        return this.core.context.session.getMsgService();
    }

    private getMarketTabList(result: any): any[] {
        const roamEmojiTab = result?.marketEmoticonInfo?.roamEmojiTab;
        if (!roamEmojiTab) {
            return [];
        }

        const tabListCandidates = [
            roamEmojiTab.ordinaryTabinfoList,
            roamEmojiTab.ordinaryTabInfoList
        ];

        for (const tabList of tabListCandidates) {
            if (Array.isArray(tabList) && tabList.length > 0) {
                return tabList;
            }
        }

        for (const tabList of tabListCandidates) {
            if (Array.isArray(tabList)) {
                return tabList;
            }
        }

        return [];
    }

    private async fetchFavoriteEmojiList(limit: number): Promise<any> {
        const msgService = this.getMsgService();

        // NapCat 不同版本里，这个能力有时挂在 MsgApi 包装层，有时只能直接从 msgService 调。
        if (typeof msgService?.fetchFavEmojiList === 'function') {
            return await msgService.fetchFavEmojiList('', limit, true, true);
        }

        const msgApi = (this.core.apis as any)?.MsgApi;
        if (typeof msgApi?.fetchFavEmojiList === 'function') {
            return await msgApi.fetchFavEmojiList(limit);
        }

        throw new Error('当前运行时没有可用的 fetchFavEmojiList 方法');
    }

    private async fetchMarketEmoticonList(): Promise<any> {
        const msgService = this.getMsgService();
        if (typeof msgService?.fetchMarketEmoticonList !== 'function') {
            throw new Error('当前运行时没有可用的 fetchMarketEmoticonList 方法');
        }

        const callArgsList: Array<[string | number, number]> = [
            ['', 0],
            [0, 0]
        ];

        let fallbackResult: any = null;
        let lastError: unknown;

        for (const args of callArgsList) {
            try {
                const result = await msgService.fetchMarketEmoticonList(...args);
                if (this.getMarketTabList(result).length > 0) {
                    return result;
                }

                if (result && !fallbackResult) {
                    fallbackResult = result;
                }
            } catch (error) {
                lastError = error;
            }
        }

        if (fallbackResult) {
            return fallbackResult;
        }

        if (lastError instanceof Error) {
            throw lastError;
        }

        throw new Error('获取市场表情包列表失败');
    }

    /**
     * 获取所有表情包列表
     * @param types 要获取的表情包类型，默认获取所有类型
     */
    async getStickerPacks(types?: StickerPackType[]): Promise<StickerPackInfo[]> {
        try {
            const packs: StickerPackInfo[] = [];
            const fetchTypes = types || [
                StickerPackType.FAVORITE_EMOJI,
                StickerPackType.MARKET_PACK,
                StickerPackType.SYSTEM_PACK
            ];

            if (fetchTypes.includes(StickerPackType.FAVORITE_EMOJI)) {
                await this.getFavoriteEmojis(packs);
            }

            if (fetchTypes.includes(StickerPackType.MARKET_PACK)) {
                await this.getMarketEmoticonPacks(packs);
            }

            if (fetchTypes.includes(StickerPackType.SYSTEM_PACK)) {
                await this.getSystemEmoticonPacks(packs);
            }

            return packs;
        } catch (error) {
            console.error('[StickerPackExporter] 获取表情包列表失败:', error);
            throw error;
        }
    }

    /**
     * 获取收藏的单个表情
     */
    private async getFavoriteEmojis(packs: StickerPackInfo[]): Promise<void> {
        try {
            const favEmojis = await this.fetchFavoriteEmojiList(1000);
            
            if (favEmojis.emojiInfoList && favEmojis.emojiInfoList.length > 0) {
                const stickers: StickerInfo[] = [];
                
                for (const emoji of favEmojis.emojiInfoList) {
                    stickers.push({
                        stickerId: emoji.eId || emoji.emoId.toString(),
                        name: emoji.desc || emoji.modifyWord || `表情_${emoji.emoId}`,
                        path: emoji.emoPath || emoji.emoOriginalPath,
                        downloaded: emoji.isExist,
                        md5: emoji.md5,
                        fileSize: 0
                    });
                }
                
                packs.push({
                    packId: 'favorite_emojis',
                    packName: '收藏的表情',
                    packType: StickerPackType.FAVORITE_EMOJI,
                    description: '用户收藏的单个表情',
                    stickerCount: stickers.length,
                    stickers
                });
            }
        } catch (error) {
            console.error('[StickerPackExporter] 获取收藏表情失败:', error);
        }
    }

    private async getMarketEmoticonPacks(packs: StickerPackInfo[]): Promise<void> {
        try {
            const msgService = this.getMsgService();
            const result = await this.fetchMarketEmoticonList();
            const tabList = this.getMarketTabList(result);

            if (tabList.length === 0) {
                return;
            }

            for (const tab of tabList) {
                const epId = tab.epId.toString();
                const packName = tab.tabName || `表情包 #${epId}`;

                try {
                    const jsonResult: any = await msgService.fetchMarketEmotionJsonFile(epId);

                    if (jsonResult && jsonResult.result === 0 && jsonResult.errMsg) {
                        const jsonFilePath = jsonResult.errMsg;

                        if (fs.existsSync(jsonFilePath)) {
                            const jsonContent = fs.readFileSync(jsonFilePath, 'utf-8');
                            const packData = JSON.parse(jsonContent);
                            const stickers = this.parseMarketPackJsonData(packData, epId, packName);
                            
                            if (stickers.length > 0) {
                                // 从JSON数据中获取描述信息
                                const description = packData.mark || packData.description || `包含 ${stickers.length} 个表情`;
                                
                                packs.push({
                                    packId: `market_${epId}`,
                                    packName,
                                    packType: StickerPackType.MARKET_PACK,
                                    description,
                                    stickerCount: stickers.length,
                                    stickers,
                                    rawData: { epId, tabInfo: tab, jsonData: packData }
                                });
                                continue;
                            }
                        }
                    }
                } catch (error: any) {
                    // 静默处理，尝试其他方法
                }

                try {
                    const imagesResult: any = await msgService.fetchMarketEmoticonFaceImages(epId);
                    if (imagesResult && imagesResult.result === 0) {
                        const stickers = this.parseMarketPackInfo(imagesResult, epId, packName);
                        if (stickers.length > 0) {
                            const description = imagesResult.mark || imagesResult.description || `包含 ${stickers.length} 个表情`;
                            
                            packs.push({
                                packId: `market_${epId}`,
                                packName,
                                packType: StickerPackType.MARKET_PACK,
                                description,
                                stickerCount: stickers.length,
                                stickers,
                                rawData: { epId, tabInfo: tab }
                            });
                            continue;
                        }
                    }
                } catch (error: any) {
                    // 静默处理
                }

                try {
                    const aioResult: any = await msgService.fetchMarketEmoticonAioImage(epId);
                    if (aioResult && aioResult.result === 0) {
                        const stickers = this.parseMarketPackInfo(aioResult, epId, packName);
                        if (stickers.length > 0) {
                            const description = aioResult.mark || aioResult.description || `包含 ${stickers.length} 个表情`;
                            
                            packs.push({
                                packId: `market_${epId}`,
                                packName,
                                packType: StickerPackType.MARKET_PACK,
                                description,
                                stickerCount: stickers.length,
                                stickers,
                                rawData: { epId, tabInfo: tab }
                            });
                            continue;
                        }
                    }
                } catch (error: any) {
                    // 静默处理
                }

                // 所有方法都失败，添加占位
                packs.push({
                    packId: `market_${epId}`,
                    packName,
                    packType: StickerPackType.MARKET_PACK,
                    description: `待加载详情`,
                    stickerCount: 0,
                    stickers: [],
                    rawData: { epId, tabInfo: tab }
                });
            }
        } catch (error) {
            console.error('[StickerPackExporter] 获取市场表情包失败:', error);
        }
    }
    
    private parseMarketPackJsonData(packData: any, _epId: string, packName: string): StickerInfo[] {
        const stickers: StickerInfo[] = [];

        try {
            const emojiList = packData.imgs || [];

            for (const emoji of emojiList) {
                const md5 = emoji.id;
                const name = emoji.name || `表情_${md5}`;
                const md5Prefix = md5.substring(0, 2);
                const url = `https://gxh.vip.qq.com/club/item/parcel/item/${md5Prefix}/${md5}/raw300.gif`;

                stickers.push({
                    stickerId: md5,
                    name: name,
                    path: url,
                    downloaded: true,
                    md5: md5,
                    fileSize: 0
                });
            }
        } catch (error) {
            console.error(`[StickerPackExporter] 解析表情包 ${packName} 失败:`, error);
        }

        return stickers;
    }
    
    private parseMarketPackInfo(packInfo: any, _epId: string, packName: string): StickerInfo[] {
        const stickers: StickerInfo[] = [];
        
        try {
            // 尝试从不同的字段提取表情列表
            const emojiList = packInfo.emojiInfoList || packInfo.marketEmoticonInfo?.emojiList || [];
            
            for (const emoji of emojiList) {
                stickers.push({
                    stickerId: emoji.eId || emoji.emoId?.toString() || emoji.id?.toString(),
                    name: emoji.name || emoji.desc || emoji.emojiName || `表情_${emoji.eId}`,
                    path: emoji.path || emoji.emoPath || '',
                    downloaded: emoji.isExist || false,
                    md5: emoji.md5 || '',
                    fileSize: emoji.size || 0
                });
            }
        } catch (error) {
            console.error(`[StickerPackExporter] 解析表情包 ${packName} 失败:`, error);
        }
        
        return stickers;
    }

    /**
     * 获取系统表情包
     */
    private async getSystemEmoticonPacks(packs: StickerPackInfo[]): Promise<void> {
        try {
            
            // 从face_config.json中读取系统表情包配置
            const faceConfigPath = path.join(__dirname, '../../../core/external/face_config.json');
            
            if (fs.existsSync(faceConfigPath)) {
                const faceConfig = JSON.parse(fs.readFileSync(faceConfigPath, 'utf-8'));
                
                if (faceConfig.sysface && Array.isArray(faceConfig.sysface)) {
                    // 按AniStickerPackName分组
                    const packMap = new Map<string, any[]>();
                    
                    for (const face of faceConfig.sysface) {
                        const packName = face.AniStickerPackName || '系统表情';
                        
                        if (!packMap.has(packName)) {
                            packMap.set(packName, []);
                        }
                        packMap.get(packName)!.push(face);
                    }
                    
                    // 转换为表情包列表
                    for (const [packName, faces] of packMap.entries()) {
                        const stickers: StickerInfo[] = faces.map(face => ({
                            stickerId: face.QSid || face.AniStickerId,
                            name: face.QDes || `表情_${face.QSid}`,
                            path: '', // 系统表情需要通过API下载
                            downloaded: false,
                            fileSize: 0
                        }));
                        
                        packs.push({
                            packId: `system_${packName.replace(/\s+/g, '_')}`,
                            packName: `系统表情 - ${packName}`,
                            packType: StickerPackType.SYSTEM_PACK,
                            description: `QQ系统内置表情包`,
                            stickerCount: stickers.length,
                            stickers,
                            rawData: { source: 'face_config' }
                        });
                    }
                }
            }
        } catch (error) {
            console.warn('[StickerPackExporter] 获取系统表情包失败:', error);
        }
    }

    /**
     * 导出指定的表情包
     */
    async exportStickerPack(packId: string): Promise<ExportResult> {
        let pack: StickerPackInfo | undefined;
        try {
            
            // 获取表情包列表
            const packs = await this.getStickerPacks();
            pack = packs.find(p => p.packId === packId);
            
            if (!pack) {
                return {
                    success: false,
                    packCount: 0,
                    stickerCount: 0,
                    exportPath: '',
                    error: '表情包不存在'
                };
            }
            
            // 创建导出目录
            const timestamp = Date.now();
            const exportDir = path.join(
                this.exportBasePath,
                `${pack.packName}_${timestamp}`
            );
            fs.mkdirSync(exportDir, { recursive: true });
            
            // 导出表情包信息
            const packInfoPath = path.join(exportDir, 'pack_info.json');
            fs.writeFileSync(packInfoPath, JSON.stringify(pack, null, 2), 'utf-8');
            
            // 并发下载/复制表情文件
            let successCount = 0;
            const stickersDir = path.join(exportDir, 'stickers');
            fs.mkdirSync(stickersDir, { recursive: true });
            
            const concurrencyLimit = 10;
            
            for (let i = 0; i < pack.stickers.length; i += concurrencyLimit) {
                const batch = pack.stickers.slice(i, i + concurrencyLimit);
                const batchTasks = batch.map(async (sticker) => {
                    try {
                        if (!sticker.path) return false;

                        // issue #313：先用源路径/URL 推一个临时扩展名，落盘后再
                        // 按真实 magic bytes 校正，避免动图被误标为 .jpg 而损坏。
                        const initialExt = this.guessInitialStickerExtension(sticker.path);
                        const destPath = path.join(
                            stickersDir,
                            `${sticker.stickerId}_${sticker.name.replace(/[\/\\:*?"<>|]/g, '_')}${initialExt}`
                        );

                        let ok = false;
                        if (sticker.path.startsWith('http://') || sticker.path.startsWith('https://')) {
                            ok = await this.downloadFile(sticker.path, destPath);
                        } else if (fs.existsSync(sticker.path)) {
                            fs.copyFileSync(sticker.path, destPath);
                            ok = true;
                        }
                        if (ok) {
                            await this.normalizeStickerExtension(destPath);
                        }
                        return ok;
                    } catch (error) {
                        return false;
                    }
                });
                
                const results = await Promise.all(batchTasks);
                successCount += results.filter(r => r).length;
            }
            
            // 添加导出记录
            const exportId = `export_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
            this.addRecord({
                id: exportId,
                type: 'single',
                packId: pack.packId,
                packName: pack.packName,
                packCount: 1,
                stickerCount: successCount,
                exportPath: exportDir,
                exportTime: new Date(),
                success: true
            });
            
            return {
                success: true,
                packCount: 1,
                stickerCount: successCount,
                exportPath: exportDir,
                exportId
            };
        } catch (error) {
            console.error(`[StickerPackExporter] 导出表情包失败:`, error);
            
            // 记录失败
            const exportId = `export_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
            this.addRecord({
                id: exportId,
                type: 'single',
                packId: pack?.packId,
                packName: pack?.packName,
                packCount: 0,
                stickerCount: 0,
                exportPath: '',
                exportTime: new Date(),
                success: false,
                error: error instanceof Error ? error.message : String(error)
            });
            
            return {
                success: false,
                packCount: 0,
                stickerCount: 0,
                exportPath: '',
                exportId,
                error: error instanceof Error ? error.message : String(error)
            };
        }
    }

    /**
     * 导出所有表情包
     */
    async exportAllStickerPacks(): Promise<ExportResult> {
        try {
            const packs = await this.getStickerPacks();
            
            if (packs.length === 0) {
                return {
                    success: false,
                    packCount: 0,
                    stickerCount: 0,
                    exportPath: '',
                    error: '没有找到表情包'
                };
            }
            
            // 创建总导出目录
            const timestamp = Date.now();
            const exportDir = path.join(this.exportBasePath, `all_packs_${timestamp}`);
            fs.mkdirSync(exportDir, { recursive: true });
            
            let totalStickerCount = 0;
            let totalSuccessCount = 0;
            
            for (const pack of packs) {
                try {
                    const packDir = path.join(exportDir, pack.packName.replace(/[\/\\:*?"<>|]/g, '_'));
                    fs.mkdirSync(packDir, { recursive: true });
                    
                    const packInfoPath = path.join(packDir, 'pack_info.json');
                    fs.writeFileSync(packInfoPath, JSON.stringify(pack, null, 2), 'utf-8');
                    
                    const stickersDir = path.join(packDir, 'stickers');
                    fs.mkdirSync(stickersDir, { recursive: true });
                    
                    const concurrencyLimit = 10;
                    
                    for (let i = 0; i < pack.stickers.length; i += concurrencyLimit) {
                        const batch = pack.stickers.slice(i, i + concurrencyLimit);
                        const batchTasks = batch.map(async (sticker) => {
                            try {
                                if (!sticker.path) return false;

                                // issue #313：先用源路径/URL 推个临时扩展名，落盘后由
                                // normalizeStickerExtension 按真实 magic bytes 校正。
                                const initialExt = this.guessInitialStickerExtension(sticker.path);
                                const destPath = path.join(
                                    stickersDir,
                                    `${sticker.stickerId}_${sticker.name.replace(/[\/\\:*?"<>|]/g, '_')}${initialExt}`
                                );

                                let ok = false;
                                if (sticker.path.startsWith('http://') || sticker.path.startsWith('https://')) {
                                    ok = await this.downloadFile(sticker.path, destPath);
                                } else if (fs.existsSync(sticker.path)) {
                                    fs.copyFileSync(sticker.path, destPath);
                                    ok = true;
                                }
                                if (ok) {
                                    await this.normalizeStickerExtension(destPath);
                                }
                                return ok;
                            } catch (error) {
                                return false;
                            }
                        });
                        
                        const results = await Promise.all(batchTasks);
                        totalSuccessCount += results.filter(r => r).length;
                    }
                    
                    totalStickerCount += pack.stickers.length;
                } catch (error) {
                    console.error(`[StickerPackExporter] 导出表情包失败: ${pack.packName}`, error);
                }
            }
            
            // 生成汇总信息
            const summaryPath = path.join(exportDir, 'summary.json');
            const summary = {
                exportTime: new Date().toISOString(),
                packCount: packs.length,
                totalStickers: totalStickerCount,
                successfulStickers: totalSuccessCount,
                packs: packs.map(p => ({
                    packId: p.packId,
                    packName: p.packName,
                    stickerCount: p.stickerCount
                }))
            };
            fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2), 'utf-8');

            // 生成收藏表情包列表
            const favoriteEmojis = packs.filter(p => p.packType === StickerPackType.FAVORITE_EMOJI);
            if (favoriteEmojis.length > 0) {
                const favoriteData = {
                    exportTime: new Date().toISOString(),
                    totalStickers: favoriteEmojis.reduce((sum, p) => sum + p.stickerCount, 0),
                    stickers: favoriteEmojis.flatMap(pack =>
                        pack.stickers.map(s => ({
                            id: s.stickerId,
                            name: s.name,
                            url: s.path,
                            md5: s.md5,
                            packId: pack.packId,
                            packName: pack.packName
                        }))
                    )
                };
                fs.writeFileSync(path.join(exportDir, 'favorite_emojis.json'), JSON.stringify(favoriteData, null, 2), 'utf-8');
            }

            // 生成所有表情包列表（不包括收藏）
            const allOtherPacks = packs.filter(p => p.packType !== StickerPackType.FAVORITE_EMOJI);
            if (allOtherPacks.length > 0) {
                const allPacksData = {
                    exportTime: new Date().toISOString(),
                    totalPacks: allOtherPacks.length,
                    totalStickers: allOtherPacks.reduce((sum, p) => sum + p.stickerCount, 0),
                    packs: allOtherPacks.map(pack => ({
                        packId: pack.packId,
                        packName: pack.packName,
                        packType: pack.packType,
                        description: pack.description,
                        stickerCount: pack.stickerCount,
                        stickers: pack.stickers.map(s => ({
                            id: s.stickerId,
                            name: s.name,
                            url: s.path,
                            md5: s.md5
                        }))
                    }))
                };
                fs.writeFileSync(path.join(exportDir, 'all_sticker_packs.json'), JSON.stringify(allPacksData, null, 2), 'utf-8');
            }
            
            // 添加导出记录
            const exportId = `export_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
            this.addRecord({
                id: exportId,
                type: 'all',
                packName: '所有表情包',
                packCount: packs.length,
                stickerCount: totalSuccessCount,
                exportPath: exportDir,
                exportTime: new Date(),
                success: true
            });
            
            return {
                success: true,
                packCount: packs.length,
                stickerCount: totalSuccessCount,
                exportPath: exportDir,
                exportId
            };
        } catch (error) {
            console.error('[StickerPackExporter] 导出所有表情包失败:', error);
            
            // 记录失败
            const exportId = `export_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
            this.addRecord({
                id: exportId,
                type: 'all',
                packName: '所有表情包',
                packCount: 0,
                stickerCount: 0,
                exportPath: '',
                exportTime: new Date(),
                success: false,
                error: error instanceof Error ? error.message : String(error)
            });
            
            return {
                success: false,
                packCount: 0,
                stickerCount: 0,
                exportPath: '',
                exportId,
                error: error instanceof Error ? error.message : String(error)
            };
        }
    }
}

