/**
 * Command Sanitization Utility
 * Provides secure command execution with input validation and sanitization
 */

export interface SanitizedCommand {
	command: string;
	args: string[];
	isValid: boolean;
	errors: string[];
}

export class CommandSanitizer {
	// Allowed tmux commands - whitelist approach
	private static readonly ALLOWED_TMUX_COMMANDS = new Set([
		'new-session',
		'new-window', 
		'send-keys',
		'capture-pane',
		'list-sessions',
		'list-windows',
		'kill-session',
		'kill-window',
		'has-session',
		'display-message'
	]);

	// Dangerous patterns that should never appear in commands
	private static readonly DANGEROUS_PATTERNS = [
		/[;&|`$(){}]/,  // Shell metacharacters
		/\.\./,         // Directory traversal
		/[<>]/,         // Redirections
		/\s*rm\s/i,     // Remove commands
		/\s*sudo\s/i,   // Privilege escalation
		/\s*chmod\s/i,  // Permission changes
		/\s*wget\s/i,   // Downloads
		/\s*curl\s/i,   // Downloads
		/\s*ssh\s/i,    // Remote access
		/\s*nc\s/i,     // Network connections
		/\\\\/,         // Escape sequences
	];

	// Safe characters for project names and session identifiers
	private static readonly SAFE_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_-]+$/;

	/**
	 * Sanitize a tmux command and its arguments
	 */
	public static sanitizeTmuxCommand(command: string, args: string[]): SanitizedCommand {
		const result: SanitizedCommand = {
			command,
			args: [],
			isValid: true,
			errors: []
		};

		// Validate command is in whitelist
		if (!this.ALLOWED_TMUX_COMMANDS.has(command)) {
			result.isValid = false;
			result.errors.push(`Command '${command}' is not in the allowed list`);
			return result;
		}

		// Sanitize each argument
		for (const arg of args) {
			const sanitizedArg = this.sanitizeArgument(arg);
			if (sanitizedArg.isValid) {
				result.args.push(sanitizedArg.value);
			} else {
				result.isValid = false;
				result.errors.push(...sanitizedArg.errors);
			}
		}

		return result;
	}

	/**
	 * Sanitize a single command argument
	 */
	private static sanitizeArgument(arg: string): { value: string; isValid: boolean; errors: string[] } {
		const result = {
			value: arg,
			isValid: true,
			errors: [] as string[]
		};

		// Check for null/undefined
		if (arg == null) {
			result.isValid = false;
			result.errors.push('Argument cannot be null or undefined');
			return result;
		}

		// Convert to string and trim
		const stringArg = String(arg).trim();
		
		// Check length
		if (stringArg.length === 0) {
			result.isValid = false;
			result.errors.push('Argument cannot be empty');
			return result;
		}

		if (stringArg.length > 1000) {
			result.isValid = false;
			result.errors.push('Argument exceeds maximum length of 1000 characters');
			return result;
		}

		// Check for dangerous patterns
		for (const pattern of this.DANGEROUS_PATTERNS) {
			if (pattern.test(stringArg)) {
				result.isValid = false;
				result.errors.push(`Argument contains dangerous pattern: ${pattern.source}`);
			}
		}

		result.value = stringArg;
		return result;
	}

	/**
	 * Validate project name specifically
	 */
	public static validateProjectName(projectName: string): { isValid: boolean; errors: string[] } {
		const result = { isValid: true, errors: [] as string[] };

		if (!projectName || typeof projectName !== 'string') {
			result.isValid = false;
			result.errors.push('Project name must be a non-empty string');
			return result;
		}

		const trimmed = projectName.trim();

		if (trimmed.length === 0) {
			result.isValid = false;
			result.errors.push('Project name cannot be empty');
		}

		if (trimmed.length > 50) {
			result.isValid = false;
			result.errors.push('Project name cannot exceed 50 characters');
		}

		if (!this.SAFE_IDENTIFIER_PATTERN.test(trimmed)) {
			result.isValid = false;
			result.errors.push('Project name can only contain letters, numbers, underscores, and hyphens');
		}

		// Check for reserved names
		const reservedNames = ['system', 'admin', 'root', 'tmp', 'var', 'etc', 'bin'];
		if (reservedNames.includes(trimmed.toLowerCase())) {
			result.isValid = false;
			result.errors.push('Project name cannot be a reserved system name');
		}

		return result;
	}

	/**
	 * Validate session name
	 */
	public static validateSessionName(sessionName: string): { isValid: boolean; errors: string[] } {
		// Session names have similar requirements to project names
		return this.validateProjectName(sessionName);
	}

	/**
	 * Sanitize file path to prevent directory traversal
	 */
	public static sanitizeFilePath(filePath: string, allowedBasePath?: string): { path: string; isValid: boolean; errors: string[] } {
		const result = {
			path: filePath,
			isValid: true,
			errors: [] as string[]
		};

		if (!filePath || typeof filePath !== 'string') {
			result.isValid = false;
			result.errors.push('File path must be a non-empty string');
			return result;
		}

		// Normalize the path
		const path = require('path');
		let normalizedPath: string;
		
		try {
			normalizedPath = path.normalize(filePath);
		} catch (error) {
			result.isValid = false;
			result.errors.push('Invalid file path format');
			return result;
		}

		// Check for directory traversal attempts
		if (normalizedPath.includes('..') || normalizedPath.includes('/.')) {
			result.isValid = false;
			result.errors.push('File path contains directory traversal attempts');
		}

		// Check if path is within allowed base path
		if (allowedBasePath) {
			const resolvedPath = path.resolve(normalizedPath);
			const resolvedBase = path.resolve(allowedBasePath);
			
			if (!resolvedPath.startsWith(resolvedBase)) {
				result.isValid = false;
				result.errors.push('File path is outside of allowed directory');
			}
		}

		// Check for dangerous paths
		const dangerousPaths = ['/etc', '/var', '/bin', '/sbin', '/usr/bin', '/usr/sbin', '/root'];
		for (const dangerousPath of dangerousPaths) {
			if (normalizedPath.startsWith(dangerousPath)) {
				result.isValid = false;
				result.errors.push('File path accesses system directory');
			}
		}

		result.path = normalizedPath;
		return result;
	}

	/**
	 * Create a safe tmux target string
	 */
	public static createSafeTmuxTarget(sessionName: string, windowIndex: number | string): string {
		const sessionValidation = this.validateSessionName(sessionName);
		if (!sessionValidation.isValid) {
			throw new Error(`Invalid session name: ${sessionValidation.errors.join(', ')}`);
		}

		// Validate window index
		const windowIndexNum = typeof windowIndex === 'string' ? parseInt(windowIndex, 10) : windowIndex;
		if (isNaN(windowIndexNum) || windowIndexNum < 0 || windowIndexNum > 999) {
			throw new Error('Window index must be a number between 0 and 999');
		}

		return `${sessionName}:${windowIndexNum}`;
	}

	/**
	 * Validate and sanitize tmux command execution
	 */
	public static prepareTmuxExecution(command: string, args: string[]): SanitizedCommand {
		const sanitized = this.sanitizeTmuxCommand(command, args);
		
		if (!sanitized.isValid) {
			throw new Error(`Command sanitization failed: ${sanitized.errors.join(', ')}`);
		}

		return sanitized;
	}

	/**
	 * Validate project idea input
	 */
	public static validateProjectIdea(projectIdea: string): { isValid: boolean; errors: string[] } {
		const result = { isValid: true, errors: [] as string[] };

		if (!projectIdea || typeof projectIdea !== 'string') {
			result.isValid = false;
			result.errors.push('Project idea must be a non-empty string');
			return result;
		}

		const trimmed = projectIdea.trim();

		if (trimmed.length === 0) {
			result.isValid = false;
			result.errors.push('Project idea cannot be empty');
		}

		if (trimmed.length < 10) {
			result.isValid = false;
			result.errors.push('Project idea must be at least 10 characters long');
		}

		if (trimmed.length > 5000) {
			result.isValid = false;
			result.errors.push('Project idea cannot exceed 5000 characters');
		}

		// Check for dangerous patterns in project ideas
		for (const pattern of this.DANGEROUS_PATTERNS) {
			if (pattern.test(trimmed)) {
				result.isValid = false;
				result.errors.push('Project idea contains potentially dangerous content');
			}
		}

		return result;
	}

	/**
	 * Validate quality requirements
	 */
	public static validateQualityRequirements(qualityRequirements: string): { isValid: boolean; errors: string[] } {
		const result = { isValid: true, errors: [] as string[] };

		if (!qualityRequirements || typeof qualityRequirements !== 'string') {
			result.isValid = false;
			result.errors.push('Quality requirements must be a non-empty string');
			return result;
		}

		const validQualityLevels = ['lite', 'standard', 'high', 'critical'];
		if (!validQualityLevels.includes(qualityRequirements.toLowerCase())) {
			result.isValid = false;
			result.errors.push(`Quality requirements must be one of: ${validQualityLevels.join(', ')}`);
		}

		return result;
	}


	/**
	 * Validate time string format
	 */
	public static validateTimeString(timeString: string): { isValid: boolean; errors: string[] } {
		const result = { isValid: true, errors: [] as string[] };

		if (!timeString || typeof timeString !== 'string') {
			// Time string can be optional for some operations
			return result;
		}

		const trimmed = timeString.trim();
		
		// Allow common time formats: HH:MM, YYYY-MM-DD HH:MM, ISO 8601
		const timeFormats = [
			/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/, // HH:MM
			/^\d{4}-\d{2}-\d{2} ([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/, // YYYY-MM-DD HH:MM
			/^\d{4}-\d{2}-\d{2}T([0-1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.\d{3})?Z?$/ // ISO 8601
		];

		const isValidFormat = timeFormats.some(format => format.test(trimmed));
		
		if (!isValidFormat) {
			result.isValid = false;
			result.errors.push('Invalid time format. Expected HH:MM, YYYY-MM-DD HH:MM, or ISO 8601 format');
		}

		return result;
	}

	/**
	 * Validate reference string
	 */
	public static validateReference(reference: string): { isValid: boolean; errors: string[] } {
		const result = { isValid: true, errors: [] as string[] };

		if (!reference || typeof reference !== 'string') {
			// Reference can be optional
			return result;
		}

		const trimmed = reference.trim();

		if (trimmed.length > 200) {
			result.isValid = false;
			result.errors.push('Reference cannot exceed 200 characters');
		}

		// Check for dangerous patterns
		for (const pattern of this.DANGEROUS_PATTERNS) {
			if (pattern.test(trimmed)) {
				result.isValid = false;
				result.errors.push('Reference contains potentially dangerous content');
			}
		}

		return result;
	}

	/**
	 * Comprehensive input validation for project creation
	 */
	public static validateProjectInputs(inputs: {
		projectIdea: string;
		projectName: string;
		projectPath: string;
		qualityRequirements: string;
		teamSizePriority?: string;
		preferredStartTime?: string;
		reference?: string;
	}): { isValid: boolean; errors: string[] } {
		const result = { isValid: true, errors: [] as string[] };

		// Validate project idea
		const projectIdeaValidation = this.validateProjectIdea(inputs.projectIdea);
		if (!projectIdeaValidation.isValid) {
			result.isValid = false;
			result.errors.push(...projectIdeaValidation.errors.map(err => `Project Idea: ${err}`));
		}

		// Validate project name
		const projectNameValidation = this.validateProjectName(inputs.projectName);
		if (!projectNameValidation.isValid) {
			result.isValid = false;
			result.errors.push(...projectNameValidation.errors.map(err => `Project Name: ${err}`));
		}

		// Validate project path
		const projectPathValidation = this.sanitizeFilePath(inputs.projectPath);
		if (!projectPathValidation.isValid) {
			result.isValid = false;
			result.errors.push(...projectPathValidation.errors.map(err => `Project Path: ${err}`));
		}

		// Validate quality requirements
		const qualityValidation = this.validateQualityRequirements(inputs.qualityRequirements);
		if (!qualityValidation.isValid) {
			result.isValid = false;
			result.errors.push(...qualityValidation.errors.map(err => `Quality Requirements: ${err}`));
		}

		// Validate optional fields

		if (inputs.preferredStartTime) {
			const timeValidation = this.validateTimeString(inputs.preferredStartTime);
			if (!timeValidation.isValid) {
				result.isValid = false;
				result.errors.push(...timeValidation.errors.map(err => `Start Time: ${err}`));
			}
		}

		if (inputs.reference) {
			const referenceValidation = this.validateReference(inputs.reference);
			if (!referenceValidation.isValid) {
				result.isValid = false;
				result.errors.push(...referenceValidation.errors.map(err => `Reference: ${err}`));
			}
		}

		return result;
	}
}