/**
 * Secure Tmux Wrapper
 * Provides secure tmux operations with comprehensive input validation
 */

import { CommandSanitizer, SanitizedCommand } from './commandSanitizer';
import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

export interface TmuxExecutionResult {
	success: boolean;
	stdout: string;
	stderr: string;
	command: string;
	timestamp: string;
}

export interface TmuxExecutionOptions {
	timeout?: number;
	maxBuffer?: number;
	cwd?: string;
	env?: Record<string, string>;
}

export class SecureTmuxWrapper {
	private static readonly DEFAULT_TIMEOUT = 30000; // 30 seconds
	private static readonly DEFAULT_MAX_BUFFER = 1024 * 1024; // 1MB
	private static readonly TMUX_BINARY = 'tmux';

	/**
	 * Execute a tmux command securely
	 */
	public static async executeSecurely(
		command: string,
		args: string[],
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		const startTime = new Date().toISOString();

		try {
			// Sanitize the command and arguments
			const sanitized = CommandSanitizer.prepareTmuxExecution(command, args);
			
			// Build the full command
			const fullCommand = this.buildCommand(sanitized);
			
			// Set execution options
			const execOptions = {
				timeout: options.timeout || this.DEFAULT_TIMEOUT,
				maxBuffer: options.maxBuffer || this.DEFAULT_MAX_BUFFER,
				cwd: options.cwd,
				env: { ...process.env, ...options.env }
			};

			// Execute the command
			const { stdout, stderr } = await execAsync(fullCommand, execOptions);

			return {
				success: true,
				stdout: stdout || '',
				stderr: stderr || '',
				command: fullCommand,
				timestamp: startTime
			};

		} catch (error: any) {
			return {
				success: false,
				stdout: error.stdout || '',
				stderr: error.stderr || error.message || 'Unknown error',
				command: `${this.TMUX_BINARY} ${command} ${args.join(' ')}`,
				timestamp: startTime
			};
		}
	}

	/**
	 * Send keys to a tmux window securely
	 */
	public static async sendKeysSecurely(
		sessionName: string,
		windowIndex: number,
		keys: string,
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		// Validate session name and window index
		const target = CommandSanitizer.createSafeTmuxTarget(sessionName, windowIndex);
		
		// Sanitize keys input
		const sanitizedKeys = this.sanitizeKeys(keys);
		
		return this.executeSecurely('send-keys', ['-t', target, sanitizedKeys], options);
	}

	/**
	 * Send a command to a tmux window securely
	 */
	public static async sendCommandSecurely(
		sessionName: string,
		windowIndex: number,
		command: string,
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		// Validate session name and window index
		const target = CommandSanitizer.createSafeTmuxTarget(sessionName, windowIndex);
		
		// Sanitize command input
		const sanitizedCommand = this.sanitizeKeys(command);
		
		return this.executeSecurely('send-keys', ['-t', target, sanitizedCommand, 'Enter'], options);
	}

	/**
	 * Create a new tmux session securely
	 */
	public static async createSessionSecurely(
		sessionName: string,
		initialDirectory?: string,
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		const sessionValidation = CommandSanitizer.validateSessionName(sessionName);
		if (!sessionValidation.isValid) {
			throw new Error(`Invalid session name: ${sessionValidation.errors.join(', ')}`);
		}

		const args = ['-d', '-s', sessionName];
		
		if (initialDirectory) {
			const pathValidation = CommandSanitizer.sanitizeFilePath(initialDirectory);
			if (!pathValidation.isValid) {
				throw new Error(`Invalid directory path: ${pathValidation.errors.join(', ')}`);
			}
			args.push('-c', pathValidation.path);
		}

		return this.executeSecurely('new-session', args, options);
	}

	/**
	 * Create a new tmux window securely
	 */
	public static async createWindowSecurely(
		sessionName: string,
		windowName: string,
		initialDirectory?: string,
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		const sessionValidation = CommandSanitizer.validateSessionName(sessionName);
		if (!sessionValidation.isValid) {
			throw new Error(`Invalid session name: ${sessionValidation.errors.join(', ')}`);
		}

		const windowValidation = CommandSanitizer.validateProjectName(windowName);
		if (!windowValidation.isValid) {
			throw new Error(`Invalid window name: ${windowValidation.errors.join(', ')}`);
		}

		const args = ['-t', sessionName, '-n', windowName];
		
		if (initialDirectory) {
			const pathValidation = CommandSanitizer.sanitizeFilePath(initialDirectory);
			if (!pathValidation.isValid) {
				throw new Error(`Invalid directory path: ${pathValidation.errors.join(', ')}`);
			}
			args.push('-c', pathValidation.path);
		}

		return this.executeSecurely('new-window', args, options);
	}

