import { execSync, spawn } from 'child_process';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { Logger } from '../utils/logger.js';
import { LudusConfig } from './interactiveSetup.js';
import { LudusSSHTunnelManager, type SSHTunnelConfig } from './sshTunnelManager.js';

export interface CommandResult {
  success: boolean;
  data?: any;
  message: string;
  rawOutput?: string;
}

export class LudusCliWrapper {
  private logger: Logger;
  private config: LudusConfig;
  private sshTunnelPid: number | null = null;
  private sshTunnelPort: number = 8081;
  private regularTunnelPid: number | null = null; // For port 8080
  private tunnelManager?: LudusSSHTunnelManager;
  private baseCwd: string;

  constructor(logger: Logger, config: LudusConfig) {
    this.logger = logger;
    this.config = config;
    this.baseCwd = path.join(os.homedir(), '.ludus-mcp');
    
    // Ensure base directory exists
    this.ensureBaseDirectory();
    
    // Log environment details for debugging
    this.logger.info('LudusCliWrapper initialized', {
      platform: process.platform,
      nodeVersion: process.version,
      workingDirectory: process.cwd(),
      baseCwd: this.baseCwd,
      connectionMethod: this.config.connectionMethod,
      sshAuthMethod: this.config.sshAuthMethod,
      pathEnv: process.env.PATH?.substring(0, 500) + '...', // First 500 chars of PATH
      userProfile: process.env.USERPROFILE || process.env.HOME,
      sshAgent: process.env.SSH_AUTH_SOCK || 'not set'
    });
    
    // Always initialize tunnel manager for admin operations (port 8081)
    // Admin commands always use SSH tunnel regardless of connection method
    this.initializeTunnelManager().catch((error: any) => {
        // Extremely aggressive error handling for debugging
        let errorInfo: any = {
          timestamp: new Date().toISOString(),
          phase: 'constructor_catch_block'
        };

        try {
          errorInfo.errorMessage = error?.message || 'no_message_property';
        } catch (e) {
          errorInfo.errorMessage = 'message_access_failed';
        }

        try {
          errorInfo.errorString = String(error);
        } catch (e) {
          errorInfo.errorString = 'string_conversion_failed';
        }

        try {
          errorInfo.errorType = error?.constructor?.name || 'unknown_type';
        } catch (e) {
          errorInfo.errorType = 'type_access_failed';
        }

        try {
          errorInfo.errorKeys = Object.keys(error || {});
        } catch (e) {
          errorInfo.errorKeys = 'keys_access_failed';
        }

        try {
          errorInfo.errorToString = error?.toString?.() || 'no_toString';
        } catch (e) {
          errorInfo.errorToString = 'toString_failed';
        }

        this.logger.error('SSH TUNNEL CONSTRUCTOR FAILURE', errorInfo);
      });
  }

  /**
   * Initialize the SSH tunnel manager using ssh2 library
   */
  private async initializeTunnelManager(): Promise<void> {
    if (!this.config.sshHost || !this.config.sshUser) {
      throw new Error('SSH configuration is incomplete - missing host or user');
    }

    if (this.config.sshAuthMethod === 'key' && !this.config.sshKeyPath) {
      throw new Error('SSH key path is required for key authentication');
    }

    if (this.config.sshAuthMethod === 'password' && !this.config.sshPassword) {
      throw new Error('SSH password is required for password authentication');
    }

    const tunnelConfig: SSHTunnelConfig = {
      host: this.config.sshHost,
      port: 22, // Default SSH port
      username: this.config.sshUser,
      authMethod: this.config.sshAuthMethod,
      regularPort: 8080,
      primaryPort: 8081,
      privateKeyPath: this.config.sshAuthMethod === 'key' ? this.config.sshKeyPath! : undefined,
      privateKeyPassphrase: this.config.sshAuthMethod === 'key' ? this.config.sshKeyPassphrase : undefined,
      password: this.config.sshAuthMethod === 'password' ? this.config.sshPassword! : undefined
    };

    this.tunnelManager = new LudusSSHTunnelManager(tunnelConfig, this.logger);
    
    try {
      await this.tunnelManager.connect();
      this.logger.info('SSH tunnel manager initialized successfully');
    } catch (error) {
      this.logger.error('Failed to initialize SSH tunnel manager', { error });
      throw error;
    }
  }

