import axios from 'axios';
import fs from '../../../utils/fsExtraCompat.js';
import { promises as fsPromises, existsSync, readdirSync } from 'fs';
import { createWriteStream } from 'fs';
import * as path from 'path';
import { zipToolsManager } from '../../../utils/zipToolsManager.js';
import { spawn } from 'child_process';
import { ApiService as MinecraftAPI, FileManager } from './minecraft-server-api.js';
export class MrpackServerAPI {
    constructor(tempDir) {
        this.cancelled = false;
        this.tempDir = tempDir || path.join(process.cwd(), 'temp-mrpack');
    }
    async searchModpacks(options = {}) {
        try {
            const params = new URLSearchParams();
            let facets = [];
            facets.push('["project_type:modpack"]');
            facets.push('["server_side:required","server_side:optional"]');
            if (options.categories && options.categories.length > 0) {
                const categoryFacets = options.categories.map(cat => `"categories:${cat}"`);
                facets.push(`[${categoryFacets.join(',')}]`);
            }
            if (options.versions && options.versions.length > 0) {
                const versionFacets = options.versions.map(ver => `"versions:${ver}"`);
                facets.push(`[${versionFacets.join(',')}]`);
            }
            if (options.license) {
                facets.push(`["license:${options.license}"]`);
            }
            if (facets.length > 0) {
                params.append('facets', `[${facets.join(',')}]`);
            }
            if (options.query) {
                params.append('query', options.query);
            }
            params.append('limit', (options.limit || 20).toString());
            params.append('offset', (options.offset || 0).toString());
            params.append('index', options.index || 'relevance');
            const response = await axios.get(`${MrpackServerAPI.MODRINTH_API_BASE}${MrpackServerAPI.SEARCH_ENDPOINT}?${params.toString()}`);
            return response.data;
        }
        catch (error) {
            if (axios.isAxiosError(error)) {
                throw new Error(`搜索整合包失败: ${error.message}`);
            }
            throw error;
        }
    }
    async getProjectVersions(projectId) {
        try {
            const response = await axios.get(`${MrpackServerAPI.MODRINTH_API_BASE}/project/${projectId}/version`, {
                headers: {
                    'User-Agent': 'GSM3/1.0.0 (game server manager)'
                },
                timeout: 10000
            });
            return response.data;
        }
        catch (error) {
            if (axios.isAxiosError(error)) {
                throw new Error(`获取项目版本失败: ${error.response?.status} ${error.response?.statusText}`);
            }
            throw new Error(`获取项目版本失败: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async downloadAndParseMrpack(mrpackUrl) {
        try {
            await fs.ensureDir(this.tempDir);
            const mrpackPath = path.join(this.tempDir, 'modpack.mrpack');
            const response = await axios({
                method: 'GET',
                url: mrpackUrl,
                responseType: 'stream'
            });
            const writer = createWriteStream(mrpackPath);
            response.data.pipe(writer);
            await new Promise((resolve, reject) => {
                writer.on('finish', () => resolve());
                writer.on('error', reject);
            });
            const indexData = await this.extractModrinthIndex(mrpackPath);
            return indexData;
        }
        catch (error) {
            throw new Error(`下载和解析mrpack文件失败: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async extractModrinthIndex(mrpackPath) {
        const tempDir = path.join(this.tempDir, `mrpack_index_${Date.now()}`);
        try {
            await fs.ensureDir(tempDir);
            await zipToolsManager.extractZip(mrpackPath, tempDir);
            const indexPath = path.join(tempDir, 'modrinth.index.json');
            if (!await fs.pathExists(indexPath)) {
                throw new Error('在mrpack文件中未找到modrinth.index.json');
            }
            const data = await fs.readFile(indexPath, 'utf-8');
            const indexData = JSON.parse(data);
            return indexData;
        }
        catch (error) {
            if (error.message?.includes('modrinth.index.json')) {
                throw error;
            }
            throw new Error(`解析mrpack文件失败: ${error.message || error}`);
        }
        finally {
            await fs.remove(tempDir).catch(() => { });
        }
    }
    async extractOverrides(mrpackPath, targetDir) {
        const tempDir = path.join(this.tempDir, `mrpack_overrides_${Date.now()}`);
        try {
            await fs.ensureDir(tempDir);
            await zipToolsManager.extractZip(mrpackPath, tempDir);
            const overridesDir = path.join(tempDir, 'overrides');
            if (await fs.pathExists(overridesDir)) {
                await fs.copy(overridesDir, targetDir, { overwrite: true });
            }
        }
        finally {
            await fs.remove(tempDir).catch(() => { });
        }
    }
    async downloadMods(indexData, modsDir, onProgress) {
        await fs.ensureDir(modsDir);
        let downloadedCount = 0;
        const totalMods = indexData.files.length;
        if (onProgress) {
            onProgress(`开始下载 ${totalMods} 个mod文件...`, 'info');
        }
        for (const file of indexData.files) {
            if (this.cancelled) {
                throw new Error('操作已取消');
            }
            if (file.env && file.env.server === 'unsupported') {
                if (onProgress) {
                    onProgress(`跳过客户端专用文件: ${path.basename(file.path)}`, 'warn');
                }
                continue;
            }
            const fileName = path.basename(file.path);
            const filePath = path.join(modsDir, fileName);
            try {
                for (const downloadUrl of file.downloads) {
                    try {
                        const response = await axios({
                            method: 'GET',
                            url: downloadUrl,
                            responseType: 'stream',
                            timeout: 60000
                        });
                        const writer = createWriteStream(filePath);
                        response.data.pipe(writer);
                        await new Promise((resolve, reject) => {
                            writer.on('finish', () => resolve());
                            writer.on('error', reject);
                        });
                        downloadedCount++;
                        if (onProgress) {
                            onProgress(`已下载: ${fileName} (${downloadedCount}/${totalMods})`, 'info');
                        }
                        break;
                    }
                    catch (downloadError) {
                        if (onProgress) {
                            onProgress(`下载失败，尝试下一个链接: ${fileName}`, 'warn');
                        }
                        continue;
                    }
                }
            }
            catch (error) {
                if (onProgress) {
                    onProgress(`下载mod失败: ${fileName} - ${error}`, 'error');
                }
            }
        }
        return downloadedCount;
    }
    cancel() {
        this.cancelled = true;
        if (this.currentProcess && !this.currentProcess.killed) {
            this.currentProcess.kill('SIGTERM');
            setTimeout(() => {
                if (this.currentProcess && !this.currentProcess.killed) {
                    this.currentProcess.kill('SIGKILL');
                }
            }, 5000);
        }
        this.cleanup().catch(() => {
        });
    }
    isCancelled() {
        return this.cancelled;
    }
    async deployModpack(options) {
        try {
            const { mrpackUrl, targetDirectory, onProgress } = options;
            this.cancelled = false;
            if (onProgress) {
                onProgress('开始部署整合包...', 'info');
            }
            if (this.cancelled) {
                throw new Error('操作已取消');
            }
            if (onProgress) {
                onProgress('正在下载和解析mrpack文件...', 'info');
            }
            const indexData = await this.downloadAndParseMrpack(mrpackUrl);
            if (this.cancelled) {
                throw new Error('操作已取消');
            }
            const minecraftVersion = options.minecraftVersion || indexData.dependencies.minecraft;
            const loaderType = options.loaderType || this.detectLoaderType(indexData);
            if (!minecraftVersion) {
                throw new Error('无法确定Minecraft版本');
            }
            if (onProgress) {
                onProgress(`检测到Minecraft版本: ${minecraftVersion}, 加载器: ${loaderType}`, 'info');
            }
            const tempServerDir = await FileManager.createTempDirectory();
            const serverName = this.getServerName(loaderType, minecraftVersion);
            if (onProgress) {
                onProgress(`正在下载${loaderType}服务端核心...`, 'info');
            }
            const downloadData = await MinecraftAPI.getDownloadUrl(serverName, minecraftVersion);
            const serverJarPath = FileManager.getServerJarPath(serverName, minecraftVersion);
            await MinecraftAPI.downloadFile(downloadData.url, serverJarPath, undefined, onProgress);
            if (this.cancelled) {
                throw new Error('操作已取消');
            }
            if (!options.skipJavaCheck) {
                const hasJava = await FileManager.validateJavaEnvironment();
                if (!hasJava) {
                    throw new Error('未检测到Java环境，请安装Java后重试');
                }
            }
            if (onProgress) {
                onProgress('正在初始化服务端...', 'info');
            }
            if (this.cancelled) {
                throw new Error('操作已取消');
            }
            this.currentProcess = await this.runServerUntilEulaWithCancel(serverJarPath, tempServerDir, onProgress);
            if (this.cancelled) {
                throw new Error('操作已取消');
            }
            const modsDir = path.join(tempServerDir, 'mods');
            const downloadedMods = await this.downloadMods(indexData, modsDir, onProgress);
            if (this.cancelled) {
                throw new Error('操作已取消');
            }
            if (onProgress) {
                onProgress('正在提取配置文件...', 'info');
            }
            const mrpackPath = path.join(this.tempDir, 'modpack.mrpack');
            await this.extractOverrides(mrpackPath, tempServerDir);
            if (this.cancelled) {
                throw new Error('操作已取消');
            }
            if (onProgress) {
                onProgress('正在移动文件到目标目录...', 'info');
            }
            await this.moveFilesToTarget(tempServerDir, targetDirectory, onProgress);
            if (onProgress) {
                onProgress('正在清理临时文件...', 'info');
            }
            if (await fs.pathExists(tempServerDir)) {
                await fs.remove(tempServerDir);
            }
            await fs.remove(this.tempDir);
            if (onProgress) {
                onProgress('临时文件清理完成', 'info');
            }
            if (onProgress) {
                onProgress('整合包部署完成！', 'success');
            }
            return {
                success: true,
                message: '整合包部署成功',
                targetDirectory,
                installedMods: downloadedMods,
                loaderVersion: indexData.dependencies[loaderType] || 'latest',
                serverType: loaderType
            };
        }
        catch (error) {
            try {
                await this.cleanup();
            }
            catch (cleanupError) {
                console.warn('清理临时文件时出错:', cleanupError);
            }
            return {
                success: false,
                message: `部署失败: ${error instanceof Error ? error.message : String(error)}`
            };
        }
    }
    detectLoaderType(indexData) {
        if (indexData.dependencies.neoforge)
            return 'neoforge';
        if (indexData.dependencies.forge)
            return 'forge';
        if (indexData.dependencies.fabric || indexData.dependencies['fabric-loader'])
            return 'fabric';
        if (indexData.dependencies.quilt || indexData.dependencies['quilt-loader'])
            return 'quilt';
        return 'fabric';
    }
    getServerName(loaderType, minecraftVersion) {
        switch (loaderType) {
            case 'forge':
                return 'forge';
            case 'neoforge':
                return 'neoforge';
            case 'fabric':
                return 'fabric';
            case 'quilt':
                return 'quilt';
            default:
                return 'fabric';
        }
    }
    async moveFilesToTarget(sourceDir, targetDir, onProgress) {
        await fs.ensureDir(targetDir);
        const files = await fsPromises.readdir(sourceDir);
        if (onProgress) {
            onProgress(`正在移动 ${files.length} 个文件到目标目录...`, 'info');
        }
        for (const file of files) {
            const sourcePath = path.join(sourceDir, file);
            const targetPath = path.join(targetDir, file);
            try {
                await fs.move(sourcePath, targetPath, { overwrite: true });
                const stat = await fsPromises.stat(targetPath);
                if (stat.isFile()) {
                    if (onProgress) {
                        onProgress(`已移动文件: ${file}`, 'info');
                    }
                }
                else if (stat.isDirectory()) {
                    if (onProgress) {
                        onProgress(`已移动目录: ${file}`, 'info');
                    }
                }
            }
            catch (error) {
                if (onProgress) {
                    onProgress(`移动失败: ${file} - ${error}`, 'error');
                }
                throw error;
            }
        }
    }
    isForgeInstaller(jarPath) {
        const fileName = path.basename(jarPath).toLowerCase();
        return fileName.startsWith('forge-') || fileName.startsWith('neoforge-');
    }
    async runForgeInstallerWithCancel(jarPath, workingDir, onProgress) {
        return new Promise(async (resolve, reject) => {
            if (this.cancelled) {
                reject(new Error('操作已取消'));
                return;
            }
            if (onProgress) {
                onProgress('检测到Forge/NeoForge安装器，正在执行静默安装...', 'info');
            }
            const installerProcess = spawn('java', ['-jar', path.basename(jarPath), '--installServer'], {
                cwd: workingDir,
                stdio: ['pipe', 'pipe', 'pipe']
            });
            this.currentProcess = installerProcess;
            let installerCompleted = false;
            installerProcess.stdout?.on('data', async (data) => {
                if (this.cancelled) {
                    installerProcess.kill('SIGTERM');
                    return;
                }
                const output = data.toString();
                if (onProgress) {
                    onProgress(output, 'info');
                }
                if (output.includes('You can delete this installer file now if you wish')) {
                    installerCompleted = true;
                    if (onProgress) {
                        onProgress('Forge/NeoForge安装器安装完成，准备运行服务端...', 'success');
                    }
                    try {
                        if (onProgress) {
                            onProgress(`开始调用runForgeServerWithCancel，目录: ${workingDir}`, 'info');
                        }
                        await this.runForgeServerWithCancel(workingDir, onProgress);
                        if (onProgress) {
                            onProgress('runForgeServerWithCancel执行完成', 'success');
                        }
                        if (!installerProcess.killed) {
                            installerProcess.kill('SIGTERM');
                        }
                        resolve(installerProcess);
                    }
                    catch (error) {
                        if (onProgress) {
                            onProgress(`runForgeServerWithCancel执行失败: ${error}`, 'error');
                        }
                        if (!installerProcess.killed) {
                            installerProcess.kill('SIGTERM');
                        }
                        reject(error);
                    }
                }
            });
            installerProcess.stderr?.on('data', (data) => {
                if (this.cancelled) {
                    installerProcess.kill('SIGTERM');
                    return;
                }
                const output = data.toString();
                if (onProgress) {
                    onProgress(output, 'warn');
                }
            });
            installerProcess.on('close', async (code) => {
                this.currentProcess = undefined;
                if (installerCompleted) {
                    if (onProgress) {
                        onProgress('Forge/NeoForge安装器进程已退出。', 'info');
                    }
                    return;
                }
                if (this.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }
                if (code === 0) {
                    if (onProgress) {
                        onProgress('Forge/NeoForge安装器执行完成，但未检测到完成标志。', 'warn');
                    }
                    resolve(installerProcess);
                }
                else {
                    reject(new Error(`Forge/NeoForge安装器异常退出，退出码: ${code}`));
                }
            });
            installerProcess.on('error', (error) => {
                this.currentProcess = undefined;
                reject(new Error(`启动Forge/NeoForge安装器失败: ${error.message}`));
            });
            setTimeout(() => {
                if (!installerProcess.killed && !this.cancelled) {
                    if (onProgress) {
                        onProgress('Forge/NeoForge安装器运行超时，正在强制关闭...', 'warn');
                    }
                    installerProcess.kill('SIGKILL');
                    this.currentProcess = undefined;
                    resolve(installerProcess);
                }
            }, 10 * 60 * 1000);
        });
    }
    async runServerUntilEulaWithCancel(jarPath, workingDir, onProgress) {
        if (this.isForgeInstaller(jarPath)) {
            return this.runForgeInstallerWithCancel(jarPath, workingDir, onProgress);
        }
        return new Promise((resolve, reject) => {
            if (this.cancelled) {
                reject(new Error('操作已取消'));
                return;
            }
            if (onProgress) {
                onProgress('正在启动服务端...', 'info');
            }
            const serverProcess = spawn('java', ['-jar', path.basename(jarPath)], {
                cwd: workingDir,
                stdio: ['pipe', 'pipe', 'pipe']
            });
            this.currentProcess = serverProcess;
            let hasEulaMessage = false;
            serverProcess.stdout?.on('data', (data) => {
                if (this.cancelled) {
                    serverProcess.kill('SIGTERM');
                    return;
                }
                const output = data.toString();
                if (onProgress) {
                    onProgress(output, 'info');
                }
                if (output.toLowerCase().includes('eula') ||
                    output.toLowerCase().includes('you need to agree to the eula')) {
                    hasEulaMessage = true;
                    if (onProgress) {
                        onProgress('检测到EULA协议提示，正在关闭服务端...', 'info');
                    }
                    serverProcess.kill('SIGTERM');
                }
            });
            serverProcess.stderr?.on('data', (data) => {
                if (this.cancelled) {
                    serverProcess.kill('SIGTERM');
                    return;
                }
                const output = data.toString();
                if (onProgress) {
                    onProgress(output, 'warn');
                }
                if (output.toLowerCase().includes('eula')) {
                    hasEulaMessage = true;
                    if (onProgress) {
                        onProgress('检测到EULA协议提示，正在关闭服务端...', 'info');
                    }
                    serverProcess.kill('SIGTERM');
                }
            });
            serverProcess.on('close', (code) => {
                this.currentProcess = undefined;
                if (this.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }
                if (hasEulaMessage) {
                    if (onProgress) {
                        onProgress('服务端已关闭，EULA协议检测完成。', 'info');
                    }
                    resolve(serverProcess);
                }
                else if (code === 0) {
                    if (onProgress) {
                        onProgress('服务端正常退出。', 'info');
                    }
                    resolve(serverProcess);
                }
                else {
                    reject(new Error(`服务端异常退出，退出码: ${code}`));
                }
            });
            serverProcess.on('error', (error) => {
                this.currentProcess = undefined;
                reject(new Error(`启动服务端失败: ${error.message}`));
            });
            setTimeout(() => {
                if (!serverProcess.killed && !this.cancelled) {
                    if (onProgress) {
                        onProgress('服务端运行超时，正在强制关闭...', 'warn');
                    }
                    serverProcess.kill('SIGKILL');
                    this.currentProcess = undefined;
                    resolve(serverProcess);
                }
            }, 10 * 60 * 1000);
        });
    }
    async runForgeServerWithCancel(serverDir, onProgress) {
        return new Promise((resolve, reject) => {
            if (this.cancelled) {
                reject(new Error('操作已取消'));
                return;
            }
            const isWindows = process.platform === 'win32';
            const scriptName = isWindows ? 'run.bat' : 'run.sh';
            const scriptPath = path.join(serverDir, scriptName);
            if (onProgress) {
                onProgress(`正在检查启动脚本: ${scriptPath}`, 'info');
            }
            if (!existsSync(scriptPath)) {
                if (onProgress) {
                    onProgress(`启动脚本${scriptName}不存在于目录${serverDir}，跳过服务端运行`, 'warn');
                    try {
                        const files = readdirSync(serverDir);
                        onProgress(`目录${serverDir}中的文件: ${files.join(', ')}`, 'info');
                    }
                    catch (err) {
                        onProgress(`无法读取目录${serverDir}: ${err}`, 'error');
                    }
                }
                resolve();
                return;
            }
            if (onProgress) {
                onProgress(`找到启动脚本${scriptName}，正在运行...`, 'info');
            }
            const serverProcess = isWindows
                ? spawn('cmd', ['/c', scriptName], {
                    cwd: serverDir,
                    stdio: ['pipe', 'pipe', 'pipe']
                })
                : spawn('bash', [scriptName], {
                    cwd: serverDir,
                    stdio: ['pipe', 'pipe', 'pipe']
                });
            this.currentProcess = serverProcess;
            let hasEulaMessage = false;
            serverProcess.stdout?.on('data', (data) => {
                if (this.cancelled) {
                    serverProcess.kill('SIGTERM');
                    return;
                }
                const output = data.toString();
                if (onProgress) {
                    onProgress(output, 'info');
                }
                if (output.toLowerCase().includes('eula') ||
                    output.toLowerCase().includes('you need to agree to the eula')) {
                    hasEulaMessage = true;
                    if (onProgress) {
                        onProgress('检测到EULA协议提示，正在关闭服务端...', 'info');
                    }
                    serverProcess.kill('SIGTERM');
                }
            });
            serverProcess.stderr?.on('data', (data) => {
                if (this.cancelled) {
                    serverProcess.kill('SIGTERM');
                    return;
                }
                const output = data.toString();
                if (onProgress) {
                    onProgress(output, 'error');
                }
                if (output.toLowerCase().includes('eula')) {
                    hasEulaMessage = true;
                    if (onProgress) {
                        onProgress('检测到EULA协议提示，正在关闭服务端...', 'info');
                    }
                    serverProcess.kill('SIGTERM');
                }
            });
            serverProcess.on('close', (code) => {
                this.currentProcess = undefined;
                if (this.cancelled) {
                    reject(new Error('操作已取消'));
                    return;
                }
                if (hasEulaMessage) {
                    if (onProgress) {
                        onProgress('服务端已关闭，EULA协议检测完成。', 'success');
                    }
                    resolve();
                }
                else if (code === 0) {
                    if (onProgress) {
                        onProgress('服务端正常退出。', 'success');
                    }
                    resolve();
                }
                else {
                    if (onProgress) {
                        onProgress(`服务端退出，退出码: ${code}`, 'info');
                    }
                    resolve();
                }
            });
            serverProcess.on('error', (error) => {
                this.currentProcess = undefined;
                if (onProgress) {
                    onProgress(`启动服务端失败: ${error.message}`, 'error');
                }
                resolve();
            });
            setTimeout(() => {
                if (!serverProcess.killed && !this.cancelled) {
                    if (onProgress) {
                        onProgress('服务端运行超时，正在强制关闭...', 'warn');
                    }
                    serverProcess.kill('SIGKILL');
                    this.currentProcess = undefined;
                    resolve();
                }
            }, 10 * 60 * 1000);
        });
    }
    async cleanup() {
        try {
            if (await fs.pathExists(this.tempDir)) {
                await fs.remove(this.tempDir);
            }
        }
        catch (error) {
            console.warn(`清理临时目录失败: ${error}`);
        }
    }
}
MrpackServerAPI.MODRINTH_API_BASE = 'https://api.modrinth.com/v2';
MrpackServerAPI.SEARCH_ENDPOINT = '/search';
export default MrpackServerAPI;
