/**
 * Terminal Handler
 *
 * Manages PTY processes for terminal sessions.
 * Uses node-pty for pseudo-terminal support.
 */
import { WebSocket } from 'ws';
import { spawn } from 'node-pty';
import { platform, homedir } from 'os';
import { existsSync } from 'fs';
import { exec, execSync } from 'child_process';
// Terminal process manager
export class TerminalManager {
    terminals = new Map();
    // Kill process using port (backend side)
    async killProcessOnPort(port) {
        return new Promise((resolve) => {
            const plat = platform();
            if (plat === 'win32') {
                // Windows: use netstat and taskkill
                const command = `netstat -ano | findstr :${port}`;
                exec(command, { shell: 'cmd.exe' }, (error, stdout) => {
                    if (error || !stdout.trim()) {
                        resolve(false);
                        return;
                    }
                    const lines = stdout.trim().split('\n');
                    const pids = [];
                    lines.forEach(line => {
                        const parts = line.trim().split(/\s+/);
                        if (parts.length >= 5) {
                            const pid = parts[parts.length - 1];
                            if (pid && !pids.includes(pid)) {
                                pids.push(pid);
                            }
                        }
                    });
                    if (pids.length === 0) {
                        resolve(false);
                        return;
                    }
                    console.log(`[TerminalManager] Killing processes on port ${port}: PID ${pids.join(', ')}`);
                    let killed = 0;
                    pids.forEach(pid => {
                        try {
                            execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
                            killed++;
                        }
                        catch (e) {
                            // Ignore error
                        }
                    });
                    console.log(`[TerminalManager] Killed ${killed} process(es) on port ${port}`);
                    setTimeout(() => resolve(killed > 0), 1000);
                });
            }
            else {
                // macOS/Linux: use lsof and kill
                const command = `lsof -ti:${port}`;
                exec(command, (error, stdout) => {
                    if (error || !stdout.trim()) {
                        resolve(false);
                        return;
                    }
                    const pids = stdout.trim().split('\n').filter(Boolean);
                    if (pids.length === 0) {
                        resolve(false);
                        return;
                    }
                    console.log(`[TerminalManager] Killing processes on port ${port}: PID ${pids.join(', ')}`);
                    let killed = 0;
                    for (const pid of pids) {
                        try {
                            process.kill(parseInt(pid, 10), 'SIGTERM');
                            killed++;
                        }
                        catch (e) {
                            // Process may already be gone
                        }
                    }
                    console.log(`[TerminalManager] Killed ${killed} process(es) on port ${port}`);
                    setTimeout(() => resolve(killed > 0), 1000);
                });
            }
        });
    }
    getDefaultShell() {
        const plat = platform();
        if (plat === 'win32') {
            return process.env.ComSpec || 'cmd.exe';
        }
        // Priority order for Unix shells
        const shells = [
            process.env.SHELL,
            '/bin/zsh',
            '/bin/bash',
            '/bin/sh',
        ].filter((s) => Boolean(s) && existsSync(s));
        return shells[0] || '/bin/bash';
    }
    getAvailableShells() {
        const plat = platform();
        if (plat === 'win32') {
            return [process.env.ComSpec || 'cmd.exe'];
        }
        return ['/bin/zsh', '/bin/bash', '/bin/sh'].filter(existsSync);
    }
    getUserPath(shell) {
        try {
            if (platform() !== 'win32') {
                const command = `${shell} -l -c 'echo $PATH'`;
                const result = execSync(command, {
                    encoding: 'utf-8',
                    timeout: 5000,
                    env: { ...process.env, PATH: process.env.PATH || '/usr/bin:/bin' }
                });
                const path = result.trim();
                if (path && path.includes('/')) {
                    return path;
                }
            }
        }
        catch (error) {
            console.warn('[TerminalManager] Failed to get user PATH:', error);
        }
        // Fallback PATH
        const plat = platform();
        const existingPath = process.env.PATH || '';
        const commonPaths = [];
        if (plat === 'win32') {
            // Windows common paths for npm, node, and global modules
            const home = homedir();
            commonPaths.push(`${home}\\AppData\\Roaming\\npm`, // npm global prefix on Windows
            'C:\\Program Files\\nodejs', // Node.js install path
            'C:\\Program Files (x86)\\nodejs', // Node.js x86 install path
            `${home}\\AppData\\Local\\Programs\\Microsoft VS Code\\bin`, // VS Code CLI
            'C:\\Windows\\System32', 'C:\\Windows');
        }
        else {
            // macOS/Linux common paths
            commonPaths.push('/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/opt/homebrew/bin', '/opt/homebrew/sbin', '/usr/local/sbin');
        }
        const pathSeparator = plat === 'win32' ? ';' : ':';
        return [existingPath, ...commonPaths].filter(Boolean).join(pathSeparator);
    }
    prepareEnv(shell) {
        const env = {};
        const plat = platform();
        // Windows 需要保留所有系统环境变量，特别是网络相关的
        if (plat === 'win32') {
            // 复制所有现有环境变量（这是关键）
            for (const [key, value] of Object.entries(process.env)) {
                if (value !== undefined) {
                    env[key] = value;
                }
            }
        }
        else {
            // macOS/Linux: Copy essential environment variables
            const essentialVars = ['HOME', 'USER', 'LOGNAME', 'TMPDIR', 'SSH_AUTH_SOCK', 'LANG', 'LC_ALL'];
            for (const key of essentialVars) {
                if (process.env[key]) {
                    env[key] = process.env[key];
                }
            }
        }
        env.PATH = this.getUserPath(shell);
        env.HOME = env.HOME || homedir();
        env.USER = env.USER || 'user';
        env.SHELL = shell;
        env.TERM = 'xterm-256color';
        // Shell-specific prompt configuration (show current directory name)
        if (plat !== 'win32') {
            if (shell.includes('zsh')) {
                env.PS1 = '%1~ %# ';
            }
            else {
                env.PS1 = '\\W $ ';
            }
        }
        // Fix for corporate network isolation: bind to all interfaces instead of 127.0.0.1
        // This allows dev servers (Vue, React, etc.) to work in restricted network environments
        // Set HOST for all platforms to ensure dev servers bind correctly
        env.HOST = '0.0.0.0';
        return env;
    }
    resolveCwd(cwd) {
        if (cwd && existsSync(cwd)) {
            return cwd;
        }
        return homedir();
    }
    create(id, sessionId, cwd) {
        return new Promise((resolve, reject) => {
            const shell = this.getDefaultShell();
            const workingDir = this.resolveCwd(cwd);
            const env = this.prepareEnv(shell);
            console.log('[TerminalManager] Creating terminal:', { id, sessionId, shell, cwd: workingDir });
            if (!existsSync(shell)) {
                reject(new Error(`Shell not found: ${shell}`));
                return;
            }
            if (!existsSync(workingDir)) {
                reject(new Error(`Working directory does not exist: ${workingDir}`));
                return;
            }
            try {
                const pty = spawn(shell, [], {
                    name: 'xterm-256color',
                    cols: 80,
                    rows: 24,
                    cwd: workingDir,
                    env: env,
                });
                this.terminals.set(id, { pty, sessionId });
                resolve({ success: true, shell });
            }
            catch (error) {
                console.error('[TerminalManager] Failed to spawn:', error);
                reject(error);
            }
        });
    }
    get(id) {
        return this.terminals.get(id)?.pty;
    }
    write(id, data) {
        const entry = this.terminals.get(id);
        if (entry) {
            entry.pty.write(data);
            return true;
        }
        return false;
    }
    resize(id, cols, rows) {
        const entry = this.terminals.get(id);
        if (entry) {
            entry.pty.resize(cols, rows);
            return true;
        }
        return false;
    }
    kill(id) {
        const entry = this.terminals.get(id);
        if (entry) {
            entry.pty.kill();
            this.terminals.delete(id);
            return true;
        }
        return false;
    }
    setupEventHandlers(id, ws, onData, onExit) {
        const entry = this.terminals.get(id);
        if (!entry)
            return;
        entry.pty.onData(onData);
        entry.pty.onExit(({ exitCode }) => {
            this.terminals.delete(id);
            onExit(exitCode);
        });
    }
    killSessionTerminals(sessionId) {
        for (const [id, entry] of this.terminals) {
            if (entry.sessionId === sessionId) {
                entry.pty.kill();
                this.terminals.delete(id);
            }
        }
    }
    disposeAll() {
        for (const entry of this.terminals.values()) {
            entry.pty.kill();
        }
        this.terminals.clear();
    }
}
// Terminal handler for WebSocket
export function setupTerminalHandler(ws, sessionId, terminalManager) {
    const pendingHandlers = new Map();
    return (msg) => {
        const { type, id, method, args } = msg;
        if (method === 'terminal:create') {
            const terminalId = args[0];
            const cwd = args[1] || homedir();
            const portToKill = args[2]; // Optional port to kill before creating
            // Kill port if specified (backend side, silent)
            const createTerminal = () => {
                terminalManager.create(terminalId, sessionId, cwd)
                    .then((result) => {
                    // Setup event handlers
                    terminalManager.setupEventHandlers(terminalId, ws, (data) => {
                        if (ws.readyState === WebSocket.OPEN) {
                            ws.send(JSON.stringify({
                                type: 'terminal:data',
                                sessionId: terminalId,
                                data,
                            }));
                        }
                    }, (exitCode) => {
                        if (ws.readyState === WebSocket.OPEN) {
                            ws.send(JSON.stringify({
                                type: 'terminal:exit',
                                sessionId: terminalId,
                                exitCode,
                            }));
                        }
                        pendingHandlers.delete(terminalId);
                    });
                    ws.send(JSON.stringify({ type: 'response', id, result }));
                })
                    .catch((error) => {
                    ws.send(JSON.stringify({ type: 'error', id, error: String(error) }));
                });
            };
            // If port specified, kill it first then create terminal
            if (portToKill && portToKill > 0) {
                terminalManager.killProcessOnPort(portToKill).then(() => {
                    setTimeout(() => {
                        createTerminal();
                    }, 500);
                });
            }
            else {
                createTerminal();
            }
            return;
        }
        if (method === 'terminal:write') {
            const terminalId = args[0];
            const data = args[1];
            const success = terminalManager.write(terminalId, data);
            if (success) {
                ws.send(JSON.stringify({ type: 'response', id, result: { success: true } }));
            }
            else {
                ws.send(JSON.stringify({ type: 'error', id, error: `Terminal not found: ${terminalId}` }));
            }
            return;
        }
        if (method === 'terminal:resize') {
            const terminalId = args[0];
            const cols = args[1];
            const rows = args[2];
            const success = terminalManager.resize(terminalId, cols, rows);
            if (success) {
                ws.send(JSON.stringify({ type: 'response', id, result: { success: true } }));
            }
            else {
                ws.send(JSON.stringify({ type: 'error', id, error: `Terminal not found: ${terminalId}` }));
            }
            return;
        }
        if (method === 'terminal:kill') {
            const terminalId = args[0];
            const success = terminalManager.kill(terminalId);
            if (success) {
                ws.send(JSON.stringify({ type: 'response', id, result: { success: true } }));
            }
            else {
                ws.send(JSON.stringify({ type: 'error', id, error: `Terminal not found: ${terminalId}` }));
            }
            return;
        }
        if (method === 'terminal:getShells') {
            const shells = terminalManager.getAvailableShells();
            ws.send(JSON.stringify({ type: 'response', id, result: shells }));
            return;
        }
        // Unknown terminal method
        ws.send(JSON.stringify({ type: 'error', id, error: `Unknown terminal method: ${method}` }));
    };
}
//# sourceMappingURL=terminal.js.map