  /**
   * Ensure SSH tunnels are healthy before executing commands
   */
  private async ensureTunnelsHealthy(): Promise<void> {
    // Initialize tunnel manager if not already done
    if (!this.tunnelManager) {
      await this.initializeTunnelManager();
    }
    
    if (this.tunnelManager) {
      try {
        await this.tunnelManager.ensureTunnelsHealthy();
      } catch (error) {
        this.logger.error('Failed to ensure tunnel health', { error });
        throw new Error(`SSH tunnel health check failed: ${error instanceof Error ? error.message : String(error)}`);
      }
    } else {
      throw new Error('Unable to initialize SSH tunnel manager for fallback');
    }
  }

  /**
   * Check if a command requires admin API (SSH tunnel)
   */
  private isAdminCommand(command: string): boolean {
    // Based on Ludus CLI documentation analysis (see ludus-command-api-endpoints.txt)
    // ONLY these commands require SSH tunnel to port 8081:
    // From docs/cli.md: "To use the `add` or `rm` commands, the admin API endpoint must be used."
    
    const adminCommands = [
      'users add',   // Create new users - requires admin endpoint
      'users rm'     // Remove users - requires admin endpoint
    ];

    // ALL other commands use regular endpoint (port 8080) including:
    // - users apikey, users list, users wireguard, users creds
    // - ALL range operations (even with --user flag)
    // - ALL template operations  
    // - ALL other operations
    return adminCommands.some(adminCmd => command.includes(adminCmd));
  }

  /**
   * Set up environment variables for regular API calls via WireGuard
   */
  private setupWireGuardEnvironment(): void {
    process.env.LUDUS_API_KEY = this.config.apiKey;
    process.env.LUDUS_URL = this.config.ludusUrl || 'https://198.51.100.1:8080';
    process.env.LUDUS_VERIFY = this.config.verifySSL ? 'true' : 'false';
    process.env.LUDUS_JSON = 'true'; // Always use JSON for MCP processing
  }

  /**
   * Set up environment variables for regular API calls via SSH tunnel (port 8080)
   */
  private setupSSHTunnelRegularEnvironment(): void {
    process.env.LUDUS_API_KEY = this.config.apiKey;
    process.env.LUDUS_URL = 'https://127.0.0.1:8080';
    process.env.LUDUS_VERIFY = 'false'; // Local tunnel doesn't need SSL verification
    process.env.LUDUS_JSON = 'true';
  }

  /**
   * Set up environment variables for admin API calls via SSH tunnel (port 8081)
   */
  private setupSSHTunnelAdminEnvironment(): void {
    process.env.LUDUS_API_KEY = this.config.apiKey;
    process.env.LUDUS_URL = `https://127.0.0.1:${this.sshTunnelPort}`;
    process.env.LUDUS_VERIFY = 'false'; // Local tunnel doesn't need SSL verification
    process.env.LUDUS_JSON = 'true';
  }

  /**
   * Create SSH tunnel for admin operations using tunnel manager
   */
  private async createSSHTunnel(): Promise<boolean> {
    try {
      this.logger.info('Creating SSH tunnel for admin operations using tunnel manager');
      
      // Initialize tunnel manager if not already done
      if (!this.tunnelManager) {
        await this.initializeTunnelManager();
      }

      // Ensure tunnels are healthy
      if (this.tunnelManager) {
        await this.tunnelManager.ensureTunnelsHealthy();
        this.logger.info('SSH tunnel for admin operations established successfully');
        return true;
      } else {
        this.logger.error('Failed to initialize tunnel manager');
        return false;
      }
    } catch (error: any) {
      this.logger.error('Failed to create SSH tunnel for admin operations', { error });
      return false;
    }
  }

