import { EventEmitter } from "events";
import { exec } from "child_process";
import { getErrorMessage } from "../types/index.js";
let pty = null;
try {
    // Dynamic import to avoid crash if node-pty isn't installed
    pty = require("node-pty");
}
catch {
    // node-pty not available
}
// Dangerous command patterns that should be blocked
const BLOCKED_PATTERNS = [
    /rm\s+(-rf?|--recursive)\s+[/~]/i, // rm -rf on root or home
    /mkfs\./i, // Format filesystem
    /dd\s+.*of=\/dev\//i, // dd to device
    />\s*\/dev\/sd[a-z]/i, // Redirect to disk
    /chmod\s+-R\s+777\s+\//i, // chmod 777 on root
    /chown\s+-R\s+.*\s+\//i, // chown on root
    /:(){ :|:& };:/, // Fork bomb
    /\|\s*sh\s*$/i, // Piping to shell
    /curl.*\|\s*(ba)?sh/i, // curl | bash
    /wget.*\|\s*(ba)?sh/i, // wget | bash
];
/**
 * Validate command for dangerous patterns
 * @returns null if safe, error message if dangerous
 */
function validateCommand(command) {
    for (const pattern of BLOCKED_PATTERNS) {
        if (pattern.test(command)) {
            return `Blocked potentially dangerous command pattern: ${pattern.source}`;
        }
    }
    return null;
}
export class InteractiveBashTool extends EventEmitter {
    sessions = new Map();
    sessionCounter = 0;
    isPTYAvailable;
    constructor() {
        super();
        this.isPTYAvailable = pty !== null;
    }
    isPTYSupported() {
        return this.isPTYAvailable;
    }
    async executeInteractive(command, options = {}) {
        // Validate command for dangerous patterns before execution
        const validationError = validateCommand(command);
        if (validationError) {
            return { sessionId: '', output: `Error: ${validationError}` };
        }
        if (!this.isPTYAvailable) {
            return this.fallbackExecute(command);
        }
        const sessionId = `pty-${++this.sessionCounter}`;
        const cols = options.cols || 120;
        const rows = options.rows || 30;
        return new Promise((resolve, reject) => {
            try {
                if (!pty) {
                    reject(new Error("PTY module not available"));
                    return;
                }
                const shell = pty.spawn("bash", ["-c", command], {
                    name: "xterm-256color",
                    cols,
                    rows,
                    cwd: options.cwd || process.cwd(),
                    env: {
                        ...process.env,
                        ...options.env,
                        TERM: "xterm-256color",
                    },
                });
                let output = "";
                let resolved = false;
                const session = {
                    id: sessionId,
                    command,
                    startTime: new Date(),
                    isRunning: true,
                };
                this.sessions.set(sessionId, { shell, session });
                shell.onData((data) => {
                    output += data;
                    this.emit("pty:data", { sessionId, data });
                });
                shell.onExit(({ exitCode }) => {
                    session.isRunning = false;
                    this.emit("pty:exit", { sessionId, exitCode });
                    if (!resolved) {
                        resolved = true;
                        resolve({ sessionId, output });
                    }
                });
                // Timeout for non-interactive commands
                setTimeout(() => {
                    if (!resolved && !this.isInteractiveCommand(command)) {
                        resolved = true;
                        shell.kill();
                        resolve({ sessionId, output });
                    }
                }, 30000);
            }
            catch (error) {
                reject(new Error(`PTY execution failed: ${getErrorMessage(error)}`));
            }
        });
    }
    isInteractiveCommand(command) {
        const interactiveCommands = [
            "vim",
            "vi",
            "nano",
            "emacs",
            "htop",
            "top",
            "less",
            "more",
            "man",
            "git rebase -i",
            "git add -i",
            "git add -p",
            "ssh",
            "python",
            "node",
            "irb",
            "rails console",
            "mysql",
            "psql",
            "mongo",
        ];
        return interactiveCommands.some((cmd) => command.toLowerCase().includes(cmd));
    }
    async fallbackExecute(command) {
        const sessionId = `exec-${++this.sessionCounter}`;
        return new Promise((resolve) => {
            // Use exec for simpler command execution with maxBuffer support
            exec(command, {
                timeout: 60000,
                maxBuffer: 10 * 1024 * 1024,
                cwd: process.cwd(),
                shell: '/bin/bash',
                env: {
                    ...process.env,
                    // Disable shell history for security
                    HISTFILE: "/dev/null",
                    HISTSIZE: "0",
                },
            }, (error, stdout, stderr) => {
                if (error) {
                    resolve({
                        sessionId,
                        output: `Error: ${getErrorMessage(error)}\n${stderr || ''}`,
                    });
                }
                else {
                    resolve({
                        sessionId,
                        output: stdout + (stderr ? `\nStderr:\n${stderr}` : ""),
                    });
                }
            });
        });
    }
    sendInput(sessionId, input) {
        const entry = this.sessions.get(sessionId);
        if (!entry || !entry.session.isRunning) {
            return false;
        }
        try {
            entry.shell.write(input);
            return true;
        }
        catch {
            return false;
        }
    }
    sendKey(sessionId, key) {
        const keyMap = {
            enter: "\r",
            tab: "\t",
            escape: "\x1b",
            up: "\x1b[A",
            down: "\x1b[B",
            left: "\x1b[D",
            right: "\x1b[C",
            "ctrl-c": "\x03",
            "ctrl-d": "\x04",
            "ctrl-z": "\x1a",
            "ctrl-l": "\x0c",
        };
        const keyCode = keyMap[key.toLowerCase()] || key;
        return this.sendInput(sessionId, keyCode);
    }
    resize(sessionId, cols, rows) {
        const entry = this.sessions.get(sessionId);
        if (!entry || !this.isPTYAvailable) {
            return false;
        }
        try {
            entry.shell.resize(cols, rows);
            return true;
        }
        catch {
            return false;
        }
    }
    kill(sessionId) {
        const entry = this.sessions.get(sessionId);
        if (!entry) {
            return false;
        }
        try {
            entry.shell.kill();
            entry.session.isRunning = false;
            return true;
        }
        catch {
            return false;
        }
    }
    getSession(sessionId) {
        const entry = this.sessions.get(sessionId);
        return entry?.session || null;
    }
    getActiveSessions() {
        return Array.from(this.sessions.values())
            .map((entry) => entry.session)
            .filter((session) => session.isRunning);
    }
    async executeWithToolResult(command, options) {
        try {
            const { output } = await this.executeInteractive(command, options);
            // Strip ANSI codes for cleaner output
            const cleanOutput = this.stripAnsi(output);
            return {
                success: true,
                output: cleanOutput,
            };
        }
        catch (error) {
            return {
                success: false,
                error: getErrorMessage(error),
            };
        }
    }
    stripAnsi(str) {
        // eslint-disable-next-line no-control-regex
        return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
    }
    formatHelp() {
        const ptyStatus = this.isPTYAvailable
            ? "✓ Available"
            : "✗ Not installed (install node-pty for full support)";
        return `
Interactive Terminal (PTY) Support

Status: ${ptyStatus}

Capabilities:
  - Run interactive commands (vim, htop, git rebase -i)
  - Full terminal emulation with color support
  - Send keystrokes and control sequences
  - Resize terminal dimensions

Usage:
  /interactive <command>   Run command in PTY
  /pty send <input>        Send input to active session
  /pty key <keyname>       Send special key (enter, ctrl-c, etc.)
  /pty resize <cols> <rows> Resize terminal
  /pty kill <sessionId>    Kill session

Special Keys:
  enter, tab, escape, up, down, left, right
  ctrl-c, ctrl-d, ctrl-z, ctrl-l

Examples:
  /interactive vim file.txt
  /interactive htop
  /interactive git rebase -i HEAD~5

Note: PTY support requires the 'node-pty' package:
  npm install node-pty
  # or
  bun add node-pty
`;
    }
    cleanup() {
        for (const [sessionId] of this.sessions) {
            this.kill(sessionId);
        }
        this.sessions.clear();
    }
    /**
     * Dispose the tool and clean up all resources
     */
    dispose() {
        this.cleanup();
        this.removeAllListeners();
    }
}
// Singleton instance
let interactiveBashInstance = null;
export function getInteractiveBash() {
    if (!interactiveBashInstance) {
        interactiveBashInstance = new InteractiveBashTool();
    }
    return interactiveBashInstance;
}
//# sourceMappingURL=interactive-bash.js.map