/**
 * 工具函数集合 - 文件压缩工具
 * @author wed15
 * @version 1.1.0
 */

// 国际化文本配置
const i18n = {
    zh: {
        // 页面标题
        PAGE_TITLE: '投影拓展包生成器--自动，本地',
        
        // 状态消息
        STATUS_READY: '准备就绪',
        STATUS_PROCESSING: '正在处理文件...',
        STATUS_GENERATING: '正在生成压缩包...',
        STATUS_COMPLETE: '压缩包已生成，可以下载了！',
        STATUS_DOWNLOAD_START: '下载已开始！',
        STATUS_FILE_ADDED: '文件 {name} 已添加！',
        STATUS_FILE_EXISTS: '文件 {name} 已存在！',
        STATUS_FILE_ERROR: '处理文件 {name} 时出错: {error}',
        STATUS_FILE_DELETED: '文件已删除',
        STATUS_NO_FILES: '请先添加文件！',
        STATUS_GENERATING_ZIP: '正在生成压缩包...',
        STATUS_COMPLETE_DONE: '处理完成！',
        STATUS_NO_DOWNLOAD: '没有可下载的文件',
        STATUS_DOWNLOAD_STARTED: '下载已开始',
        STATUS_DOWNLOAD_FAILED: '下载失败: {error}',
        STATUS_BROWSER_HINT: '建议在浏览器中打开该网站(右上角三个点哦!)',
        
        // 错误消息
        ERROR_NO_FILES: '没有上传任何文件',
        ERROR_FILE_READ: '读取文件时出错',
        ERROR_ZIP_GENERATE: '生成压缩包时出错',
        ERROR_DOWNLOAD_FAIL: '下载失败',
        ERROR_NO_DOWNLOAD_FILE: '没有可下载的文件，请先生成压缩包',
        ERROR_INVALID_FILE_TYPE: '只支持 .mcstructure、.nbt 和 .litematic 文件',
        ERROR_IMAGE_FETCH: '获取图像时出错',
        ERROR_ZIP_ERROR: '生成压缩包时出错: {error}',
        
        // UI文本
        PROGRESS_TEXT: '处理中...',
        PROGRESS_COMPLETE: '完成！',
        DROP_ZONE_TEXT: '拖放文件到此处，或点击',
        SELECT_FILES_BTN: '选择文件',
        DOWNLOAD_BTN: '下载压缩包',
        
        // 页面标题和副标题
        MAIN_TITLE: '投影包生成工具',
        SUB_TITLE: '无需上传 本地生成 大文件秒传 支持多文件',
        NAV_DOWNLOAD: '下载汉化投影主包',
        
        // 上传区域
        SECTION_UPLOAD: '上传文件',
        FORMAT_HINT: '支持格式：.mcstructure | .nbt | .litematic',
        
        // 配置区域
        SECTION_CONFIG: '配置选项',
        LABEL_PROJECT_NAME: '拓展包名称:',
        DEFAULT_PROJECT_NAME: '我的投影包',
        BTN_PROCESS: '处理并生成投影拓展包',
        
        // 进度区域
        SECTION_PROGRESS: '处理进度',
        
        // 下载区域
        SECTION_DOWNLOAD: '下载投影拓展包',
        DOWNLOAD_READY: '您的文件已准备就绪',
        
        // 删除按钮
        BTN_DELETE: '删除',
        
        // 底部
        FOOTER_SPONSOR: '反馈',
        FOOTER_ICP: '赣ICP备2026005549号-1',
        FOOTER_CDN: 'Esa分站',
        
        // 下载页面
        DL_PAGE_TITLE: '投影主包下载中心',
        DL_MAIN_TITLE: '汉化投影主包下载中心',
        DL_SUB_TITLE: '选择版本下载 持续更新中',
        DL_NAV_BACK: '返回投影包生成工具',
        DL_SECTION_AVAILABLE: '可用版本',
        DL_BTN_DOWNLOAD: '下载',
        DL_STATUS_START: '开始下载: {name}',
        DL_STATUS_FAILED: '下载失败: {error}',
        
        // 海外版专用
        OV_PAGE_TITLE: 'Construct Expansion Pack Generator',
        OV_MAIN_TITLE: 'Construct Pack Generator',
        OV_SUB_TITLE: '100% Offline · No Upload · Instant Convert',
        OV_FOOTER_POWERED: 'Powered by Sea-of-Stars-Studio',
        OV_STATUS_HINT: 'Open in browser for best experience'
    },
    en: {
        // Page title
        PAGE_TITLE: 'Construct Expansion Pack Generator - Offline & Local',
        
        // Status messages
        STATUS_READY: 'Ready',
        STATUS_PROCESSING: 'Processing files...',
        STATUS_GENERATING: 'Generating archive...',
        STATUS_COMPLETE: 'Archive generated, ready to download!',
        STATUS_DOWNLOAD_START: 'Download started!',
        STATUS_FILE_ADDED: 'File {name} added!',
        STATUS_FILE_EXISTS: 'File {name} already exists!',
        STATUS_FILE_ERROR: 'Error processing {name}: {error}',
        STATUS_FILE_DELETED: 'File deleted',
        STATUS_NO_FILES: 'Please add files first!',
        STATUS_GENERATING_ZIP: 'Generating archive...',
        STATUS_COMPLETE_DONE: 'Processing complete!',
        STATUS_NO_DOWNLOAD: 'No file to download',
        STATUS_DOWNLOAD_STARTED: 'Download started',
        STATUS_DOWNLOAD_FAILED: 'Download failed: {error}',
        STATUS_BROWSER_HINT: 'Please open this site in a browser (tap the 3 dots in the top right!)',
        
        // Error messages
        ERROR_NO_FILES: 'No files uploaded',
        ERROR_FILE_READ: 'Error reading file',
        ERROR_ZIP_GENERATE: 'Error generating archive',
        ERROR_DOWNLOAD_FAIL: 'Download failed',
        ERROR_NO_DOWNLOAD_FILE: 'No file to download, please generate archive first',
        ERROR_INVALID_FILE_TYPE: 'Only .mcstructure, .nbt and .litematic files are supported',
        ERROR_IMAGE_FETCH: 'Error fetching image',
        ERROR_ZIP_ERROR: 'Error generating archive: {error}',
        
        // UI text
        PROGRESS_TEXT: 'Processing...',
        PROGRESS_COMPLETE: 'Complete!',
        DROP_ZONE_TEXT: 'Drag files here or click to select',
        SELECT_FILES_BTN: 'Select Files',
        DOWNLOAD_BTN: 'Download Archive',
        
        // Page title and subtitle
        MAIN_TITLE: 'Construct Expansion Pack Generator',
        SUB_TITLE: 'Generate Construct Expansion Pack easily with different files',
        NAV_DOWNLOAD: 'Download Chinese Projection Pack',
        
        // Upload section
        SECTION_UPLOAD: 'Upload Files',
        FORMAT_HINT: 'Supported: .mcstructure | .nbt | .litematic',
        
        // Config section
        SECTION_CONFIG: 'Configuration',
        LABEL_PROJECT_NAME: 'Pack Name:',
        DEFAULT_PROJECT_NAME: 'My Construct Pack',
        BTN_PROCESS: 'Process & Generate Pack',
        
        // Progress section
        SECTION_PROGRESS: 'Processing Progress',
        
        // Download section
        SECTION_DOWNLOAD: 'Download Your Pack',
        DOWNLOAD_READY: 'Import Into MCBE & Load It In The World',
        
        // Delete button
        BTN_DELETE: 'Delete',
        
        // Footer
        FOOTER_SPONSOR: 'Report Bug',

        // Download page
        DL_PAGE_TITLE: 'Download Construct',
        DL_MAIN_TITLE: 'Projection Pack Download Center',
        DL_SUB_TITLE: 'Select a version to download · Updated regularly',
        DL_NAV_BACK: 'Back to Pack Generator',
        DL_SECTION_AVAILABLE: 'Available Versions',
        DL_BTN_DOWNLOAD: 'Download',
        DL_STATUS_START: 'Downloading: {name}',
        DL_STATUS_FAILED: 'Download failed: {error}',
        
        // Overseas version
        OV_PAGE_TITLE: 'Construct Expansion Pack Generator',
        OV_MAIN_TITLE: 'Construct Pack Generator',
        OV_SUB_TITLE: 'Generate Construct Expansion Pack easily with different files',
        OV_FOOTER_POWERED: 'Powered by Sea-of-Stars-Studio',
        OV_STATUS_HINT: 'Open in browser for best experience'
    }
};