  /**
   * Create SSH tunnel for regular operations using tunnel manager
   */
  private async createRegularOperationsTunnel(): Promise<boolean> {
    try {
      this.logger.info('Creating SSH tunnel for regular operations using tunnel manager');
      
      // Initialize tunnel manager if not already done
      if (!this.tunnelManager) {
        await this.initializeTunnelManager();
      }

      // Ensure tunnels are healthy
      if (this.tunnelManager) {
        await this.tunnelManager.ensureTunnelsHealthy();
        this.logger.info('SSH tunnel for regular operations established successfully');
        return true;
      } else {
        this.logger.error('Failed to initialize tunnel manager');
        return false;
      }
    } catch (error: any) {
      this.logger.error('Failed to create SSH tunnel for regular operations', { error });
      return false;
    }
  }

  /**
   * Execute command with smart routing
   */
  async executeCommand(command: string, args: string[] = [], workingDirectory?: string): Promise<CommandResult> {
    const originalCwd = process.cwd();
    let usingSSHFallback = false;
    
    // Build command string for admin check and logging (not execution)
    const fullCommand = `${command} ${args.join(' ')}`.trim();
    const isAdmin = this.isAdminCommand(fullCommand);

    try {
      // Use specified working directory or default to ~/.ludus-mcp/
      const targetCwd = workingDirectory || this.baseCwd;
      process.chdir(targetCwd);
      
      const actualRoute = isAdmin ? 'SSH tunnel' : 
        (this.config.connectionMethod === 'ssh-tunnel' ? 'SSH tunnel' : 'WireGuard VPN');
      
      this.logger.info('Executing Ludus command', { 
        command: fullCommand, 
        isAdmin,
        route: actualRoute,
        workingDirectory: targetCwd
      });

      // Ensure connections are healthy before executing commands
      
      if (isAdmin) {
        // Admin commands always use SSH tunnel
        await this.ensureTunnelsHealthy();
      } else {
        // Regular commands use connection method specified in config
        if (this.config.connectionMethod === 'ssh-tunnel') {
          await this.ensureTunnelsHealthy();
        } else {
          // Check WireGuard health - if unhealthy, try SSH fallback
          const wgHealth = await this.checkWireGuardHealth();
          if (!wgHealth.healthy) {
            this.logger.warn('WireGuard not healthy, attempting SSH fallback', { 
              reason: wgHealth.message 
            });
            
            try {
              await this.ensureTunnelsHealthy();
              usingSSHFallback = true;
              this.logger.info('SSH fallback successful - using SSH tunnel for this command');
            } catch (sshError) {
              throw new Error(`WireGuard unavailable: ${wgHealth.message}. SSH fallback also failed: ${sshError instanceof Error ? sshError.message : String(sshError)}`);
            }
          }
        }
      }

      // Set up appropriate environment and connectivity
      if (isAdmin) {
        // Admin command - use SSH tunnel (port 8081)
        this.setupSSHTunnelAdminEnvironment();
      } else {
        // Regular command - use appropriate connection method
        if (this.config.connectionMethod === 'ssh-tunnel' || usingSSHFallback) {
          this.setupSSHTunnelRegularEnvironment();
        } else {
          this.setupWireGuardEnvironment();
        }
      }

      // Execute the command securely using argument array
      const ludusArgs = [command, ...args];
      const ludusCommand = `ludus ${fullCommand}`; // For logging only
      let output: string = '';
      
      try {
        // Use spawn with argument array to prevent command injection
        const ludusProcess = spawn('ludus', ludusArgs, {
          stdio: ['pipe', 'pipe', 'pipe'],
          shell: false // Prevent shell interpretation
        });

        let stdout = '';
        let stderr = '';

        ludusProcess.stdout.on('data', (data: Buffer) => {
          stdout += data.toString();
        });

        ludusProcess.stderr.on('data', (data: Buffer) => {
          stderr += data.toString();
        });

        await new Promise<void>((resolve, reject) => {
          ludusProcess.on('close', (code: number) => {
            // Ludus CLI outputs to stderr even on success
            output = stderr || stdout || '';
            if (output && stderr) {
              this.logger.debug('Using stderr output from Ludus CLI', { 
                stderr: stderr.substring(0, 200) + '...'
              });
            }
            resolve();
          });

          ludusProcess.on('error', (error: Error) => {
            reject(error);
          });

          // Set timeout
          setTimeout(() => {
            ludusProcess.kill();
            reject(new Error('Command timeout'));
          }, 30000);
        });
      } catch (error: any) {
        // Fallback to execSync approach - use spawn instead to maintain security
        try {
          // Note: execSync with string commands is still potentially vulnerable
          // This is kept as fallback only - primary spawn approach above is secure
          output = execSync(ludusCommand, {
            encoding: 'utf-8',
            timeout: 30000,
            maxBuffer: 1024 * 1024 * 10 // 10MB buffer
          }) as string;
        } catch (execError: any) {
          if (execError.stderr && execError.stderr.toString().trim().length > 0) {
            output = execError.stderr.toString();
            this.logger.debug('Using stderr output from Ludus CLI (fallback)', { 
              stderr: execError.stderr.toString().substring(0, 200) + '...'
            });
          } else if (execError.stdout && execError.stdout.toString().trim().length > 0) {
            output = execError.stdout.toString();
          } else {
            throw error;
          }
        }
      }

      // Parse JSON output if possible
      let parsedData: any;
      
      if (output.trim().startsWith('{') || output.trim().startsWith('[')) {
        try {
          parsedData = JSON.parse(output);
        } catch (parseError) {
          // If parsing fails, use raw output
          parsedData = output;
        }
      } else {
        // Not JSON, use raw output
        parsedData = output;
      }

      // Tunnel cleanup is handled automatically by tunnel manager
      // Clear environment variables
      delete process.env.LUDUS_API_KEY;
      delete process.env.LUDUS_URL;
      delete process.env.LUDUS_VERIFY;
      delete process.env.LUDUS_JSON;

      return {
        success: true,
        data: parsedData,
        message: output,
        rawOutput: output
      };
    } catch (error: any) {
      this.logger.error('Command execution failed', {
        command: fullCommand,
        error: error.message,
        workingDirectory,
        stdout: error.stdout?.toString(),
        stderr: error.stderr?.toString()
      });

      // Tunnel cleanup is handled automatically by tunnel manager
      // Clear environment variables
      delete process.env.LUDUS_API_KEY;
      delete process.env.LUDUS_URL;
      delete process.env.LUDUS_VERIFY;
      delete process.env.LUDUS_JSON;

      return {
        success: false,
        message: `Command failed: ${error.message}`,
        rawOutput: error.stdout?.toString() || error.stderr?.toString()
      };
    } finally {
      // Always restore original working directory
      process.chdir(originalCwd);
    }
  }

