import { spawn, exec } from "child_process";
import path from "path";
import fs from "fs";
import os from "os";
import { promisify } from "util";
import { logger } from "./logger.js";
const execAsync = promisify(exec);
// Mapping of environment variables
const shellEnvVariables = {
    http_proxy: "http://localhost:8888",
    HTTPS_PROXY: "http://localhost:8888",
    HTTP_PROXY: "http://localhost:8888",
    https_proxy: "http://localhost:8888",
    no_proxy: "",
    NO_PROXY: "",
};
let terminalProcess;
let terminalPID;
function getTerminalConfig() {
    const platform = os.platform();
    switch (platform) {
        case "win32":
            return {
                command: "cmd",
                args: ["/c", "start", "cmd", "/k"],
                scriptExtension: ".bat",
                scriptHeader: "@echo off",
                pidCommand: "echo %$",
            };
        case "darwin":
            return {
                command: "open",
                args: ["-a", "Terminal"],
                scriptExtension: ".sh",
                scriptHeader: "#!/bin/bash",
                pidCommand: "echo $$",
            };
        case "linux":
        default:
            // Try common Linux terminals in order of preference
            const linuxTerminals = [
                "gnome-terminal",
                "konsole",
                "xfce4-terminal",
                "xterm",
                "terminator",
                "alacritty",
            ];
            // For now, default to gnome-terminal, but this could be enhanced
            // to detect which terminal is available
            return {
                command: "gnome-terminal",
                args: ["--"],
                scriptExtension: ".sh",
                scriptHeader: "#!/bin/bash",
                pidCommand: "echo $$",
            };
    }
}
function createEnvironmentScript(config) {
    const tempDir = os.tmpdir();
    const scriptPath = path.join(tempDir, `skyramp-launch${config.scriptExtension}`);
    const pidFile = path.join(tempDir, "skyramp-terminal.pid");
    const infoMessage = os.platform() === "win32"
        ? "A new terminal has been spawned here by Skyramp to enable trace collection.\\nAsk skyramp agent to stop trace to terminate trace collection and return to where you were.\\nFor more information, visit skyramp.dev/docs."
        : "\\033[33mA new terminal has been spawned here by Skyramp to enable trace collection.\\nAsk skyramp agent to stop trace to terminate trace collection and return to where you were.\\nFor more information, visit skyramp.dev/docs.\\033[0m";
    let scriptContent;
    if (os.platform() === "win32") {
        // Windows batch script
        const setStatements = Object.entries(shellEnvVariables)
            .map(([key, value]) => `set ${key}=${value}`)
            .join("\n");
        scriptContent = `${config.scriptHeader}
${setStatements}
echo %^%^% > "${pidFile}"
echo ${infoMessage}
cmd /k`;
    }
    else {
        // Unix-like script (macOS/Linux)
        const exportStatements = Object.entries(shellEnvVariables)
            .map(([key, value]) => `export ${key}="${value}"`)
            .join("\n");
        scriptContent = `${config.scriptHeader}
${exportStatements}
${config.pidCommand} > "${pidFile}"
echo -e "${infoMessage}"
exec $SHELL`;
    }
    try {
        // Remove existing script if it exists
        if (fs.existsSync(scriptPath)) {
            try {
                fs.unlinkSync(scriptPath);
                logger.debug("Removed existing script file", { scriptPath });
            }
            catch (removeError) {
                logger.warning("Could not remove existing script file", {
                    scriptPath,
                    error: removeError instanceof Error
                        ? removeError.message
                        : String(removeError),
                });
            }
        }
        // Create the script
        fs.writeFileSync(scriptPath, scriptContent, { mode: 0o755 });
        // Verify the script was created successfully
        if (!fs.existsSync(scriptPath)) {
            throw new Error("Script file was not created successfully");
        }
        // Check if the script is readable and executable
        try {
            fs.accessSync(scriptPath, fs.constants.R_OK | fs.constants.X_OK);
        }
        catch (accessError) {
            logger.warning("Script may not have correct permissions", {
                scriptPath,
                error: accessError instanceof Error
                    ? accessError.message
                    : String(accessError),
            });
        }
        logger.debug("Cross-platform script created and verified", {
            platform: os.platform(),
            scriptPath,
            scriptSize: fs.statSync(scriptPath).size,
        });
        return scriptPath;
    }
    catch (error) {
        logger.error("Failed to create terminal script", {
            platform: os.platform(),
            scriptPath,
            tempDir,
            error: error instanceof Error ? error.message : String(error),
        });
        throw error;
    }
}
async function findAvailableTerminal() {
    if (os.platform() !== "linux") {
        return null; // Use default for non-Linux platforms
    }
    const terminals = [
        "gnome-terminal",
        "konsole",
        "xfce4-terminal",
        "xterm",
        "terminator",
        "alacritty",
        "kitty",
    ];
    for (const terminal of terminals) {
        try {
            await execAsync(`which ${terminal}`);
            logger.debug("Found available terminal", { terminal });
            return terminal;
        }
        catch {
            // Terminal not found, try next
        }
    }
    logger.warning("No common Linux terminal found, falling back to xterm");
    return "xterm";
}
export default async function openProxyTerminal() {
    logger.info("Starting cross-platform proxy terminal creation", {
        platform: os.platform(),
        arch: os.arch(),
    });
    try {
        const config = getTerminalConfig();
        // For Linux, try to find an available terminal
        if (os.platform() === "linux") {
            const availableTerminal = await findAvailableTerminal();
            if (availableTerminal && availableTerminal !== "gnome-terminal") {
                config.command = availableTerminal;
                // Adjust args based on terminal
                if (availableTerminal === "xterm") {
                    config.args = ["-e"];
                }
                else if (availableTerminal === "konsole") {
                    config.args = ["-e"];
                }
            }
        }
        const scriptPath = createEnvironmentScript(config);
        const pidFile = path.join(os.tmpdir(), "skyramp-terminal.pid");
        // Verify script exists before launching terminal
        if (!fs.existsSync(scriptPath)) {
            throw new Error(`Script file does not exist: ${scriptPath}`);
        }
        // Verify script is accessible
        try {
            fs.accessSync(scriptPath, fs.constants.R_OK | fs.constants.X_OK);
        }
        catch (accessError) {
            throw new Error(`Script file is not accessible: ${scriptPath} - ${accessError instanceof Error
                ? accessError.message
                : String(accessError)}`);
        }
        logger.debug("Script verified before terminal launch", {
            scriptPath,
            scriptSize: fs.statSync(scriptPath).size,
        });
        // Launch terminal with script
        const spawnArgs = [...config.args, scriptPath];
        terminalProcess = spawn(config.command, spawnArgs, {
            env: {
                ...process.env,
                ...shellEnvVariables,
            },
            detached: true,
            stdio: "ignore",
        });
        logger.debug("Spawned cross-platform terminal", {
            platform: os.platform(),
            command: config.command,
            args: spawnArgs,
            pid: terminalProcess.pid,
        });
        // Handle process events
        terminalProcess.on("exit", (code) => {
            logger.info("Terminal launch command completed", {
                platform: os.platform(),
                exitCode: code,
            });
            // Wait for terminal to write PID and execute script
            // On macOS, Terminal.app takes longer to start and execute the script
            const cleanupDelay = os.platform() === "darwin" ? 10000 : 3000;
            setTimeout(() => {
                try {
                    if (fs.existsSync(pidFile)) {
                        const pidContent = fs.readFileSync(pidFile, "utf8").trim();
                        const pid = parseInt(pidContent);
                        if (!isNaN(pid)) {
                            terminalPID = pid;
                            logger.info("Terminal PID captured", { terminalPID: pid });
                        }
                        // Cleanup PID file
                        try {
                            fs.unlinkSync(pidFile);
                        }
                        catch (cleanupError) {
                            logger.debug("PID file cleanup completed with minor issues", {
                                error: cleanupError instanceof Error
                                    ? cleanupError.message
                                    : String(cleanupError),
                            });
                        }
                    }
                    // Cleanup script file with additional delay and verification
                    setTimeout(() => {
                        try {
                            if (fs.existsSync(scriptPath)) {
                                fs.unlinkSync(scriptPath);
                                logger.debug("Script file cleaned up", { scriptPath });
                            }
                        }
                        catch (scriptCleanupError) {
                            logger.debug("Script cleanup completed with minor issues", {
                                error: scriptCleanupError instanceof Error
                                    ? scriptCleanupError.message
                                    : String(scriptCleanupError),
                            });
                        }
                    }, 2000);
                }
                catch (err) {
                    logger.warning("Could not read terminal PID", {
                        error: err instanceof Error ? err.message : String(err),
                    });
                    // Even if PID reading fails, still cleanup script after delay
                    setTimeout(() => {
                        try {
                            if (fs.existsSync(scriptPath)) {
                                fs.unlinkSync(scriptPath);
                                logger.debug("Script file cleaned up (fallback)", {
                                    scriptPath,
                                });
                            }
                        }
                        catch (scriptCleanupError) {
                            logger.debug("Fallback script cleanup completed with minor issues", {
                                error: scriptCleanupError instanceof Error
                                    ? scriptCleanupError.message
                                    : String(scriptCleanupError),
                            });
                        }
                    }, 2000);
                }
            }, cleanupDelay);
        });
        terminalProcess.on("error", (error) => {
            logger.error("Terminal spawn error", {
                platform: os.platform(),
                command: config.command,
                error: error.message,
            });
        });
        logger.info("Cross-platform proxy terminal opened successfully", {
            platform: os.platform(),
            arch: os.arch(),
        });
    }
    catch (error) {
        logger.error("Failed to open proxy terminal", {
            platform: os.platform(),
            error: error instanceof Error ? error.message : String(error),
        });
        throw error;
    }
}
export async function closeProxyTerminal() {
    logger.info("Starting cross-platform proxy terminal closure", {
        platform: os.platform(),
    });
    // Kill the spawn process if it exists
    if (terminalProcess) {
        const killResult = terminalProcess.kill();
        logger.debug("Terminal spawn process kill result", { killResult });
    }
    // Handle platform-specific terminal closing
    if (terminalPID) {
        try {
            const platform = os.platform();
            if (platform === "darwin") {
                // macOS: First try to kill the specific PID and its process group
                try {
                    // Kill the process group to ensure child processes are also terminated
                    process.kill(-terminalPID, "SIGTERM");
                    logger.info("macOS terminal process group killed via SIGTERM", {
                        terminalPID,
                    });
                    // Give processes time to terminate gracefully
                    await new Promise((resolve) => setTimeout(resolve, 1000));
                    // Check if process still exists and force kill if needed
                    try {
                        process.kill(terminalPID, 0); // Check if process exists
                        process.kill(-terminalPID, "SIGKILL");
                        logger.info("macOS terminal process group force killed", {
                            terminalPID,
                        });
                    }
                    catch (killErr) {
                        // Process is already dead, which is what we want
                        logger.debug("Terminal process already terminated", {
                            terminalPID,
                        });
                    }
                }
                catch (pidErr) {
                    logger.debug("PID-based kill failed, trying AppleScript fallback", {
                        error: pidErr instanceof Error ? pidErr.message : String(pidErr),
                    });
                    // Fallback: Use AppleScript to close terminal windows with our proxy environment
                    const closeScript = `tell application "Terminal"
            repeat with w in windows
              repeat with t in tabs of w
                try
                  -- Send exit command to close the shell in this tab
                  do script "exit" in t
                end try
              end repeat
            end repeat
          end tell`;
                    await execAsync(`osascript -e '${closeScript.replace(/'/g, "'\\''")}'`);
                    logger.info("macOS terminal closed via AppleScript fallback");
                }
            }
            else if (platform === "win32") {
                // Windows: Use taskkill with /T flag to kill process tree
                await execAsync(`taskkill /PID ${terminalPID} /T /F`);
                logger.info("Windows terminal and children closed via taskkill", {
                    terminalPID,
                });
            }
            else {
                // Linux: Send SIGTERM to process group
                try {
                    process.kill(-terminalPID, "SIGTERM");
                    logger.info("Linux terminal process group closed via SIGTERM", {
                        terminalPID,
                    });
                    // Give processes time to terminate gracefully
                    await new Promise((resolve) => setTimeout(resolve, 1000));
                    // Check if process still exists and force kill if needed
                    try {
                        process.kill(terminalPID, 0); // Check if process exists
                        process.kill(-terminalPID, "SIGKILL");
                        logger.info("Linux terminal process group force killed", {
                            terminalPID,
                        });
                    }
                    catch (killErr) {
                        // Process is already dead, which is what we want
                        logger.debug("Terminal process already terminated", {
                            terminalPID,
                        });
                    }
                }
                catch (pidErr) {
                    // Fallback to regular process kill
                    try {
                        process.kill(terminalPID, "SIGTERM");
                        logger.info("Linux terminal closed via fallback SIGTERM", {
                            terminalPID,
                        });
                    }
                    catch (fallbackErr) {
                        logger.warning("Could not kill terminal process", {
                            terminalPID,
                            error: fallbackErr instanceof Error
                                ? fallbackErr.message
                                : String(fallbackErr),
                        });
                    }
                }
            }
        }
        catch (err) {
            logger.warning("Error during platform-specific terminal closure", {
                platform: os.platform(),
                terminalPID,
                error: err instanceof Error ? err.message : String(err),
            });
        }
    }
    else {
        logger.warning("No terminal PID available for cleanup - terminal may not have been properly tracked");
    }
    // Reset state
    terminalProcess = undefined;
    terminalPID = undefined;
    logger.info("Cross-platform proxy terminal closure completed", {
        platform: os.platform(),
    });
}