// 当前语言设置（可通过 switchLang 切换）
let currentLang = 'en';

/**
 * 带变量替换的翻译函数
 * @param {string} key - i18n 键名
 * @param {Object} vars - 替换变量，如 {name: 'test.txt'}
 * @returns {string} 翻译后的文本
 */
function t(key, vars) {
    let text = i18n[currentLang][key] || key;
    if (vars) {
        for (const [k, v] of Object.entries(vars)) {
            text = text.replace('{' + k + '}', v);
        }
    }
    return text;
}

/**
 * 切换语言并应用到页面
 * @param {string} lang - 目标语言 'zh' 或 'en'
 */
function switchLang(lang) {
    if (!i18n[lang]) return;
    currentLang = lang;
    // 存储到 localStorage
    try { localStorage.setItem('lang', lang); } catch(e) {}
    applyI18n();
}

/**
 * 将 i18n 翻译应用到页面中所有带 data-i18n 属性的元素
 */
function applyI18n() {
    // 页面标题
    document.title = t('PAGE_TITLE');
    
    // 遍历所有带 data-i18n 属性的元素
    document.querySelectorAll('[data-i18n]').forEach(el => {
        const key = el.getAttribute('data-i18n');
        const val = i18n[currentLang][key];
        if (val !== undefined) {
            if (el.tagName === 'INPUT' && el.type !== 'button') {
                // input 元素：仅当当前值等于任一语言的默认值时才替换（避免覆盖用户输入）
                const attr = el.getAttribute('data-i18n-attr') || 'value';
                const currentVal = el[attr];
                const allDefaults = Object.values(i18n).map(lang => lang[key]);
                if (allDefaults.includes(currentVal) || currentVal === '') {
                    el[attr] = val;
                }
            } else {
                el.textContent = val;
            }
        }
    });
    
    // 处理 data-i18n-placeholder 属性
    document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
        const key = el.getAttribute('data-i18n-placeholder');
        const val = i18n[currentLang][key];
        if (val !== undefined) {
            el.placeholder = val;
        }
    });
    
    // 更新语言切换按钮文本
    const langBtn = document.getElementById('langSwitchBtn');
    if (langBtn) {
        langBtn.textContent = currentLang === 'zh' ? 'EN' : 'ZH';
    }
}