  /**
   * Execute arbitrary Ludus CLI command
   */
  async executeArbitraryCommand(command: string, args: string[] = []): Promise<CommandResult> {
    return this.executeCommand(command, args);
  }

  /**
   * List user ranges (current user or specific user for admin)
   */
  async listUserRanges(user?: string): Promise<CommandResult> {
    const args = ['list'];
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('range', args);
  }

  /**
   * Deploy range with full CLI options support
   */
  async deployRange(options: {
    user?: string;
    configPath?: string;
    force?: boolean;
    tags?: string;
    limit?: string;
    onlyRoles?: string;
    verboseAnsible?: boolean;
  } = {}): Promise<CommandResult> {
    try {
      const { user, configPath, force, tags, limit, onlyRoles, verboseAnsible } = options;

      // First set config if provided
      if (configPath) {
        const configArgs = ['config', 'set', '-f', configPath];
        if (user) {
          configArgs.push('--user', user);
        }
        if (force) {
          configArgs.push('--force');
        }
        
        const configResult = await this.executeCommand('range', configArgs);
        if (!configResult.success) {
          return configResult;
        }
      }

      // Then deploy with all options
      const deployArgs = ['deploy'];
      
      if (user) {
        deployArgs.push('--user', user);
      }
      if (force) {
        deployArgs.push('--force');
      }
      if (tags) {
        deployArgs.push('--tags', tags);
      }
      if (limit) {
        deployArgs.push('--limit', limit);
      }
      if (onlyRoles) {
        deployArgs.push('--only-roles', onlyRoles);
      }
      if (verboseAnsible) {
        deployArgs.push('--verbose-ansible');
      }
      
      return this.executeCommand('range', deployArgs);
    } catch (error: any) {
      return {
        success: false,
        message: `Range deployment failed: ${error.message}`
      };
    }
  }

