import { spawn } from 'node:child_process';
import { randomUUID } from 'node:crypto';
import { EventEmitter } from 'node:events';
import { platform } from 'node:process';
import { BASH_OUTPUT_PREVIEW_LENGTH, INTERVAL_BASH_PROGRESS_MS, } from '../constants.js';
const isWindows = platform === 'win32';
export class BashExecutor extends EventEmitter {
    executions = new Map();
    execute(command) {
        const executionId = randomUUID();
        const state = {
            executionId,
            command,
            outputPreview: '',
            fullOutput: '',
            stderr: '',
            isComplete: false,
            exitCode: null,
            error: null,
        };
        const proc = isWindows
            ? spawn('cmd', ['/c', command])
            : spawn('sh', ['-c', command]);
        // Collect output
        proc.stdout.on('data', (data) => {
            state.fullOutput += data.toString();
            state.outputPreview = state.fullOutput.slice(-BASH_OUTPUT_PREVIEW_LENGTH);
        });
        proc.stderr.on('data', (data) => {
            state.stderr += data.toString();
        });
        // Progress interval - emit updates every 500ms
        const intervalId = setInterval(() => {
            this.emit('progress', { ...state });
        }, INTERVAL_BASH_PROGRESS_MS);
        const promise = new Promise((resolve, _reject) => {
            // Store resolve function so cancel() can resolve the promise
            this.executions.set(executionId, {
                state,
                process: proc,
                intervalId,
                resolve,
            });
            proc.on('close', (code) => {
                // Only process if not already handled by cancel()
                if (!this.executions.has(executionId))
                    return;
                clearInterval(intervalId);
                state.isComplete = true;
                state.exitCode = code;
                this.emit('complete', { ...state });
                this.executions.delete(executionId);
                resolve({ ...state });
            });
            proc.on('error', (error) => {
                // Only process if not already handled by cancel()
                if (!this.executions.has(executionId))
                    return;
                clearInterval(intervalId);
                state.isComplete = true;
                state.error = error.message;
                this.emit('complete', { ...state });
                this.executions.delete(executionId);
                resolve({ ...state }); // Resolve with error state instead of rejecting
            });
        });
        // Emit initial state
        this.emit('start', { ...state });
        return { executionId, promise };
    }
    cancel(executionId) {
        const execution = this.executions.get(executionId);
        if (!execution)
            return false;
        clearInterval(execution.intervalId);
        execution.process.kill('SIGTERM');
        execution.state.isComplete = true;
        execution.state.error = 'Cancelled by user';
        this.emit('complete', { ...execution.state });
        // Resolve the promise with the cancelled state
        execution.resolve({ ...execution.state });
        this.executions.delete(executionId);
        return true;
    }
    getState(executionId) {
        const execution = this.executions.get(executionId);
        return execution ? { ...execution.state } : undefined;
    }
    hasActiveExecutions() {
        return this.executions.size > 0;
    }
    getActiveExecutionIds() {
        return Array.from(this.executions.keys());
    }
}
// Singleton instance for app-wide use
export const bashExecutor = new BashExecutor();
//# sourceMappingURL=bash-executor.js.map