/**
 * 初始化语言：优先系统语言，若有手动切换记录则使用手动选择，默认 zh
 */
function initLang() {
    // 1. 先根据系统语言设置默认值
    try {
        const sysLang = (navigator.language || navigator.userLanguage || 'zh').toLowerCase();
        currentLang = sysLang.startsWith('zh') ? 'zh' : 'en';
    } catch(e) {}
    // 2. 若用户曾手动切换过语言，优先使用手动选择
    try {
        const saved = localStorage.getItem('lang');
        if (saved && i18n[saved]) {
            currentLang = saved;
        }
    } catch(e) {}
}

/**
 * UUID生成器 - 符合RFC4122标准
 * @returns {string} 生成的UUID字符串
 */
function generateUUID() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = Math.random() * 16 | 0;
        const v = c === 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

/**
 * 计算文件MD5哈希值用于去重
 * @param {File} file - 要计算哈希的文件对象
 * @returns {Promise<string>} 文件的MD5哈希值
 */
function computeFileHash(file) {
    return new Promise((resolve, reject) => {
        const spark = new SparkMD5.ArrayBuffer();
        const reader = new FileReader();
        
        reader.onload = function(e) {
            spark.append(e.target.result);
            resolve(spark.end());
        };
        
        reader.onerror = function() {
            reject(new Error(i18n[currentLang].ERROR_FILE_READ));
        };
        
        reader.readAsArrayBuffer(file);
    });
}