  /**
   * Get available deployment tags
   */
  async getTags(user?: string): Promise<CommandResult> {
    const args = ['gettags'];
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('range', args);
  }

  /**
   * Abort range deployment
   */
  async abortRange(user?: string): Promise<CommandResult> {
    const args = ['abort'];
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('range', args);
  }

  /**
   * Get range status (current user or specific user for admin)
   */
  async getRangeStatus(user?: string): Promise<CommandResult> {
    const args = ['list']; // 'list' is alias for 'status' in Ludus CLI
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('range', args);
  }

  /**
   * Destroy range - permanently remove all VMs and free resources
   */
  async destroyRange(user?: string, noPrompt: boolean = false): Promise<CommandResult> {
    const args = ['rm'];
    if (noPrompt) {
      args.push('--no-prompt');
    }
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('range', args);
  }

  /**
   * Get RDP connection files for Windows VMs
   */
  async getRangeRdpInfo(user?: string): Promise<CommandResult> {
    // Create user-specific directory for file downloads
    const userDir = this.ensureUserDirectory(user);
    const rdpPath = path.join(userDir, 'rdp.zip');
    
    const args = ['rdp', '--output', rdpPath];
    if (user) {
      args.push('--user', user);
    }
    
    this.logger.info('Downloading RDP configuration to specific path', { 
      path: rdpPath,
      user: user || 'current-user'
    });
    
    // Execute command with explicit output path
    const result = await this.executeCommand('range', args);
    
    if (result.success) {
      return {
        success: true,
        data: result.data,
        message: `RDP configuration saved to ${rdpPath}`,
        rawOutput: result.rawOutput || ''
      };
    }
    
    return result;
  }

  /**
   * Get WireGuard configuration for user
   */
  async getUserWireguardConfig(user?: string): Promise<CommandResult> {
    const args = ['wireguard'];
    if (user) {
      args.push('--user', user);
    }
    
    // Create user-specific directory for file downloads
    const userDir = this.ensureUserDirectory(user);
    
    // Execute command to get WireGuard config
    const result = await this.executeCommand('user', args);
    
    if (result.success && result.data) {
      try {
        // Write WireGuard config to file
        const configPath = path.join(userDir, 'wireguard.conf');
        fs.writeFileSync(configPath, result.data, 'utf8');
        
        this.logger.info('WireGuard configuration written to file', { 
          path: configPath,
          user: user || 'current-user'
        });
        
        return {
          success: true,
          data: result.data,
          message: `WireGuard configuration saved to ${configPath}`,
          rawOutput: result.rawOutput || ''
        };
      } catch (error: any) {
        this.logger.error('Failed to write WireGuard config to file', { 
          error: error.message,
          userDir 
        });
        
        // Return original result if file writing fails
        return result;
      }
    }
    
    return result;
  }