	/**
	 * Capture pane content securely
	 */
	public static async capturePaneSecurely(
		sessionName: string,
		windowIndex: number,
		startLine?: number,
		endLine?: number,
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		const target = CommandSanitizer.createSafeTmuxTarget(sessionName, windowIndex);
		const args = ['-t', target, '-p'];

		if (startLine !== undefined && endLine !== undefined) {
			// Validate line numbers
			if (!Number.isInteger(startLine) || !Number.isInteger(endLine) || 
				startLine < 0 || endLine < 0 || startLine > endLine) {
				throw new Error('Invalid line range: start and end must be non-negative integers with start <= end');
			}
			args.push('-S', startLine.toString(), '-E', endLine.toString());
		}

		return this.executeSecurely('capture-pane', args, options);
	}

	/**
	 * List sessions securely
	 */
	public static async listSessionsSecurely(options: TmuxExecutionOptions = {}): Promise<TmuxExecutionResult> {
		return this.executeSecurely('list-sessions', ['-F', '#{session_name}:#{session_windows}:#{session_created}'], options);
	}

	/**
	 * List windows for a session securely
	 */
	public static async listWindowsSecurely(
		sessionName: string,
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		const sessionValidation = CommandSanitizer.validateSessionName(sessionName);
		if (!sessionValidation.isValid) {
			throw new Error(`Invalid session name: ${sessionValidation.errors.join(', ')}`);
		}

		return this.executeSecurely('list-windows', ['-t', sessionName, '-F', '#{window_index}:#{window_name}:#{window_active}'], options);
	}

	/**
	 * Kill a tmux session securely
	 */
	public static async killSessionSecurely(
		sessionName: string,
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		const sessionValidation = CommandSanitizer.validateSessionName(sessionName);
		if (!sessionValidation.isValid) {
			throw new Error(`Invalid session name: ${sessionValidation.errors.join(', ')}`);
		}

		return this.executeSecurely('kill-session', ['-t', sessionName], options);
	}

	/**
	 * Check if a session exists securely
	 */
	public static async hasSessionSecurely(
		sessionName: string,
		options: TmuxExecutionOptions = {}
	): Promise<boolean> {
		const sessionValidation = CommandSanitizer.validateSessionName(sessionName);
		if (!sessionValidation.isValid) {
			throw new Error(`Invalid session name: ${sessionValidation.errors.join(', ')}`);
		}

		const result = await this.executeSecurely('has-session', ['-t', sessionName], options);
		return result.success;
	}

	/**
	 * Kill a tmux window securely
	 */
	public static async killWindowSecurely(
		sessionName: string,
		windowIndex: number,
		options: TmuxExecutionOptions = {}
	): Promise<TmuxExecutionResult> {
		const target = CommandSanitizer.createSafeTmuxTarget(sessionName, windowIndex);
		return this.executeSecurely('kill-window', ['-t', target], options);
	}

	/**
	 * Build the full tmux command string
	 */
	private static buildCommand(sanitized: SanitizedCommand): string {
		const escapedArgs = sanitized.args.map(arg => this.escapeShellArgument(arg));
		return `${this.TMUX_BINARY} ${sanitized.command} ${escapedArgs.join(' ')}`;
	}

	/**
	 * Sanitize keys input to prevent injection
	 */
	private static sanitizeKeys(keys: string): string {
		if (!keys || typeof keys !== 'string') {
			throw new Error('Keys must be a non-empty string');
		}

		// Remove or escape dangerous characters
		let sanitized = keys
			.replace(/[;&|`$(){}]/g, '') // Remove shell metacharacters
			.replace(/\\/g, '\\\\')      // Escape backslashes
			.trim();

		if (sanitized.length === 0) {
			throw new Error('Keys cannot be empty after sanitization');
		}

		if (sanitized.length > 10000) {
			throw new Error('Keys exceed maximum length of 10000 characters');
		}

		return sanitized;
	}

	/**
	 * Escape shell argument for safe execution
	 */
	private static escapeShellArgument(arg: string): string {
		// Use single quotes to prevent shell interpretation
		return `'${arg.replace(/'/g, "'\"'\"'")}'`;
	}

	/**
	 * Validate tmux is available on the system
	 */
	public static async validateTmuxAvailable(): Promise<boolean> {
		try {
			const result = await execAsync(`${this.TMUX_BINARY} -V`, { timeout: 5000 });
			return result.stdout.includes('tmux');
		} catch (error) {
			return false;
		}
	}
}