/**
 * 验证文件类型是否为.mcstructure、.nbt 或.litematic
 * @param {File} file - 要验证的文件对象
 * @returns {boolean} 是否为有效文件类型
 */
function isValidFileType(file) {
    const fileName = file.name.toLowerCase();
    return fileName.endsWith('.mcstructure') || 
           fileName.endsWith('.nbt') || 
           fileName.endsWith('.litematic');
}

/**
 * 格式化文件大小显示
 * @param {number} bytes - 文件大小（字节）
 * @returns {string} 格式化后的文件大小字符串
 */
function formatFileSize(bytes) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;                                    // 单位换算基数
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];        // 单位数组
    const i = Math.floor(Math.log(bytes) / Math.log(k)); // 计算单位级别
    // 返回格式化后的大小字符串
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

/**
 * 文件处理器类
 */
class FileProcessor {
    constructor() {
        this.uploadedFiles = [];
        this.fileHashes = new Set();
        this.generatedZipBlob = null;
        this.convertedFiles = new Map(); // 存储转换后的文件数据
    }
    
    /**
     * 添加文件到处理队列
     * @param {File} file - 要添加的文件
     * @returns {Promise<boolean>} 是否成功添加
     */
    async addFile(file) {
        // 验证文件类型
        if (!isValidFileType(file)) {
            throw new Error(i18n[currentLang].ERROR_INVALID_FILE_TYPE);
        }

        // 计算文件哈希值用于去重
        const hash = await computeFileHash(file);
        if (this.fileHashes.has(hash)) {
            return false; // 文件已存在，跳过
        }

        this.fileHashes.add(hash);
        this.uploadedFiles.push({
            file: file,
            name: file.name,
            size: file.size,
            hash: hash
        });

        // 延迟转换：在生成 ZIP 时再处理 nbt / litematic 文件，避免添加阶段阻塞
        return true;
    }
    
    /**
     * 转换 nbt 或 litematic 文件为 mcstructure
     * @param {File} file - 要转换的文件
     * @param {string} hash - 文件哈希值
     */
    async convertFile(file, hash) {
        try {
            const fileName = file.name.toLowerCase();
            let mcstructureData = null;
            let convertedFileName = '';
            
            if (fileName.endsWith('.litematic')) {
                // Litematic 先转 NBT，再转 Mcstructure（需要两步转换）
                if (typeof window.LitematicConverter === 'undefined') {
                    console.warn('LitematicConverter 未加载，跳过转换');
                    return;
                }

                const nbtResult = await window.LitematicConverter.convert(file);
                if (!nbtResult.success) {
                    console.error(`Litematic 转 NBT 失败：${nbtResult.error}`);
                    return;
                }
                
                if (typeof window.NBTToMcStructure === 'undefined') {
                    console.warn('NBTToMcStructure 未加载，跳过转换');
                    return;
                }
                
                await window.NBTToMcStructure.init('./data/');
                const buffer = nbtResult.data;
                const result = await window.NBTToMcStructure.convert(
                    buffer,
                    file.name.replace(/\.litematic$/i, '.nbt'),
                    { version: '1.21.0.03' }
                );
                
                if (result.success) {
                    mcstructureData = result.data;
                    convertedFileName = file.name.replace(/\.litematic$/i, '.mcstructure');
                } else {
                    console.error(`NBT 转 Mcstructure 失败：${result.error}`);
                    return;
                }
            } else if (fileName.endsWith('.nbt')) {
                // NBT 直接转 Mcstructure
                if (typeof window.NBTToMcStructure === 'undefined') {
                    console.warn('NBTToMcStructure 未加载，跳过转换');
                    return;
                }
                
                await window.NBTToMcStructure.init('./data/');
                const buffer = await file.arrayBuffer();
                const result = await window.NBTToMcStructure.convert(
                    buffer,
                    file.name,
                    { version: '1.21.0.03' }
                );
                
                if (result.success) {
                    mcstructureData = result.data;
                    convertedFileName = file.name.replace(/\.nbt$/i, '.mcstructure');
                } else {
                    console.error(`NBT 转 Mcstructure 失败：${result.error}`);
                    return;
                }
            }
            
            if (mcstructureData) {
                this.convertedFiles.set(hash, {
                    data: mcstructureData,
                    name: convertedFileName,
                    originalName: file.name
                });
                console.log(`文件转换成功：${file.name} → ${convertedFileName}`);
            }
        } catch (error) {
            console.error(`转换文件 ${file.name} 失败:`, error);
        }
    }
    