  /**
   * Get /etc/hosts formatted file for range
   */
  async getRangeEtcHosts(user?: string): Promise<CommandResult> {
    const args = ['etc-hosts'];
    if (user) {
      args.push('--user', user);
    }
    
    // Create user-specific directory for file downloads
    const userDir = this.ensureUserDirectory(user);
    
    // Execute command to get hosts entries
    const result = await this.executeCommand('range', args);
    
    if (result.success && result.data) {
      try {
        // Write hosts entries to file
        const hostsPath = path.join(userDir, 'hosts');
        fs.writeFileSync(hostsPath, result.data, 'utf8');
        
        this.logger.info('Hosts entries written to file', { 
          path: hostsPath,
          user: user || 'current-user'
        });
        
        return {
          success: true,
          data: result.data,
          message: `Hosts entries saved to ${hostsPath}`,
          rawOutput: result.rawOutput || ''
        };
      } catch (error: any) {
        this.logger.error('Failed to write hosts entries to file', { 
          error: error.message,
          userDir 
        });
        
        // Return original result if file writing fails
        return result;
      }
    }
    
    return result;
  }

  /**
   * Power on VMs in range
   */
  async powerOnRange(user?: string, vmNames?: string): Promise<CommandResult> {
    const args = ['on']; // Ludus power commands don't support --force flag
    if (vmNames) {
      args.push('--name', vmNames);
    }
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('power', args);
  }

  /**
   * Power off VMs in range
   */
  async powerOffRange(user?: string, vmNames?: string): Promise<CommandResult> {
    const args = ['off']; // Ludus power commands don't support --force flag
    if (vmNames) {
      args.push('--name', vmNames);
    }
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('power', args);
  }

  /**
   * Get range configuration
   */
  async getRangeConfig(user?: string): Promise<CommandResult> {
    const args = ['config', 'get'];
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('range', args);
  }

  /**
   * Set range configuration from file
   */
  async setRangeConfig(configPath: string, user?: string, force: boolean = false): Promise<CommandResult> {
    const args = ['config', 'set', '-f', configPath];
    if (user) {
      args.push('--user', user);
    }
    if (force) {
      args.push('--force');
    }
    return this.executeCommand('range', args);
  }

  /**
   * Get range deployment logs
   */
  async getRangeLogs(user?: string, follow: boolean = false): Promise<CommandResult> {
    const args = ['logs'];
    if (follow) {
      args.push('-f');
    }
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('range', args);
  }

  /**
   * List available templates
   */
  async listTemplates(): Promise<CommandResult> {
    return this.executeCommand('templates', ['list']);
  }

  /**
   * Get user information for a specific user (or current user if none specified)
   */
  async getUserInfo(user?: string): Promise<CommandResult> {
    const args = ['list'];
    if (user) {
      args.push('--user', user);
    }
    return this.executeCommand('users', args);
  }

  /**
   * List all users in the system
   */
  async listAllUsers(): Promise<CommandResult> {
    return this.executeCommand('users', ['list', 'all']);
  }

  /**
   * Add a new user (admin operation)
   */
  async addUser(name: string, userId: string, isAdmin: boolean = false): Promise<CommandResult> {
    const args = ['add', '--name', name, '--userid', userId];
    if (isAdmin) {
      args.push('--admin');
    }
    return this.executeCommand('users', args);
  }

  /**
   * Remove a user (admin operation)
   */
  async removeUser(userId: string): Promise<CommandResult> {
    return this.executeCommand('users', ['rm', '--user', userId]);
  }

  /**
   * Get API key for user (admin operation)
   */
  async getUserApiKey(userId: string): Promise<CommandResult> {
    return this.executeCommand('users', ['apikey', '--user', userId]);
  }

