import axios from 'axios';
import fs from '../../../utils/fsExtraCompat.js';
import { createWriteStream, mkdtemp } from 'fs';
import { promises as fsPromises } from 'fs';
import * as path from 'path';
import { zipToolsManager } from '../../../utils/zipToolsManager.js';
import { spawn } from 'child_process';
import * as os from 'os';
import * as tar from 'tar';
import { MrpackServerAPI } from './mrpack-server-api.js';
import { FileManager } from './minecraft-server-api.js';
import { pipeline } from 'stream';
import { promisify } from 'util';
import { createTarSecurityFilter } from '../../../utils/tarSecurityFilter.js';
const mkdtempAsync = promisify(mkdtemp);
const streamPipeline = (source, destination) => {
    return new Promise((resolve, reject) => {
        pipeline(source, destination, (error) => {
            if (error) {
                reject(error);
            }
            else {
                resolve();
            }
        });
    });
};
class CancellationTokenImpl {
    constructor() {
        this._isCancelled = false;
        this._callbacks = [];
    }
    get isCancelled() {
        return this._isCancelled;
    }
    cancel() {
        if (!this._isCancelled) {
            this._isCancelled = true;
            this._callbacks.forEach(callback => {
                try {
                    callback();
                }
                catch (error) {
                    console.error('取消回调执行失败:', error);
                }
            });
            this._callbacks = [];
        }
    }
    onCancelled(callback) {
        if (this._isCancelled) {
            callback();
        }
        else {
            this._callbacks.push(callback);
        }
    }
    throwIfCancelled() {
        if (this._isCancelled) {
            throw new Error('操作已被取消');
        }
    }
}
class DeploymentManagerImpl {
    constructor() {
        this.activeDeployments = new Map();
        this.deploymentCounter = 0;
    }
    createDeployment(game, targetDirectory, onProgress, deploymentId) {
        const finalDeploymentId = deploymentId || `deploy_${++this.deploymentCounter}_${Date.now()}`;
        const cancellationToken = new CancellationTokenImpl();
        const deployment = {
            id: finalDeploymentId,
            game,
            targetDirectory,
            startTime: new Date(),
            tempDirectories: [],
            processes: [],
            cancellationToken,
            onProgress
        };
        this.activeDeployments.set(finalDeploymentId, deployment);
        return deployment;
    }
    async cancelDeployment(deploymentId) {
        const deployment = this.activeDeployments.get(deploymentId);
        if (!deployment) {
            return false;
        }
        try {
            deployment.cancellationToken.cancel();
            for (const process of deployment.processes) {
                if (!process.killed) {
                    process.kill('SIGTERM');
                    setTimeout(() => {
                        if (!process.killed) {
                            process.kill('SIGKILL');
                        }
                    }, 5000);
                }
            }
            await this.cleanupDeployment(deploymentId);
            if (deployment.onProgress) {
                deployment.onProgress(`部署 ${deploymentId} 已取消`, 'warn');
            }
            return true;
        }
        catch (error) {
            console.error(`取消部署 ${deploymentId} 失败:`, error);
            return false;
        }
    }
    async cancelAllDeployments() {
        const deploymentIds = Array.from(this.activeDeployments.keys());
        let cancelledCount = 0;
        for (const deploymentId of deploymentIds) {
            const success = await this.cancelDeployment(deploymentId);
            if (success) {
                cancelledCount++;
            }
        }
        return cancelledCount;
    }
    getActiveDeployments() {
        return Array.from(this.activeDeployments.values());
    }
    async cleanupDeployment(deploymentId) {
        const deployment = this.activeDeployments.get(deploymentId);
        if (!deployment) {
            return;
        }
        try {
            for (const tempDir of deployment.tempDirectories) {
                try {
                    if (await fs.pathExists(tempDir)) {
                        await fs.remove(tempDir);
                        if (deployment.onProgress) {
                            deployment.onProgress(`已清理临时目录: ${tempDir}`, 'info');
                        }
                    }
                }
                catch (error) {
                    console.error(`清理临时目录失败 ${tempDir}:`, error);
                }
            }
            this.activeDeployments.delete(deploymentId);
        }
        catch (error) {
            console.error(`清理部署 ${deploymentId} 失败:`, error);
        }
    }
}
const globalDeploymentManager = new DeploymentManagerImpl();
export function getActiveDeployments() {
    return globalDeploymentManager.getActiveDeployments();
}
export async function cancelDeployment(deploymentId) {
    return await globalDeploymentManager.cancelDeployment(deploymentId);
}
export async function cancelAllDeployments() {
    return await globalDeploymentManager.cancelAllDeployments();
}
export async function cleanupDeployment(deploymentId) {
    return await globalDeploymentManager.cleanupDeployment(deploymentId);
}
export async function createTempDirectory(deployment, prefix = 'deploy_temp_') {
    const tempDir = await mkdtempAsync(path.join(os.tmpdir(), prefix));
    deployment.tempDirectories.push(tempDir);
    return tempDir;
}
export async function getMinecraftServerCategories() {
    try {
        const { getServerCategories } = await import('./minecraft-server-api.js');
        const categories = await getServerCategories();
        return categories.map(cat => ({
            name: cat.name,
            displayName: cat.displayName,
            servers: cat.servers.map(server => ({
                name: server,
                displayName: server
            }))
        }));
    }
    catch (error) {
        throw new Error(`获取服务器分类失败: ${error instanceof Error ? error.message : String(error)}`);
    }
}
export async function getMinecraftVersions(server) {
    try {
        const { getAvailableVersions } = await import('./minecraft-server-api.js');
        return await getAvailableVersions(server);
    }
    catch (error) {
        throw new Error(`获取版本列表失败: ${error instanceof Error ? error.message : String(error)}`);
    }
}
export async function getMinecraftDownloadInfo(server, version) {
    try {
        const { getDownloadInfo } = await import('./minecraft-server-api.js');
        const downloadData = await getDownloadInfo(server, version);
        return {
            url: downloadData.url,
            filename: `${server}-${version}.jar`,
            size: 0
        };
    }
    catch (error) {
        throw new Error(`获取下载信息失败: ${error instanceof Error ? error.message : String(error)}`);
    }
}
export async function validateJavaEnvironment() {
    return new Promise((resolve) => {
        const javaProcess = spawn('java', ['-version'], { stdio: 'pipe' });
        javaProcess.on('close', (code) => {
            resolve(code === 0);
        });
        javaProcess.on('error', () => {
            resolve(false);
        });
    });
}
export async function downloadFile(url, filePath, onProgress, onLog) {
    try {
        const response = await axios({
            method: 'GET',
            url: url,
            responseType: 'stream'
        });
        const totalLength = parseInt(String(response.headers['content-length'] || '0'), 10);
        let downloadedLength = 0;
        const writer = createWriteStream(filePath);
        response.data.on('data', (chunk) => {
            downloadedLength += chunk.length;
            if (onProgress && totalLength > 0) {
                const percentage = Math.round((downloadedLength / totalLength) * 100);
                onProgress({
                    loaded: downloadedLength,
                    total: totalLength,
                    percentage
                });
            }
        });
        response.data.pipe(writer);
        return new Promise((resolve, reject) => {
            writer.on('finish', () => {
                if (onLog)
                    onLog(`文件下载完成: ${filePath}`, 'success');
                resolve();
            });
            writer.on('error', reject);
        });
    }
    catch (error) {
        throw new Error(`下载文件失败: ${error instanceof Error ? error.message : String(error)}`);
    }
}
export async function downloadFileWithCancellation(url, filePath, deployment, onProgress, onLog) {
    const controller = new AbortController();
    const writer = createWriteStream(filePath);
    deployment.cancellationToken.onCancelled(() => {
        if (onLog)
            onLog(`接收到取消信号，正在中止下载: ${url}`, 'warn');
        controller.abort();
    });
    try {
        deployment.cancellationToken.throwIfCancelled();
        if (onLog)
            onLog(`开始下载: ${url}`, 'info');
        const response = await axios({
            method: 'GET',
            url: url,
            responseType: 'stream',
            signal: controller.signal
        });
        const totalLength = parseInt(String(response.headers['content-length'] || '0'), 10);
        let downloadedLength = 0;
        const stream = response.data.pipe(writer);
        response.data.on('data', (chunk) => {
            if (deployment.cancellationToken.isCancelled) {
                response.data.destroy();
                return;
            }
            downloadedLength += chunk.length;
            if (onProgress && totalLength > 0) {
                const percentage = Math.round((downloadedLength / totalLength) * 100);
                onProgress({
                    loaded: downloadedLength,
                    total: totalLength,
                    percentage
                });
            }
        });
        await new Promise((resolve, reject) => {
            stream.on('finish', resolve);
            stream.on('error', (err) => {
                if (err.name === 'AbortError') {
                    if (onLog)
                        onLog('下载已成功中止', 'info');
                    reject(new Error('操作已被取消'));
                }
                else {
                    reject(err);
                }
            });
        });
    }
    catch (error) {
        writer.end();
        try {
            await fsPromises.unlink(filePath);
        }
        catch (unlinkError) {
        }
        if (error instanceof Error && (error.name === 'AbortError' || error.message === '操作已被取消')) {
            if (onLog)
                onLog(`下载被取消: ${url}`, 'warn');
            throw new Error('操作已被取消');
        }
        throw new Error(`下载文件失败: ${error instanceof Error ? error.message : String(error)}`);
    }
}
export async function deployMinecraftServer(options) {
    const { server, version, targetDirectory, deploymentId, skipJavaCheck = false, skipServerRun = false, onProgress, onLog } = options;
    const deployment = globalDeploymentManager.createDeployment('minecraft', targetDirectory, onLog, deploymentId);
    const onLogCallback = (message, type) => {
        if (onLog)
            onLog(message, type);
    };
    onLogCallback(`开始部署Minecraft服务器: ${server} ${version}`, 'info');
    onLogCallback(`部署ID: ${deployment.id}`, 'info');
    try {
        if (!skipJavaCheck) {
            onLogCallback('正在验证Java环境...', 'info');
            deployment.cancellationToken.throwIfCancelled();
            const javaValid = await validateJavaEnvironment();
            if (!javaValid) {
                throw new Error('Java环境未找到或无效。请确保已正确安装Java并配置了环境变量。');
            }
            onLogCallback('Java环境验证通过', 'success');
        }
        else {
            onLogCallback('已跳过Java环境验证', 'warn');
        }
        onLogCallback('正在获取下载地址...', 'info');
        deployment.cancellationToken.throwIfCancelled();
        const downloadData = await getMinecraftDownloadInfo(server, version);
        onLogCallback('下载地址获取成功。', 'success');
        await fs.ensureDir(targetDirectory);
        const jarPath = path.join(targetDirectory, downloadData.filename);
        onLogCallback(`正在下载服务端核心到: ${jarPath}`, 'info');
        await downloadFileWithCancellation(downloadData.url, jarPath, deployment, onProgress, onLog);
        onLogCallback(`服务器JAR文件已成功下载到: ${jarPath}`, 'success');
        if (!skipServerRun) {
            onLogCallback('正在首次运行服务器以生成配置文件...', 'info');
            deployment.cancellationToken.throwIfCancelled();
            await runMinecraftServerWithCancellation(jarPath, deployment, onLog);
        }
        else {
            onLogCallback('已跳过首次运行服务器', 'warn');
        }
        onLogCallback('Minecraft服务器部署成功！', 'success');
        return {
            success: true,
            message: '部署成功',
            targetDirectory,
            deploymentId: deployment.id
        };
    }
    catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        onLogCallback(`部署失败: ${errorMessage}`, 'error');
        if (errorMessage.includes('操作已被取消')) {
            return { success: false, message: '部署已取消', deploymentId: deployment.id };
        }
        return {
            success: false,
            message: `部署失败: ${errorMessage}`,
            deploymentId: deployment.id
        };
    }
    finally {
        await globalDeploymentManager.cleanupDeployment(deployment.id);
        onLogCallback(`部署 ${deployment.id} 清理完成。`, 'info');
    }
}
async function runMinecraftServerOnce(jarPath, workingDir, onLog) {
    return new Promise((resolve, reject) => {
        if (onLog)
            onLog('正在启动服务端...', 'info');
        const serverProcess = spawn('java', ['-jar', path.basename(jarPath)], {
            cwd: workingDir,
            stdio: ['pipe', 'pipe', 'pipe']
        });
        let hasEulaMessage = false;
        serverProcess.stdout?.on('data', (data) => {
            const output = data.toString();
            if (onLog)
                onLog(output, 'info');
            if (output.toLowerCase().includes('eula') ||
                output.toLowerCase().includes('you need to agree to the eula')) {
                hasEulaMessage = true;
                if (onLog)
                    onLog('检测到EULA协议提示，正在关闭服务端...', 'info');
                serverProcess.kill('SIGTERM');
            }
        });
        serverProcess.stderr?.on('data', (data) => {
            const output = data.toString();
            if (onLog)
                onLog(output, 'error');
            if (output.toLowerCase().includes('eula')) {
                hasEulaMessage = true;
                if (onLog)
                    onLog('检测到EULA协议提示，正在关闭服务端...', 'info');
                serverProcess.kill('SIGTERM');
            }
        });
        serverProcess.on('close', (code) => {
            if (hasEulaMessage) {
                if (onLog)
                    onLog('服务端已关闭，EULA协议检测完成。', 'success');
                resolve();
            }
            else if (code === 0) {
                if (onLog)
                    onLog('服务端正常退出。', 'success');
                resolve();
            }
            else {
                reject(new Error(`服务端异常退出，退出码: ${code}`));
            }
        });
        serverProcess.on('error', (error) => {
            reject(new Error(`启动服务端失败: ${error.message}`));
        });
        setTimeout(() => {
            if (!serverProcess.killed) {
                if (onLog)
                    onLog('服务端运行超时，正在强制关闭...', 'warn');
                serverProcess.kill('SIGKILL');
                resolve();
            }
        }, 5 * 60 * 1000);
    });
}
async function runMinecraftServerOnceWithCancellation(jarPath, workingDir, deployment, onLog) {
    return new Promise((resolve, reject) => {
        deployment.cancellationToken.throwIfCancelled();
        if (onLog)
            onLog('正在启动服务端...', 'info');
        const serverProcess = spawn('java', ['-jar', path.basename(jarPath)], {
            cwd: workingDir,
            stdio: ['pipe', 'pipe', 'pipe']
        });
        deployment.processes.push(serverProcess);
        let hasEulaMessage = false;
        let isResolved = false;
        deployment.cancellationToken.onCancelled(() => {
            if (!serverProcess.killed && !isResolved) {
                if (onLog)
                    onLog('正在取消服务端运行...', 'warn');
                serverProcess.kill('SIGTERM');
                setTimeout(() => {
                    if (!serverProcess.killed) {
                        serverProcess.kill('SIGKILL');
                    }
                }, 5000);
                isResolved = true;
                reject(new Error('操作已被取消'));
            }
        });
        serverProcess.stdout?.on('data', (data) => {
            if (deployment.cancellationToken.isCancelled)
                return;
            const output = data.toString();
            if (onLog)
                onLog(output, 'info');
            if (output.toLowerCase().includes('eula') ||
                output.toLowerCase().includes('you need to agree to the eula')) {
                hasEulaMessage = true;
                if (onLog)
                    onLog('检测到EULA协议提示，正在关闭服务端...', 'info');
                serverProcess.kill('SIGTERM');
            }
        });
        serverProcess.stderr?.on('data', (data) => {
            if (deployment.cancellationToken.isCancelled)
                return;
            const output = data.toString();
            if (onLog)
                onLog(output, 'error');
            if (output.toLowerCase().includes('eula')) {
                hasEulaMessage = true;
                if (onLog)
                    onLog('检测到EULA协议提示，正在关闭服务端...', 'info');
                serverProcess.kill('SIGTERM');
            }
        });
        serverProcess.on('close', (code) => {
            if (isResolved)
                return;
            isResolved = true;
            const processIndex = deployment.processes.indexOf(serverProcess);
            if (processIndex > -1) {
                deployment.processes.splice(processIndex, 1);
            }
            if (deployment.cancellationToken.isCancelled) {
                reject(new Error('操作已被取消'));
                return;
            }
            if (hasEulaMessage) {
                if (onLog)
                    onLog('服务端已关闭，EULA协议检测完成。', 'success');
                resolve();
            }
            else if (code === 0) {
                if (onLog)
                    onLog('服务端正常退出。', 'success');
                resolve();
            }
            else {
                reject(new Error(`服务端异常退出，退出码: ${code}`));
            }
        });
        serverProcess.on('error', (error) => {
            if (isResolved)
                return;
            isResolved = true;
            const processIndex = deployment.processes.indexOf(serverProcess);
            if (processIndex > -1) {
                deployment.processes.splice(processIndex, 1);
            }
            reject(new Error(`启动服务端失败: ${error.message}`));
        });
        setTimeout(() => {
            if (!serverProcess.killed && !isResolved) {
                if (onLog)
                    onLog('服务端运行超时，正在强制关闭...', 'warn');
                serverProcess.kill('SIGKILL');
                isResolved = true;
                resolve();
            }
        }, 5 * 60 * 1000);
    });
}
async function runMinecraftServerWithCancellation(jarPath, deployment, onLog) {
    return new Promise((resolve, reject) => {
        deployment.cancellationToken.throwIfCancelled();
        let isResolved = false;
        deployment.cancellationToken.onCancelled(() => {
            if (!isResolved) {
                if (onLog)
                    onLog('正在取消服务端运行...', 'warn');
                isResolved = true;
                reject(new Error('操作已被取消'));
            }
        });
        FileManager.runServerUntilEula(jarPath, onLog)
            .then(() => {
            if (!isResolved) {
                isResolved = true;
                resolve();
            }
        })
            .catch((error) => {
            if (!isResolved) {
                isResolved = true;
                reject(error);
            }
        });
    });
}
export async function getTModLoaderInfo() {
    try {
        const response = await axios.get('https://api.github.com/repos/tModLoader/tModLoader/releases/latest');
        const assets = response.data.assets;
        const patterns = [
            /tmodloader.*server.*\.zip$/i,
            /tmodloader.*\.zip$/i,
            /.*server.*\.zip$/i,
            /^(?!.*example).*\.zip$/i
        ];
        let downloadUrl = '';
        for (const pattern of patterns) {
            const asset = assets.find((a) => pattern.test(a.name));
            if (asset) {
                downloadUrl = asset.browser_download_url;
                break;
            }
        }
        if (!downloadUrl) {
            throw new Error('未找到tModLoader服务端下载链接');
        }
        return {
            version: response.data.tag_name,
            downloadUrl
        };
    }
    catch (error) {
        throw new Error(`获取tModLoader信息失败: ${error instanceof Error ? error.message : String(error)}`);
    }
}
export async function deployTModLoaderServer(options) {
    const { targetDirectory, options: tmodOptions = {}, deploymentId, onProgress } = options;
    const deployment = globalDeploymentManager.createDeployment('tmodloader', targetDirectory, onProgress, deploymentId);
    try {
        deployment.cancellationToken.throwIfCancelled();
        if (onProgress)
            onProgress('获取tModLoader版本信息...', 'info');
        const { downloadUrl, version } = await getTModLoaderInfo();
        if (onProgress)
            onProgress(`找到版本: ${version}`, 'success');
        deployment.cancellationToken.throwIfCancelled();
        const tempDir = await createTempDirectory(deployment, 'tmodloader-');
        const zipPath = path.join(tempDir, 'tmodloader.zip');
        if (onProgress)
            onProgress('正在下载tModLoader...', 'info');
        await downloadFileWithCancellation(downloadUrl, zipPath, deployment, undefined, onProgress);
        if (onProgress)
            onProgress('下载完成', 'success');
        deployment.cancellationToken.throwIfCancelled();
        await fs.ensureDir(targetDirectory);
        if (onProgress)
            onProgress('正在解压文件...', 'info');
        await extractZipFileWithCancellation(zipPath, targetDirectory, deployment);
        if (onProgress)
            onProgress('解压完成', 'success');
        setTimeout(() => {
            globalDeploymentManager.cleanupDeployment(deployment.id);
        }, 30000);
        return {
            success: true,
            message: 'tModLoader服务器部署成功',
            targetDirectory,
            deploymentId: deployment.id,
            details: {
                version,
                extractPath: targetDirectory
            }
        };
    }
    catch (error) {
        setTimeout(() => {
            globalDeploymentManager.cleanupDeployment(deployment.id);
        }, 30000);
        if (error instanceof Error && error.message === '操作已被取消') {
            return {
                success: false,
                message: 'tModLoader服务器部署已取消',
                deploymentId: deployment.id
            };
        }
        return {
            success: false,
            message: `tModLoader服务器部署失败: ${error instanceof Error ? error.message : String(error)}`,
            deploymentId: deployment.id
        };
    }
}
export async function deployFactorioServer(options) {
    const { targetDirectory, tempDir, deploymentId, onProgress } = options;
    const deployment = globalDeploymentManager.createDeployment('factorio', targetDirectory, onProgress, deploymentId);
    try {
        deployment.cancellationToken.throwIfCancelled();
        const downloadUrl = 'https://factorio.com/get-download/stable/headless/linux64';
        const workingTempDir = tempDir || await createTempDirectory(deployment, 'factorio-');
        const tempFilePath = path.join(workingTempDir, 'factorio-server.tar.xz');
        if (onProgress)
            onProgress('正在下载Factorio服务端...', 'info');
        await downloadFileWithCancellation(downloadUrl, tempFilePath, deployment, undefined, onProgress);
        if (onProgress)
            onProgress('下载完成', 'success');
        deployment.cancellationToken.throwIfCancelled();
        await fs.ensureDir(targetDirectory);
        if (onProgress)
            onProgress('正在解压文件...', 'info');
        await extractTarXzFileWithCancellation(tempFilePath, targetDirectory, deployment);
        if (onProgress)
            onProgress('解压完成', 'success');
        const serverExecutablePath = await findFactorioExecutable(targetDirectory);
        setTimeout(() => {
            globalDeploymentManager.cleanupDeployment(deployment.id);
        }, 30000);
        return {
            success: true,
            message: 'Factorio服务器部署成功',
            targetDirectory,
            deploymentId: deployment.id,
            details: {
                extractPath: targetDirectory,
                serverExecutablePath
            }
        };
    }
    catch (error) {
        setTimeout(() => {
            globalDeploymentManager.cleanupDeployment(deployment.id);
        }, 30000);
        if (error instanceof Error && error.message === '操作已被取消') {
            return {
                success: false,
                message: 'Factorio服务器部署已取消',
                deploymentId: deployment.id
            };
        }
        return {
            success: false,
            message: `Factorio服务器部署失败: ${error instanceof Error ? error.message : String(error)}`,
            deploymentId: deployment.id
        };
    }
}
async function findFactorioExecutable(extractPath) {
    const possiblePaths = [
        path.join(extractPath, 'factorio', 'bin', 'x64', 'factorio'),
        path.join(extractPath, 'bin', 'x64', 'factorio'),
        path.join(extractPath, 'factorio')
    ];
    for (const execPath of possiblePaths) {
        if (await fs.pathExists(execPath)) {
            return execPath;
        }
    }
    return undefined;
}
export async function searchMrpackModpacks(options = {}) {
    const mrpackAPI = new MrpackServerAPI();
    return await mrpackAPI.searchModpacks(options);
}
export async function getMrpackProjectVersions(projectId) {
    const mrpackAPI = new MrpackServerAPI();
    return await mrpackAPI.getProjectVersions(projectId);
}
export async function downloadAndParseMrpack(mrpackUrl) {
    const mrpackAPI = new MrpackServerAPI();
    return await mrpackAPI.downloadAndParseMrpack(mrpackUrl);
}
export async function deployMrpackServer(options) {
    const { projectId, versionId, mrpackUrl, targetDirectory, onProgress } = options;
    const deployment = globalDeploymentManager.createDeployment('mrpack', targetDirectory, onProgress, options.deploymentId);
    try {
        deployment.cancellationToken.throwIfCancelled();
        let finalMrpackUrl = mrpackUrl;
        if (!finalMrpackUrl && (!projectId || !versionId)) {
            throw new Error('必须提供mrpackUrl或者同时提供projectId和versionId');
        }
        if (!finalMrpackUrl && versionId && typeof versionId === 'string') {
            if (versionId.length < 8 || !/^[a-zA-Z0-9]+$/.test(versionId)) {
                throw new Error(`无效的版本ID格式: ${versionId}。版本ID应该是至少8位的字母数字字符串。`);
            }
        }
        if (!finalMrpackUrl && projectId && versionId) {
            if (onProgress) {
                onProgress('正在获取整合包下载链接...', 'info');
            }
            try {
                if (onProgress) {
                    onProgress(`正在请求版本信息: ${versionId}`, 'info');
                }
                const versionResponse = await axios.get(`https://api.modrinth.com/v2/version/${versionId}`, {
                    headers: {
                        'User-Agent': 'GSM3/1.0.0'
                    },
                    timeout: 10000
                });
                const versionData = versionResponse.data;
                if (onProgress) {
                    onProgress(`获取到版本数据，查找mrpack文件...`, 'info');
                }
                const mrpackFile = versionData.files?.find((file) => file.filename?.endsWith('.mrpack') || file.primary === true);
                if (!mrpackFile || !mrpackFile.url) {
                    throw new Error(`未找到有效的mrpack文件下载链接。版本ID: ${versionId}, 可用文件: ${JSON.stringify(versionData.files?.map((f) => f.filename) || [])}`);
                }
                finalMrpackUrl = mrpackFile.url;
                if (onProgress) {
                    onProgress(`找到整合包文件: ${mrpackFile.filename}`, 'info');
                }
            }
            catch (error) {
                if (axios.isAxiosError(error)) {
                    const status = error.response?.status;
                    const statusText = error.response?.statusText;
                    const responseData = error.response?.data;
                    throw new Error(`获取整合包下载链接失败: HTTP ${status} ${statusText}. 版本ID: ${versionId}. 响应: ${JSON.stringify(responseData)}`);
                }
                throw new Error(`获取整合包下载链接失败: ${error instanceof Error ? error.message : String(error)}`);
            }
        }
        if (!finalMrpackUrl) {
            throw new Error('缺少整合包下载链接');
        }
        const mrpackAPI = new MrpackServerAPI();
        deployment.cancellationToken.onCancelled(() => {
            mrpackAPI.cancel();
        });
        const deployResult = await mrpackAPI.deployModpack({
            mrpackUrl: finalMrpackUrl,
            targetDirectory,
            onProgress,
            skipJavaCheck: options.skipJavaCheck,
            minecraftVersion: options.minecraftVersion,
            loaderType: options.loaderType
        });
        await globalDeploymentManager.cleanupDeployment(deployment.id);
        if (deployResult.success) {
            return {
                success: true,
                message: deployResult.message,
                targetDirectory: deployResult.targetDirectory || targetDirectory,
                deploymentId: deployment.id,
                details: {
                    name: 'Mrpack整合包',
                    version: deployResult.loaderVersion || 'unknown',
                    installedMods: deployResult.installedMods || 0,
                    game: 'minecraft'
                }
            };
        }
        else {
            return {
                success: false,
                message: deployResult.message,
                deploymentId: deployment.id
            };
        }
    }
    catch (error) {
        await globalDeploymentManager.cleanupDeployment(deployment.id);
        if (error instanceof Error && error.message === '操作已被取消') {
            return {
                success: false,
                message: 'Mrpack整合包部署已取消',
                deploymentId: deployment.id
            };
        }
        return {
            success: false,
            message: `Mrpack整合包部署失败: ${error instanceof Error ? error.message : String(error)}`,
            deploymentId: deployment.id
        };
    }
}
export async function extractZipFile(zipPath, extractPath) {
    await zipToolsManager.extractZip(zipPath, extractPath);
}
export async function extractZipFileWithCancellation(zipPath, extractPath, deployment) {
    deployment.cancellationToken.throwIfCancelled();
    await zipToolsManager.extractZip(zipPath, extractPath);
}
export async function extractTarXzFile(filePath, extractPath) {
    try {
        await tar.extract({
            file: filePath,
            cwd: extractPath,
            strip: 1,
            filter: createTarSecurityFilter({ cwd: extractPath })
        });
    }
    catch (error) {
        throw new Error(`解压tar.xz文件失败: ${error instanceof Error ? error.message : String(error)}`);
    }
}
async function validateTarXzFile(filePath) {
    try {
        const stats = await fsPromises.stat(filePath);
        const fileSize = stats.size;
        if (fileSize === 0) {
            return { isValid: false, fileSize, error: '文件为空 (0字节)，可能下载失败或被中断' };
        }
        if (fileSize < 100) {
            return { isValid: false, fileSize, error: '文件过小，可能不是有效的tar.xz文件或下载不完整' };
        }
        if (fileSize < 50 * 1024 * 1024) {
            return { isValid: false, fileSize, error: `文件大小异常小 (${Math.round(fileSize / 1024 / 1024)}MB)，Factorio服务端通常大于50MB，可能下载不完整` };
        }
        const buffer = Buffer.alloc(12);
        const fd = await fsPromises.open(filePath, 'r');
        try {
            await fd.read(buffer, 0, 12, 0);
            const xzMagic = Buffer.from([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]);
            if (buffer.subarray(0, 6).equals(xzMagic)) {
                return { isValid: true, fileSize };
            }
            const fileHeader = buffer.toString('hex').toUpperCase();
            let detectedFormat = 'unknown';
            if (buffer.subarray(0, 2).equals(Buffer.from([0x1F, 0x8B]))) {
                detectedFormat = 'gzip';
            }
            else if (buffer.subarray(0, 3).equals(Buffer.from([0x42, 0x5A, 0x68]))) {
                detectedFormat = 'bzip2';
            }
            else if (buffer.subarray(0, 4).equals(Buffer.from([0x50, 0x4B, 0x03, 0x04])) ||
                buffer.subarray(0, 4).equals(Buffer.from([0x50, 0x4B, 0x05, 0x06]))) {
                detectedFormat = 'zip';
            }
            else if (buffer.subarray(0, 8).toString() === 'ustar\x00\x00\x00' ||
                buffer.subarray(257, 262).toString() === 'ustar') {
                detectedFormat = 'tar';
            }
            return {
                isValid: false,
                fileSize,
                error: `文件不是有效的tar.xz格式。检测到的格式: ${detectedFormat}，文件头: ${fileHeader}`
            };
        }
        finally {
            await fd.close();
        }
    }
    catch (error) {
        return {
            isValid: false,
            fileSize: 0,
            error: `文件验证失败: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}
async function checkSystemTarXzSupport() {
    try {
        await new Promise((resolve, reject) => {
            const tarCheck = spawn('tar', ['--version'], { stdio: 'pipe' });
            tarCheck.on('close', (code) => {
                if (code === 0) {
                    resolve();
                }
                else {
                    reject(new Error('tar not available'));
                }
            });
            tarCheck.on('error', reject);
            setTimeout(() => reject(new Error('timeout')), 5000);
        });
        await new Promise((resolve, reject) => {
            const xzCheck = spawn('xz', ['--version'], { stdio: 'pipe' });
            xzCheck.on('close', (code) => {
                if (code === 0) {
                    resolve();
                }
                else {
                    reject(new Error('xz not available'));
                }
            });
            xzCheck.on('error', reject);
            setTimeout(() => reject(new Error('timeout')), 5000);
        });
        return true;
    }
    catch (error) {
        return false;
    }
}
async function extractTarXzWithSystemCommand(filePath, extractPath, deployment) {
    return new Promise((resolve, reject) => {
        const isWindows = process.platform === 'win32';
        let command;
        let args;
        if (isWindows) {
            command = 'tar';
            args = ['-xf', filePath, '-C', extractPath];
        }
        else {
            command = 'sh';
            args = ['-c', `xz -dc "${filePath}" | tar -xf - -C "${extractPath}"`];
        }
        if (deployment.onProgress) {
            deployment.onProgress(`执行解压命令: ${command} ${args.join(' ')}`, 'info');
        }
        const extractProcess = spawn(command, args, {
            stdio: ['pipe', 'pipe', 'pipe']
        });
        deployment.processes.push(extractProcess);
        let stdout = '';
        let stderr = '';
        let isResolved = false;
        deployment.cancellationToken.onCancelled(() => {
            if (!extractProcess.killed && !isResolved) {
                extractProcess.kill('SIGTERM');
                setTimeout(() => {
                    if (!extractProcess.killed) {
                        extractProcess.kill('SIGKILL');
                    }
                }, 5000);
                isResolved = true;
                reject(new Error('操作已被取消'));
            }
        });
        extractProcess.stdout?.on('data', (data) => {
            stdout += data.toString();
        });
        extractProcess.stderr?.on('data', (data) => {
            stderr += data.toString();
        });
        extractProcess.on('close', (code) => {
            if (isResolved)
                return;
            isResolved = true;
            const processIndex = deployment.processes.indexOf(extractProcess);
            if (processIndex > -1) {
                deployment.processes.splice(processIndex, 1);
            }
            if (deployment.cancellationToken.isCancelled) {
                reject(new Error('操作已被取消'));
                return;
            }
            if (code === 0) {
                if (deployment.onProgress) {
                    deployment.onProgress('tar.xz文件解压完成', 'success');
                }
                resolve();
            }
            else {
                const errorMsg = `tar.xz解压失败，退出码: ${code}\n标准输出: ${stdout}\n错误输出: ${stderr}`;
                if (deployment.onProgress) {
                    deployment.onProgress(errorMsg, 'error');
                }
                reject(new Error(errorMsg));
            }
        });
        extractProcess.on('error', (error) => {
            if (isResolved)
                return;
            isResolved = true;
            const processIndex = deployment.processes.indexOf(extractProcess);
            if (processIndex > -1) {
                deployment.processes.splice(processIndex, 1);
            }
            reject(new Error(`执行解压命令失败: ${error.message}`));
        });
    });
}
export async function extractTarXzFileWithCancellation(filePath, extractPath, deployment) {
    try {
        deployment.cancellationToken.throwIfCancelled();
        if (deployment.onProgress) {
            deployment.onProgress('正在验证tar.xz文件格式...', 'info');
        }
        const validation = await validateTarXzFile(filePath);
        if (!validation.isValid) {
            let errorMsg = `Factorio服务器部署失败: 解压tar.xz文件失败: TAR_BAD_ARCHIVE: ${validation.error || 'Unrecognized archive format'}. 文件大小: ${validation.fileSize} bytes, 文件路径: ${filePath}`;
            if (deployment.onProgress) {
                deployment.onProgress(errorMsg, 'error');
                if (validation.fileSize === 0) {
                    deployment.onProgress('建议: 1) 检查网络连接是否稳定 2) 重新尝试下载 3) 检查磁盘空间是否充足 4) 验证下载URL是否有效', 'warn');
                }
                else if (validation.fileSize < 50 * 1024 * 1024) {
                    deployment.onProgress('建议: 1) 重新下载完整文件 2) 检查网络连接稳定性 3) 验证下载过程是否被中断', 'warn');
                }
                else {
                    deployment.onProgress('建议: 1) 检查文件格式是否正确 2) 重新下载文件 3) 验证Factorio下载链接是否有效', 'warn');
                }
            }
            throw new Error(errorMsg);
        }
        if (deployment.onProgress) {
            deployment.onProgress(`文件验证通过，大小: ${validation.fileSize} bytes`, 'success');
            deployment.onProgress('注意: Node.js tar库不原生支持xz压缩，将尝试系统命令解压', 'warn');
        }
        const hasSystemSupport = await checkSystemTarXzSupport();
        if (!hasSystemSupport) {
            const errorMsg = `系统不支持tar.xz解压: 缺少必要的工具 (tar, xz)。请安装相应的软件包：
        - Ubuntu/Debian: sudo apt-get install tar xz-utils
        - CentOS/RHEL: sudo yum install tar xz
        - Windows: 安装 7-Zip 或 Git for Windows`;
            if (deployment.onProgress) {
                deployment.onProgress(errorMsg, 'error');
            }
            throw new Error(errorMsg);
        }
        await extractTarXzWithSystemCommand(filePath, extractPath, deployment);
    }
    catch (error) {
        if (error instanceof Error && error.message === '操作已被取消') {
            throw error;
        }
        if (error instanceof Error && error.message.includes('TAR_BAD_ARCHIVE')) {
            throw error;
        }
        throw new Error(`Factorio服务器部署失败: 解压tar.xz文件失败: TAR_BAD_ARCHIVE: ${error instanceof Error ? error.message : String(error)}`);
    }
}
export async function deployGameServer(options) {
    const { game, targetDirectory, onProgress } = options;
    if (!game || !targetDirectory) {
        throw new Error('缺少必需参数: game和targetDirectory');
    }
    const deployment = globalDeploymentManager.createDeployment(game, targetDirectory, onProgress);
    const cancellationToken = deployment.cancellationToken;
    try {
        cancellationToken.throwIfCancelled();
        let result;
        switch (game) {
            case 'minecraft':
                if (!options.server || !options.version) {
                    throw new Error('Minecraft部署缺少必需参数: server和version');
                }
                result = await deployMinecraftServer({
                    server: options.server,
                    version: options.version,
                    targetDirectory,
                    skipJavaCheck: options.skipJavaCheck,
                    skipServerRun: options.skipServerRun,
                    onProgress: onProgress ? (progress) => onProgress(`进度: ${progress.percentage}%`, 'info') : undefined,
                    onLog: onProgress
                });
                break;
            case 'tmodloader':
                result = await deployTModLoaderServer({
                    targetDirectory,
                    options: options.tmodOptions,
                    onProgress
                });
                break;
            case 'factorio':
                result = await deployFactorioServer({
                    targetDirectory,
                    tempDir: options.tempDir,
                    onProgress
                });
                break;
            case 'mrpack':
                if (!options.mrpackUrl) {
                    throw new Error('Mrpack部署缺少必需参数: mrpackUrl');
                }
                result = await deployMrpackServer({
                    mrpackUrl: options.mrpackUrl,
                    targetDirectory,
                    minecraftVersion: options.minecraftVersion,
                    onProgress
                });
                break;
            case 'bedrock':
                result = await deployBedrockServer({
                    targetDirectory,
                    platform: options.platform,
                    onProgress
                });
                break;
            default:
                throw new Error(`不支持的游戏类型: ${game}`);
        }
        result.deploymentId = deployment.id;
        await globalDeploymentManager.cleanupDeployment(deployment.id);
        return result;
    }
    catch (error) {
        await globalDeploymentManager.cleanupDeployment(deployment.id);
        if (error instanceof Error && error.message === '操作已被取消') {
            throw new Error('统一部署操作已被取消');
        }
        throw error;
    }
}
async function downloadBedrockServerFile(url, filePath, deployment, onProgress) {
    const controller = new AbortController();
    deployment.cancellationToken.onCancelled(() => {
        if (onProgress)
            onProgress('接收到取消信号，正在中止下载...', 'warn');
        controller.abort();
    });
    try {
        deployment.cancellationToken.throwIfCancelled();
        if (onProgress)
            onProgress(`开始下载: ${url}`, 'info');
        const response = await axios({
            method: 'GET',
            url: url,
            responseType: 'stream',
            signal: controller.signal,
            timeout: 600000,
            maxRedirects: 10,
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                'Accept': 'application/octet-stream, */*',
                'Accept-Encoding': 'identity'
            },
            validateStatus: (status) => status >= 200 && status < 400
        });
        const totalLength = parseInt(String(response.headers['content-length'] || '0'), 10);
        let downloadedLength = 0;
        let lastLogTime = Date.now();
        if (onProgress) {
            if (totalLength > 0) {
                onProgress(`文件大小: ${(totalLength / 1024 / 1024).toFixed(2)}MB`, 'info');
            }
            onProgress('开始接收数据...', 'info');
        }
        const writer = createWriteStream(filePath);
        response.data.on('data', (chunk) => {
            if (deployment.cancellationToken.isCancelled) {
                response.data.destroy();
                writer.end();
                return;
            }
            downloadedLength += chunk.length;
            const now = Date.now();
            if (onProgress && totalLength > 0 && now - lastLogTime > 2000) {
                const percentage = ((downloadedLength / totalLength) * 100).toFixed(2);
                const downloadedMB = (downloadedLength / 1024 / 1024).toFixed(2);
                const totalMB = (totalLength / 1024 / 1024).toFixed(2);
                onProgress(`下载进度: ${percentage}% (${downloadedMB}MB / ${totalMB}MB)`, 'info');
                lastLogTime = now;
            }
        });
        response.data.pipe(writer);
        await new Promise((resolve, reject) => {
            writer.on('finish', () => {
                if (onProgress) {
                    onProgress(`下载完成: ${(downloadedLength / 1024 / 1024).toFixed(2)}MB`, 'success');
                }
                resolve();
            });
            writer.on('error', (err) => {
                reject(err);
            });
            response.data.on('error', (err) => {
                if (err.name === 'AbortError' || err.code === 'ERR_CANCELED') {
                    reject(new Error('操作已被取消'));
                }
                else {
                    reject(err);
                }
            });
        });
    }
    catch (error) {
        try {
            await fsPromises.unlink(filePath);
        }
        catch (unlinkError) {
        }
        if (error.name === 'AbortError' || error.code === 'ERR_CANCELED' || error.message === '操作已被取消') {
            if (onProgress)
                onProgress('下载已被取消', 'warn');
            throw new Error('操作已被取消');
        }
        if (onProgress)
            onProgress(`下载失败: ${error.message}`, 'error');
        throw new Error(`下载文件失败: ${error.message}`);
    }
}
export async function getBedrockDownloadLinks() {
    const url = 'https://net-secondary.web.minecraft-services.net/api/v1.0/download/links';
    try {
        const response = await axios.get(url, {
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
                'Accept': '*/*',
                'Accept-Encoding': 'identity',
                'Origin': 'https://www.minecraft.net',
                'Referer': 'https://www.minecraft.net/'
            },
            timeout: 30000
        });
        if (response.data && response.data.result && response.data.result.links) {
            return { links: response.data.result.links };
        }
        throw new Error('无效的API响应格式');
    }
    catch (error) {
        throw new Error(`获取基岩版下载链接失败: ${error.message}`);
    }
}
export async function deployBedrockServer(options) {
    const { targetDirectory, platform, versionType = 'stable', deploymentId, onProgress } = options;
    const finalTargetDirectory = path.join(targetDirectory, 'Minecraft-bedrock-server');
    const deployment = globalDeploymentManager.createDeployment('bedrock', finalTargetDirectory, onProgress, deploymentId);
    try {
        deployment.cancellationToken.throwIfCancelled();
        if (onProgress)
            onProgress('开始部署Minecraft基岩版服务器...', 'info');
        const osPlatform = os.platform();
        const detectedPlatform = osPlatform === 'win32' ? 'windows' : 'linux';
        const currentPlatform = platform && (platform === detectedPlatform) ? platform : detectedPlatform;
        if (onProgress)
            onProgress(`检测到系统平台: ${osPlatform} (${currentPlatform})`, 'info');
        if (onProgress)
            onProgress(`选择版本类型: ${versionType === 'stable' ? '正式版' : '预览版'}`, 'info');
        if (onProgress)
            onProgress('正在获取最新版本下载链接...', 'info');
        const versionInfo = await getBedrockDownloadLinks();
        if (onProgress) {
            onProgress(`可用的下载链接类型: ${versionInfo.links.map(l => l.downloadType).join(', ')}`, 'info');
        }
        let downloadLink;
        if (currentPlatform === 'windows') {
            if (versionType === 'preview') {
                downloadLink = versionInfo.links.find(link => link.downloadType.toLowerCase().includes('preview') &&
                    (link.downloadType.toLowerCase().includes('windows') || link.downloadType.toLowerCase().includes('win')));
            }
            else {
                downloadLink = versionInfo.links.find(link => !link.downloadType.toLowerCase().includes('preview') &&
                    (link.downloadType.toLowerCase().includes('windows') || link.downloadType.toLowerCase().includes('win')));
            }
        }
        else {
            if (versionType === 'preview') {
                downloadLink = versionInfo.links.find(link => link.downloadType.toLowerCase().includes('preview') &&
                    (link.downloadType.toLowerCase().includes('linux') || link.downloadType.toLowerCase().includes('ubuntu')));
            }
            else {
                downloadLink = versionInfo.links.find(link => !link.downloadType.toLowerCase().includes('preview') &&
                    (link.downloadType.toLowerCase().includes('linux') || link.downloadType.toLowerCase().includes('ubuntu')));
            }
        }
        if (!downloadLink) {
            const availableTypes = versionInfo.links.map(l => l.downloadType).join(', ');
            throw new Error(`未找到${currentPlatform}平台的下载链接。可用类型: ${availableTypes}`);
        }
        if (onProgress)
            onProgress(`找到下载链接: ${downloadLink.downloadUrl}`, 'success');
        await fs.ensureDir(finalTargetDirectory);
        if (onProgress)
            onProgress(`创建目标目录: ${finalTargetDirectory}`, 'info');
        const fileName = currentPlatform === 'windows' ? 'bedrock-server.zip' : 'bedrock-server.zip';
        const downloadPath = path.join(finalTargetDirectory, fileName);
        if (onProgress)
            onProgress('正在下载服务端文件...', 'info');
        await downloadBedrockServerFile(downloadLink.downloadUrl, downloadPath, deployment, onProgress);
        if (onProgress)
            onProgress('下载完成，开始解压...', 'success');
        await extractZipFileWithCancellation(downloadPath, finalTargetDirectory, deployment);
        if (onProgress)
            onProgress('解压完成', 'success');
        await fs.remove(downloadPath);
        if (onProgress)
            onProgress('清理临时文件完成', 'info');
        let startCommand = '';
        if (currentPlatform === 'windows') {
            startCommand = '.\\bedrock_server.exe';
        }
        else {
            const serverPath = path.join(finalTargetDirectory, 'bedrock_server');
            try {
                await fsPromises.chmod(serverPath, 0o755);
                if (onProgress)
                    onProgress('已设置服务端文件可执行权限', 'success');
            }
            catch (error) {
                if (onProgress)
                    onProgress(`设置可执行权限失败: ${error.message}`, 'warn');
            }
            startCommand = './bedrock_server';
        }
        if (onProgress)
            onProgress(`推荐启动命令: ${startCommand}`, 'info');
        if (onProgress)
            onProgress('Minecraft基岩版服务端部署完成！', 'success');
        await globalDeploymentManager.cleanupDeployment(deployment.id);
        return {
            success: true,
            message: 'Minecraft基岩版服务端部署成功',
            data: {
                targetDirectory: finalTargetDirectory,
                platform: currentPlatform,
                versionType,
                startCommand,
                deploymentId: deployment.id
            }
        };
    }
    catch (error) {
        await globalDeploymentManager.cleanupDeployment(deployment.id);
        if (error instanceof Error && error.message === '操作已被取消') {
            if (onProgress)
                onProgress('部署已被取消', 'warn');
            throw new Error('基岩版部署操作已被取消');
        }
        if (onProgress)
            onProgress(`部署失败: ${error.message}`, 'error');
        throw error;
    }
}
export default deployGameServer;
