import { EventEmitter } from 'events';
import fs from 'fs/promises';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import os from 'os';
import { exec } from 'child_process';
import { promisify } from 'util';
import { JavaManager } from '../environment/javaManager.js';
const execAsync = promisify(exec);
export class InstanceManager extends EventEmitter {
    constructor(terminalManager, logger, configPath = './data/instances.json') {
        super();
        this.instances = new Map();
        this.saveTimeout = null;
        this.logger = logger;
        this.terminalManager = terminalManager;
        this.configPath = configPath;
        this.javaManager = new JavaManager();
    }
    async getSystemLoad() {
        const os = await import('os');
        const totalMemory = os.totalmem();
        const freeMemory = os.freemem();
        const usedMemory = totalMemory - freeMemory;
        const memoryUsage = (usedMemory / totalMemory) * 100;
        const cpuUsage = await this.getCpuUsage();
        return {
            cpuUsage,
            memoryUsage
        };
    }
    async getCpuUsage() {
        const os = await import('os');
        return new Promise((resolve) => {
            const cpus = os.cpus();
            const startMeasure = cpus.map(cpu => {
                const total = Object.values(cpu.times).reduce((acc, time) => acc + time, 0);
                const idle = cpu.times.idle;
                return { total, idle };
            });
            setTimeout(() => {
                const endMeasure = os.cpus().map(cpu => {
                    const total = Object.values(cpu.times).reduce((acc, time) => acc + time, 0);
                    const idle = cpu.times.idle;
                    return { total, idle };
                });
                let totalUsage = 0;
                for (let i = 0; i < startMeasure.length; i++) {
                    const totalDiff = endMeasure[i].total - startMeasure[i].total;
                    const idleDiff = endMeasure[i].idle - startMeasure[i].idle;
                    const usage = 100 - (100 * idleDiff / totalDiff);
                    totalUsage += usage;
                }
                const avgUsage = totalUsage / cpus.length;
                resolve(Math.round(avgUsage * 100) / 100);
            }, 100);
        });
    }
    async waitForLoadDecrease() {
        const maxWaitTime = 300000;
        const checkInterval = 5000;
        const startTime = Date.now();
        while (Date.now() - startTime < maxWaitTime) {
            const systemLoad = await this.getSystemLoad();
            if (systemLoad.memoryUsage > 90) {
                this.logger.warn(`内存使用率过高 (${systemLoad.memoryUsage.toFixed(1)}%)，停止等待`);
                throw new Error('内存使用率过高，终止启动');
            }
            if (systemLoad.cpuUsage <= 85) {
                this.logger.info(`CPU使用率已降低到 ${systemLoad.cpuUsage.toFixed(1)}%，继续启动`);
                return;
            }
            this.logger.info(`等待CPU负载降低，当前: CPU ${systemLoad.cpuUsage.toFixed(1)}%, 内存 ${systemLoad.memoryUsage.toFixed(1)}%`);
            await this.delay(checkInterval);
        }
        this.logger.warn('等待超时，继续启动剩余实例');
    }
    async delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    async detectStartScript(workingDirectory) {
        try {
            const files = await fs.readdir(workingDirectory);
            const platform = os.platform();
            const scriptNames = platform === 'win32'
                ? ['start.bat', 'run.bat', 'start.cmd', 'run.cmd']
                : ['start.sh', 'run.sh'];
            for (const scriptName of scriptNames) {
                if (files.includes(scriptName)) {
                    this.logger.info(`检测到启动脚本: ${scriptName}`);
                    return scriptName;
                }
            }
            return null;
        }
        catch (error) {
            this.logger.error('检测启动脚本失败:', error);
            return null;
        }
    }
    async detectJarFile(workingDirectory) {
        try {
            const files = await fs.readdir(workingDirectory);
            const jarFiles = files.filter(file => file.endsWith('.jar'));
            if (jarFiles.length === 0) {
                return null;
            }
            if (jarFiles.length === 1) {
                return jarFiles[0];
            }
            const serverJar = jarFiles.find(file => file.toLowerCase().includes('server'));
            if (serverJar) {
                return serverJar;
            }
            return jarFiles[0];
        }
        catch (error) {
            this.logger.error('检测jar文件失败:', error);
            return null;
        }
    }
    async getJavaPath(javaVersion) {
        if (!javaVersion) {
            return 'java';
        }
        try {
            const javaEnvironments = await this.javaManager.getJavaEnvironments();
            const javaEnv = javaEnvironments.find(env => env.version === javaVersion);
            if (javaEnv && javaEnv.installed && javaEnv.javaExecutable) {
                this.logger.info(`找到Java ${javaVersion} 路径: ${javaEnv.javaExecutable}`);
                return `"${javaEnv.javaExecutable}"`;
            }
            this.logger.warn(`未找到已安装的Java版本 ${javaVersion}，使用系统PATH中的java`);
            return 'java';
        }
        catch (error) {
            this.logger.error(`获取Java路径失败:`, error);
            this.logger.warn(`回退到系统PATH中的java`);
            return 'java';
        }
    }
    async initialize() {
        this.logger.info('初始化实例管理器...');
        await this.loadInstances();
        this.logger.info('实例管理器初始化完成');
    }
    async loadInstances() {
        try {
            const configDir = path.dirname(this.configPath);
            await fs.mkdir(configDir, { recursive: true });
            const data = await fs.readFile(this.configPath, 'utf-8');
            const instancesData = JSON.parse(data);
            for (const instanceData of instancesData) {
                let startCommand = instanceData.startCommand;
                if (startCommand === 'auto-detect-jar') {
                    startCommand = 'echo Minecraft Java Edition';
                    this.logger.info(`迁移实例 ${instanceData.name} 的旧启动命令占位符`);
                }
                const instance = {
                    ...instanceData,
                    startCommand,
                    status: 'stopped',
                    pid: undefined,
                    terminalSessionId: undefined,
                    enableStreamForward: instanceData.enableStreamForward ?? false,
                    programPath: instanceData.programPath ?? '',
                    terminalUser: instanceData.terminalUser ?? '',
                    instanceType: instanceData.instanceType ?? 'generic',
                    javaVersion: instanceData.javaVersion ?? undefined
                };
                this.instances.set(instance.id, instance);
            }
            this.logger.info(`已加载 ${this.instances.size} 个实例配置`);
            this.startAutoStartInstances();
        }
        catch (error) {
            if (error.code === 'ENOENT') {
                this.logger.info('实例配置文件不存在，将创建新文件');
                await this.saveInstances();
            }
            else {
                this.logger.error('加载实例配置失败:', error);
            }
        }
    }
    async saveInstances() {
        try {
            if (this.saveTimeout) {
                clearTimeout(this.saveTimeout);
            }
            this.saveTimeout = setTimeout(async () => {
                const instancesData = Array.from(this.instances.values()).map(instance => ({
                    id: instance.id,
                    name: instance.name,
                    description: instance.description,
                    workingDirectory: instance.workingDirectory,
                    startCommand: instance.startCommand,
                    autoStart: instance.autoStart,
                    stopCommand: instance.stopCommand,
                    createdAt: instance.createdAt,
                    lastStarted: instance.lastStarted,
                    lastStopped: instance.lastStopped,
                    enableStreamForward: instance.enableStreamForward,
                    programPath: instance.programPath,
                    terminalUser: instance.terminalUser,
                    instanceType: instance.instanceType,
                    javaVersion: instance.javaVersion
                }));
                await fs.writeFile(this.configPath, JSON.stringify(instancesData, null, 2));
                this.logger.debug('实例配置已保存');
            }, 1000);
        }
        catch (error) {
            this.logger.error('保存实例配置失败:', error);
        }
    }
    async startAutoStartInstances() {
        const autoStartInstances = Array.from(this.instances.values()).filter(instance => instance.autoStart);
        if (autoStartInstances.length === 0) {
            return;
        }
        this.logger.info(`开始错峰启动 ${autoStartInstances.length} 个自动启动实例`);
        for (let i = 0; i < autoStartInstances.length; i++) {
            const instance = autoStartInstances[i];
            try {
                const systemLoad = await this.getSystemLoad();
                if (systemLoad.memoryUsage > 90) {
                    this.logger.warn(`内存使用率过高 (${systemLoad.memoryUsage.toFixed(1)}%)，终止剩余实例启动`);
                    break;
                }
                if (systemLoad.cpuUsage > 90) {
                    this.logger.warn(`CPU使用率过高 (${systemLoad.cpuUsage.toFixed(1)}%)，等待负载降低后继续启动`);
                    await this.waitForLoadDecrease();
                }
                this.logger.info(`错峰启动实例 (${i + 1}/${autoStartInstances.length}): ${instance.name}`);
                await this.startInstance(instance.id);
                if (i < autoStartInstances.length - 1) {
                    await this.delay(2000);
                }
            }
            catch (error) {
                this.logger.error(`启动实例 ${instance.name} 失败:`, error);
            }
        }
        this.logger.info('错峰启动完成');
    }
    getInstances() {
        return Array.from(this.instances.values());
    }
    getInstance(id) {
        return this.instances.get(id);
    }
    async createInstance(data) {
        const id = uuidv4();
        const instance = {
            id,
            ...data,
            status: 'stopped',
            createdAt: new Date().toISOString()
        };
        this.instances.set(id, instance);
        await this.saveInstances();
        this.logger.info(`创建实例: ${instance.name} (${id})`);
        this.emit('instance-created', instance);
        return this.getInstance(id);
    }
    async updateInstance(id, data) {
        const instance = this.instances.get(id);
        if (!instance) {
            return null;
        }
        if (instance.status === 'running') {
            throw new Error('无法修改正在运行的实例配置');
        }
        const updatedInstance = {
            ...instance,
            ...data
        };
        this.instances.set(id, updatedInstance);
        await this.saveInstances();
        this.logger.info(`更新实例: ${updatedInstance.name} (${id})`);
        this.emit('instance-updated', updatedInstance);
        return this.getInstance(id);
    }
    async deleteInstance(id) {
        const instance = this.instances.get(id);
        if (!instance) {
            return false;
        }
        if (instance.status === 'running') {
            await this.stopInstance(id);
        }
        this.instances.delete(id);
        await this.saveInstances();
        this.logger.info(`删除实例: ${instance.name} (${id})`);
        this.emit('instance-deleted', { id, name: instance.name });
        return true;
    }
    async startInstance(id) {
        const instance = this.instances.get(id);
        if (!instance) {
            throw new Error('实例不存在');
        }
        if (instance.status === 'running') {
            throw new Error('实例已在运行');
        }
        if (instance.status === 'starting') {
            throw new Error('实例正在启动中');
        }
        try {
            instance.status = 'starting';
            this.emit('instance-status-changed', { id, status: 'starting' });
            try {
                await fs.access(instance.workingDirectory);
            }
            catch {
                throw new Error(`工作目录不存在: ${instance.workingDirectory}`);
            }
            const platform = os.platform();
            let startCommand = instance.startCommand.trim();
            if (instance.instanceType === 'minecraft-java') {
                const startScript = await this.detectStartScript(instance.workingDirectory);
                if (startScript) {
                    if (platform === 'win32') {
                        startCommand = startScript;
                    }
                    else {
                        startCommand = `./${startScript}`;
                    }
                    this.logger.info(`我的世界Java版检测到启动脚本: ${startCommand}`);
                }
                else {
                    const jarFile = await this.detectJarFile(instance.workingDirectory);
                    if (!jarFile) {
                        const errorMsg = `启动失败：工作目录中未找到启动脚本或.jar文件\n\n请确保工作目录（${instance.workingDirectory}）中包含以下文件之一：\n` +
                            (platform === 'win32'
                                ? '• 启动脚本：start.bat, run.bat, start.cmd, run.cmd\n• 或服务端核心：.jar文件'
                                : '• 启动脚本：start.sh, run.sh\n• 或服务端核心：.jar文件');
                        this.logger.error(errorMsg);
                        throw new Error(errorMsg);
                    }
                    const javaPath = await this.getJavaPath(instance.javaVersion);
                    if (platform === 'win32' && javaPath.startsWith('"')) {
                        startCommand = `& ${javaPath} -jar ${jarFile} nogui`;
                    }
                    else {
                        startCommand = `${javaPath} -jar ${jarFile} nogui`;
                    }
                    this.logger.info(`我的世界Java版自动生成启动命令: ${startCommand}`);
                }
            }
            if (instance.instanceType === 'minecraft-bedrock') {
                const bedrockExecutable = platform === 'win32' ? 'bedrock_server.exe' : 'bedrock_server';
                const bedrockPath = path.join(instance.workingDirectory, bedrockExecutable);
                try {
                    await fs.access(bedrockPath);
                    this.logger.info(`我的世界基岩版检测到启动文件: ${bedrockExecutable}`);
                }
                catch {
                    const errorMsg = `启动失败：工作目录中未找到基岩版服务端启动文件\n\n请确保工作目录（${instance.workingDirectory}）中包含以下文件：\n` +
                        (platform === 'win32'
                            ? '• 基岩版服务端：bedrock_server.exe'
                            : '• 基岩版服务端：bedrock_server');
                    this.logger.error(errorMsg);
                    throw new Error(errorMsg);
                }
            }
            if (startCommand.startsWith('./') && (platform === 'linux' || platform === 'darwin')) {
                const commandParts = startCommand.split(/\s+/);
                const scriptPath = commandParts[0].substring(2);
                const fullPath = path.join(instance.workingDirectory, scriptPath);
                try {
                    await fs.access(fullPath);
                    this.logger.info(`为文件添加可执行权限: ${fullPath}`);
                    await execAsync(`chmod +x "${fullPath}"`);
                    this.logger.info(`已为 ${scriptPath} 添加可执行权限`);
                }
                catch (error) {
                    if (error.code === 'ENOENT') {
                        this.logger.warn(`启动脚本不存在: ${fullPath}，将尝试继续启动`);
                    }
                    else {
                        this.logger.warn(`添加可执行权限失败: ${error.message}，将尝试继续启动`);
                    }
                }
            }
            const terminalSessionId = `instance-${id}-${Date.now()}`;
            const virtualSocket = {
                id: terminalSessionId,
                emit: (event, data) => {
                    if (event === 'terminal-output') {
                        this.emit('instance-output', { id, data: data.data });
                    }
                    else if (event === 'terminal-exit') {
                        if (instance.terminalSessionId !== terminalSessionId) {
                            this.logger.info(`忽略旧终端会话退出事件: ${terminalSessionId} (当前: ${instance.terminalSessionId})`);
                            return;
                        }
                        this.logger.info(`实例 ${instance.name} 终端会话退出`);
                        instance.status = 'stopped';
                        instance.pid = undefined;
                        instance.terminalSessionId = undefined;
                        instance.lastStopped = new Date().toISOString();
                        this.emit('instance-status-changed', { id, status: 'stopped' });
                        this.saveInstances();
                    }
                    else if (event === 'terminal-error') {
                        if (instance.terminalSessionId !== terminalSessionId) {
                            this.logger.info(`忽略旧终端会话错误事件: ${terminalSessionId} (当前: ${instance.terminalSessionId})`);
                            return;
                        }
                        this.logger.error(`实例 ${instance.name} 终端错误:`, data.error);
                        instance.status = 'error';
                        instance.pid = undefined;
                        instance.terminalSessionId = undefined;
                        this.emit('instance-status-changed', { id, status: 'error' });
                        this.saveInstances();
                    }
                }
            };
            const terminalCreated = new Promise((resolve, reject) => {
                const timeout = setTimeout(() => {
                    reject(new Error('终端会话创建超时'));
                }, 5000);
                const originalEmit = virtualSocket.emit;
                virtualSocket.emit = (event, data) => {
                    if (event === 'pty-created') {
                        clearTimeout(timeout);
                        resolve(data);
                    }
                    else if (event === 'terminal-error') {
                        clearTimeout(timeout);
                        reject(new Error(data.error));
                    }
                    originalEmit.call(virtualSocket, event, data);
                };
            });
            await this.terminalManager.createPty(virtualSocket, {
                sessionId: terminalSessionId,
                name: `实例: ${instance.name} (${instance.id})`,
                cols: 100,
                rows: 30,
                workingDirectory: instance.workingDirectory,
                enableStreamForward: instance.enableStreamForward || false,
                programPath: instance.programPath || '',
                autoCloseOnForwardExit: instance.enableStreamForward || false,
                terminalUser: instance.terminalUser
            });
            await terminalCreated;
            instance.terminalSessionId = terminalSessionId;
            if (!instance.enableStreamForward) {
                setTimeout(() => {
                    this.terminalManager.handleInput(virtualSocket, {
                        sessionId: terminalSessionId,
                        data: startCommand + '\r'
                    });
                }, 1000);
            }
            instance.status = 'running';
            instance.lastStarted = new Date().toISOString();
            this.logger.info(`启动实例: ${instance.name} (终端会话: ${terminalSessionId}), 启动命令: ${startCommand}`);
            this.emit('instance-status-changed', { id, status: 'running' });
            await this.saveInstances();
            return { success: true, terminalSessionId };
        }
        catch (error) {
            this.logger.error(`启动实例 ${instance.name} 失败:`, error);
            instance.status = 'error';
            instance.pid = undefined;
            instance.terminalSessionId = undefined;
            this.emit('instance-status-changed', { id, status: 'error' });
            throw error;
        }
    }
    async restartInstance(id) {
        const instance = this.instances.get(id);
        if (!instance) {
            throw new Error('实例不存在');
        }
        try {
            this.logger.info(`重启实例: ${instance.name}`);
            if (instance.status === 'running') {
                await this.stopInstance(id);
                await new Promise(resolve => {
                    const checkStatus = () => {
                        if (instance.status === 'stopped' || instance.status === 'error') {
                            resolve(void 0);
                        }
                        else {
                            setTimeout(checkStatus, 500);
                        }
                    };
                    checkStatus();
                });
                await new Promise(resolve => setTimeout(resolve, 2000));
            }
            const result = await this.startInstance(id);
            this.logger.info(`实例 ${instance.name} 重启完成`);
            return result;
        }
        catch (error) {
            this.logger.error(`重启实例 ${instance.name} 失败:`, error);
            throw error;
        }
    }
    async stopInstance(id) {
        const instance = this.instances.get(id);
        if (!instance) {
            throw new Error('实例不存在');
        }
        if (instance.status !== 'running') {
            throw new Error('实例未在运行');
        }
        if (!instance.terminalSessionId) {
            throw new Error('终端会话ID不存在');
        }
        try {
            instance.status = 'stopping';
            this.emit('instance-status-changed', { id, status: 'stopping' });
            this.logger.info(`停止实例: ${instance.name} (终端会话: ${instance.terminalSessionId})`);
            const virtualSocket = {
                id: instance.terminalSessionId,
                emit: () => { }
            };
            switch (instance.stopCommand) {
                case 'ctrl+c':
                    this.terminalManager.handleInput(virtualSocket, {
                        sessionId: instance.terminalSessionId,
                        data: '\u0003'
                    });
                    break;
                case 'stop':
                    this.terminalManager.handleInput(virtualSocket, {
                        sessionId: instance.terminalSessionId,
                        data: 'stop\r'
                    });
                    break;
                case 'exit':
                    this.terminalManager.handleInput(virtualSocket, {
                        sessionId: instance.terminalSessionId,
                        data: 'exit\r'
                    });
                    break;
                case 'quit':
                    this.terminalManager.handleInput(virtualSocket, {
                        sessionId: instance.terminalSessionId,
                        data: 'quit\r'
                    });
                    break;
            }
            setTimeout(() => {
                if (instance.status === 'stopping') {
                    this.logger.warn(`实例 ${instance.name} 未能优雅退出，强制关闭终端会话`);
                    this.terminalManager.closePty(virtualSocket, {
                        sessionId: instance.terminalSessionId
                    });
                    instance.status = 'stopped';
                    instance.pid = undefined;
                    instance.terminalSessionId = undefined;
                    instance.lastStopped = new Date().toISOString();
                    this.emit('instance-status-changed', { id, status: 'stopped' });
                    this.saveInstances();
                }
            }, 10000);
            return true;
        }
        catch (error) {
            this.logger.error(`停止实例 ${instance.name} 失败:`, error);
            instance.status = 'error';
            throw error;
        }
    }
    async closeTerminal(id) {
        const instance = this.instances.get(id);
        if (!instance) {
            throw new Error('实例不存在');
        }
        if (!instance.terminalSessionId) {
            throw new Error('终端会话不存在');
        }
        try {
            this.logger.info(`关闭实例终端: ${instance.name} (终端会话: ${instance.terminalSessionId})`);
            const virtualSocket = {
                id: instance.terminalSessionId,
                emit: () => { }
            };
            this.terminalManager.closePty(virtualSocket, {
                sessionId: instance.terminalSessionId
            });
            instance.status = 'stopped';
            instance.pid = undefined;
            instance.terminalSessionId = undefined;
            instance.lastStopped = new Date().toISOString();
            this.emit('instance-status-changed', { id, status: 'stopped' });
            await this.saveInstances();
            return true;
        }
        catch (error) {
            this.logger.error(`关闭实例 ${instance.name} 终端失败:`, error);
            throw error;
        }
    }
    getInstanceStatus(id) {
        const instance = this.instances.get(id);
        if (!instance) {
            return null;
        }
        return {
            status: instance.status,
            pid: instance.pid
        };
    }
    sendInput(id, input) {
        const instance = this.instances.get(id);
        if (!instance || !instance.terminalSessionId || instance.status !== 'running') {
            return false;
        }
        try {
            const virtualSocket = {
                id: instance.terminalSessionId,
                emit: () => { }
            };
            this.terminalManager.handleInput(virtualSocket, {
                sessionId: instance.terminalSessionId,
                data: input
            });
            return true;
        }
        catch (error) {
            this.logger.error(`向实例 ${instance.name} 发送输入失败:`, error);
            return false;
        }
    }
    async cleanup() {
        this.logger.info('清理实例管理器资源...');
        const runningInstances = Array.from(this.instances.values())
            .filter(instance => instance.status === 'running');
        for (const instance of runningInstances) {
            try {
                await this.stopInstance(instance.id);
            }
            catch (error) {
                this.logger.error(`清理时停止实例 ${instance.name} 失败:`, error);
            }
        }
        await this.saveInstances();
        if (this.saveTimeout) {
            clearTimeout(this.saveTimeout);
        }
    }
}
export default InstanceManager;