  /**
   * Test connectivity for both regular and admin operations
   */
  async testConnectivity(): Promise<{ rangeOps: boolean; adminOps: boolean }> {
    const result = { rangeOps: false, adminOps: false };

    // Test regular API connectivity based on connection method
    if (this.config.connectionMethod === 'wireguard') {
      // Test WireGuard connectivity for regular operations
      try {
        this.logger.info('Testing WireGuard connectivity for regular operations');
        
        // Use the new WireGuard health check (no auto-connect)
        const wgHealth = await this.checkWireGuardHealth();
        
        result.rangeOps = wgHealth.healthy;
        this.logger.info('WireGuard connectivity test result (regular operations)', { 
          success: result.rangeOps, 
          message: wgHealth.message 
        });
      } catch (error: any) {
        this.logger.error('WireGuard connectivity test failed (regular operations)', { 
          error: error.message || error,
          stack: error.stack 
        });
      }
    } else {
      // Test SSH tunnel connectivity for regular operations (port 8080)
      try {
        this.logger.info('Testing SSH tunnel connectivity for regular operations (port 8080)');
        
        // Use tunnel manager to ensure tunnels are healthy
        if (this.tunnelManager) {
          await this.tunnelManager.ensureTunnelsHealthy();
        } else {
          throw new Error('No tunnel manager available for regular operations');
        }
        
        this.setupSSHTunnelRegularEnvironment();
        
        let output: string = '';
        try {
          // Use spawn to capture both stdout and stderr properly
          const ludusProcess = spawn('ludus', ['version'], {
            stdio: ['pipe', 'pipe', 'pipe']
          });

          let stdout = '';
          let stderr = '';

          ludusProcess.stdout.on('data', (data: Buffer) => {
            stdout += data.toString();
          });

          ludusProcess.stderr.on('data', (data: Buffer) => {
            stderr += data.toString();
          });

          await new Promise<void>((resolve, reject) => {
            ludusProcess.on('close', (code: number) => {
              // Ludus CLI outputs to stderr even on success
              output = stderr || stdout || '';
              resolve();
            });

            ludusProcess.on('error', (error: Error) => {
              reject(error);
            });

            // Set timeout
            setTimeout(() => {
              ludusProcess.kill();
              reject(new Error('Command timeout'));
            }, 5000);
          });
        } catch (error: any) {
          // Fallback to execSync approach
          try {
            output = execSync('ludus version', { encoding: 'utf-8', timeout: 5000 }) as string;
          } catch (execError: any) {
            if (execError.stderr) {
              output = execError.stderr.toString();
            } else if (execError.stdout) {
              output = execError.stdout.toString();
            } else {
              throw error;
            }
          }
        }
        
        result.rangeOps = output.includes('Ludus Server') || output.includes('Server version') || output.includes('server:');
        this.logger.info('SSH tunnel connectivity test result (regular operations)', { success: result.rangeOps, output: output.trim() });
      } catch (error: any) {
        this.logger.error('SSH tunnel connectivity test failed (regular operations)', { 
          error: error.message || error,
          stack: error.stack 
        });
      }
    }

    // ALWAYS test SSH tunnel for admin operations (port 8081) regardless of connection method
    try {
      this.logger.info('Testing SSH tunnel connectivity for admin operations (port 8081)');
      if (this.tunnelManager) {
        // Use tunnel manager to check health
        const health = await this.tunnelManager.checkTunnelHealth();
        result.adminOps = health.primaryHealthy;
        this.logger.info('SSH tunnel connectivity test result (admin operations)', { success: result.adminOps });
      } else {
        this.logger.debug('No tunnel manager available for admin operations test');
      }
    } catch (error) {
      this.logger.debug('SSH tunnel connectivity test failed (admin operations will be limited)', { error });
    }

    // Clean up environment
    delete process.env.LUDUS_API_KEY;
    delete process.env.LUDUS_URL;
    delete process.env.LUDUS_VERIFY;
    delete process.env.LUDUS_JSON;

    return result;
  }