    /**
     * 清空所有文件
     */
    clearFiles() {
        this.uploadedFiles = [];
        this.fileHashes.clear();
        this.generatedZipBlob = null;
        this.convertedFiles.clear();
    }
    
    /**
     * 获取文件数量
     * @returns {number} 文件总数
     */
    getFileCount() {
        return this.uploadedFiles.length;
    }
    
    /**
     * 根据哈希值删除文件
     * @param {string} hash - 文件哈希值
     * @returns {boolean} 是否成功删除
     */
    removeFileByHash(hash) {
        const initialLength = this.uploadedFiles.length;
        this.uploadedFiles = this.uploadedFiles.filter(f => f.hash !== hash);
        this.fileHashes.delete(hash);
        this.convertedFiles.delete(hash); // 同时删除转换后的文件
        return this.uploadedFiles.length < initialLength;
    }
}

/**
 * ZIP生成器类
 */
class ZipGenerator {
    constructor(fileProcessor, progressManager = null) {
        this.fileProcessor = fileProcessor;
        this.progressManager = progressManager;
        this.zip = new JSZip();
    }
    
    /**
     * 生成完整的ZIP包
     * @param {string} projectName - 项目名称
     * @returns {Promise<Blob>} 生成的ZIP文件Blob
     */
    async generate(projectName) {
        try {
            // 更新进度：开始生成
            this.updateProgress(10);
            
            // 生成manifest.json配置文件
            this.generateManifest(projectName);
            this.updateProgress(30);
            
            // 生成脚本文件
            await this.generateScripts();
            this.updateProgress(50);
            
            // 添加图标文件
            await this.addIcon();
            this.updateProgress(70);

            // 添加用户上传的结构文件
            await this.addStructureFiles();
            this.updateProgress(90);
            
            // 生成最终的ZIP文件
            const blob = await this.createZipBlob();
            this.updateProgress(100);
            
            return blob;
        } catch (error) {
            console.error('生成ZIP文件失败:', error);
            throw error;
        }
    }
    
    /**
     * 更新进度
     * @param {number} percentage - 进度百分比
     */
    updateProgress(percentage) {
        if (this.progressManager) {
            this.progressManager.update(percentage);
        }
    }
    
    /**
     * 生成manifest.json配置文件
     * @param {string} projectName - 项目名称
     */
    generateManifest(projectName) {
        const manifest = {
            format_version: 2,
            header: {
                name: `Construct-Expansion-${projectName}`,
                description: `ID：${generateUUID()} Made by Construct Pack Generator`,
                uuid: generateUUID(),
                version: [1, 0, 0],
                min_engine_version: [1, 21, 90]
            },
            modules: [
                {
                    type: "data",
                    uuid: generateUUID(),
                    version: [1, 0, 0]
                },
                {
                    type: "script",
                    language: "javascript",
                    uuid: generateUUID(),
                    entry: "scripts/main.js",
                    version: [1, 0, 0]
                }
            ],
            dependencies: [
                {
                    module_name: "@minecraft/server",
                    version: "2.1.0"
                }
            ]
        };
        
        this.zip.file("manifest.json", JSON.stringify(manifest, null, 2));
    }
    
    /**
     * 生成脚本文件
     */
    async generateScripts() {
        const scriptsFolder = this.zip.folder("scripts");
        let fileNames = this.fileProcessor.uploadedFiles

            .map(file => file.name.replace(/\.mcstructure$/i, ''))
            .map(file => JSON.stringify(file))

            .join("或\n");


        
            
        const mainJsContent = `//@ts-ignore
import { world, system, EasingType } from "@minecraft/server";

const HELP_MESSAGE = \`Open the Construct menu,enter:\\n${fileNames}\\n as the Structure ID\`;
world.afterEvents.playerSpawn.subscribe((event) => {
    if (event.initialSpawn) {
        system.runTimeout(() => {
            event.player.sendMessage(HELP_MESSAGE);
        }, 20);
    }
});
//@ts-ignore
world.beforeEvents.chatSend.subscribe((event) => {
    var freeCameraRunId = Number(event.sender.getDynamicProperty("help")) || 0;
    if (event.message === "提示") {
        event.cancel = true;
        event.sender.sendMessage(HELP_MESSAGE);
        return;
    }
});`;
        
        scriptsFolder.file("main.js", mainJsContent);
    }
    
    /**
     * 添加图标文件
     * @returns {Promise<void>}
     */
    async addIcon() {
        try {
            const imageUrl = `./pack_icon.png`;
            const response = await fetch(imageUrl);
            if (response.ok) {
                const blob = await response.blob();
                this.zip.file("pack_icon.png", blob);
            }
        } catch (error) {
            console.warn(i18n[currentLang].ERROR_IMAGE_FETCH, error);
        }
    }
    
    /**
     * 添加结构文件到 ZIP（包括转换后的文件）
     * @returns {Promise<void>}
     */
    async addStructureFiles() {
        const structuresFolder = this.zip.folder("structures");
        const files = this.fileProcessor.uploadedFiles;
            
        for (const item of files) {
            let arrayBuffer;
            let fileName = item.name;
            const fileNameLower = item.name.toLowerCase();
            const converted = this.fileProcessor.convertedFiles.get(item.hash);

            if (converted) {
                // 使用已转换的 mcstructure 文件
                arrayBuffer = converted.data;
                fileName = converted.name;
            } else if (fileNameLower.endsWith('.nbt') || fileNameLower.endsWith('.litematic')) {
                // 延迟转换
                await this.fileProcessor.convertFile(item.file, item.hash);
                const convertedAfter = this.fileProcessor.convertedFiles.get(item.hash);
                if (convertedAfter) {
                    arrayBuffer = convertedAfter.data;
                    fileName = convertedAfter.name;
                } else {
                    // 转换失败，回退为原始文件
                    arrayBuffer = await item.file.arrayBuffer();
                }
            } else {
                arrayBuffer = await item.file.arrayBuffer();
            }
                
            structuresFolder.file(fileName, arrayBuffer);
        }
    }
    
    /**
     * 创建ZIP文件Blob
     * @returns {Promise<Blob>} ZIP文件Blob
     */
    async createZipBlob() {
        return await this.zip.generateAsync({
            type: "blob",
            compression: "DEFLATE",
            compressionOptions: {
                level: 6
            }
        });
    }
}

/**
 * 进度管理器
 */
class ProgressManager {
    constructor(progressBar, progressText, statusMessage) {
        this.progressBar = progressBar;
        this.progressText = progressText;
        this.statusMessage = statusMessage;
    }
    
    /**
     * 更新进度显示
     * @param {number} percentage - 进度百分比 (0-100)
     */
    update(percentage) {
        this.progressBar.style.width = percentage + '%';
        this.progressText.textContent = Math.round(percentage) + '%';
    }
    
    /**
     * 显示状态消息
     * @param {string} message - 消息内容
     * @param {string} color - 文字颜色
     */
    showStatus(message, color = 'black') {
        this.statusMessage.textContent = message;
        this.statusMessage.style.color = color;
        console.log(message)
    }
    
    /**
     * 重置进度
     */
    reset() {
        this.update(0);
        this.progressText.textContent = i18n[currentLang].PROGRESS_TEXT;
        this.showStatus('');
    }
}