  /**
   * Check if WireGuard is currently connected
   */
  private checkWireGuardConnectivity(): boolean {
    try {
      const result = execSync('wg show', { encoding: 'utf8', stdio: 'pipe' });
      return result.trim().length > 0;
    } catch {
      return false;
    }
  }

  /**
   * Check if WireGuard is available on the system
   */
  private checkWireGuardAvailable(): boolean {
    try {
      execSync('wg --version', { stdio: 'ignore' });
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Check WireGuard health by pinging the WireGuard interface
   * Returns health status and suggestions for the user
   */
  private async checkWireGuardHealth(): Promise<{ healthy: boolean; message: string }> {
    try {
      this.logger.debug('Testing WireGuard connectivity via ping');
      
      // Ping the WireGuard interface (2 pings for reliability)
      const isWindows = process.platform === 'win32';
      const pingCommand = isWindows ? 'ping' : 'ping';
      const pingArgs = isWindows ? ['-n', '2', '198.51.100.1'] : ['-c', '2', '198.51.100.1'];
      
      const result = execSync(`${pingCommand} ${pingArgs.join(' ')}`, {
        encoding: 'utf-8',
        timeout: 5000,
        stdio: 'pipe'
      }) as string;
      
      // Check if ping was successful (look for success indicators)
      const isHealthy = isWindows 
        ? result.includes('Reply from') || result.includes('bytes=') 
        : result.includes('bytes from') || !result.includes('100% packet loss');
      
      if (!isHealthy) {
        return {
          healthy: false,
          message: 'WireGuard interface (198.51.100.1) not reachable via ping'
        };
      }
      
      this.logger.debug('WireGuard interface ping successful');
      return {
        healthy: true,
        message: 'WireGuard interface is reachable'
      };
      
    } catch (error: any) {
      this.logger.debug('WireGuard ping test failed', { error: error.message });
      return {
        healthy: false,
        message: `WireGuard ping test failed: ${error.message}`
      };
    }
  }

  /**
   * Ensure base directory exists
   */
  private ensureBaseDirectory(): void {
    if (!fs.existsSync(this.baseCwd)) {
      this.logger.info('Creating base directory for user-specific files', { path: this.baseCwd });
      fs.mkdirSync(this.baseCwd, { recursive: true });
    } else {
      this.logger.debug('Base directory already exists', { path: this.baseCwd });
    }
  }

  /**
   * Get or create user-specific directory for file downloads
   */
  private ensureUserDirectory(user?: string): string {
    const currentUser = user || 'current-user';
    const userDir = path.join(this.baseCwd, currentUser);
    
    if (!fs.existsSync(userDir)) {
      this.logger.info('Creating user directory for file downloads', { 
        user: currentUser, 
        path: userDir 
      });
      fs.mkdirSync(userDir, { recursive: true });
    } else {
      this.logger.debug('User directory already exists', { 
        user: currentUser, 
        path: userDir 
      });
    }
    
    return userDir;
  }



  /**
   * Cleanup resources including SSH tunnel manager
   */
  async cleanup(): Promise<void> {
    // Use new tunnel manager if available
    if (this.tunnelManager) {
      try {
        await this.tunnelManager.disconnect();
        this.logger.info('SSH tunnel manager disconnected successfully');
      } catch (error) {
        this.logger.error('Error disconnecting SSH tunnel manager', { error });
      }
    } else {
      // Fallback to old cleanup methods
      // this.closeSSHTunnel(); // Removed
      // this.closeRegularOperationsTunnel(); // Removed
    }
    
    // Clear any remaining environment variables
    delete process.env.LUDUS_API_KEY;
    delete process.env.LUDUS_URL;
    delete process.env.LUDUS_VERIFY;
    delete process.env.LUDUS_JSON;
  }
} 