import { spawn } from 'child_process'
import { join } from 'path'

/**
 * CLI runner for end-to-end testing.
 * Spawns actual CLI process to test real behavior.
 */
export class CLIRunner {
  constructor(cliPath = null) {
    this.cliPath = cliPath || join(process.cwd(), 'cli.js')
  }

  /**
   * Run CLI command and capture output.
   */
  async run(args = [], env = {}) {
    return new Promise((resolve, reject) => {
      const output = { stdout: '', stderr: '', exitCode: null }
      
      // Merge environment variables
      const processEnv = { ...process.env, ...env }
      
      // Spawn the CLI process
      const child = spawn('node', [this.cliPath, ...args], {
        env: processEnv,
        timeout: 30000 // 30 second timeout
      })

      // Capture stdout
      child.stdout.on('data', (data) => {
        output.stdout += data.toString()
      })

      // Capture stderr
      child.stderr.on('data', (data) => {
        output.stderr += data.toString()
      })

      // Handle process exit
      child.on('exit', (code) => {
        output.exitCode = code
        resolve(output)
      })

      // Handle errors
      child.on('error', (error) => {
        reject(error)
      })
    })
  }

  /**
   * Run command expecting success (exit code 0).
   */
  async runSuccess(args = [], env = {}) {
    const result = await this.run(args, env)
    
    if (result.exitCode !== 0) {
      throw new Error(`CLI exited with code ${result.exitCode}:\n${result.stderr}`)
    }
    
    return result
  }

  /**
   * Run command expecting failure.
   */
  async runFailure(args = [], env = {}) {
    const result = await this.run(args, env)
    
    if (result.exitCode === 0) {
      throw new Error('Expected CLI to fail but it succeeded')
    }
    
    return result
  }

  /**
   * Run interactive command with input.
   * Useful for testing SSH connections.
   */
  async runInteractive(args = [], env = {}, inputs = []) {
    return new Promise((resolve, reject) => {
      const output = { stdout: '', stderr: '', exitCode: null }
      
      const child = spawn('node', [this.cliPath, ...args], {
        env: { ...process.env, ...env }
      })

      child.stdout.on('data', (data) => {
        output.stdout += data.toString()
      })

      child.stderr.on('data', (data) => {
        output.stderr += data.toString()
      })

      // Send inputs
      inputs.forEach((input, index) => {
        setTimeout(() => {
          child.stdin.write(input + '\n')
        }, index * 100)
      })

      // Close stdin after inputs
      setTimeout(() => {
        child.stdin.end()
      }, inputs.length * 100 + 100)

      child.on('exit', (code) => {
        output.exitCode = code
        resolve(output)
      })

      child.on('error', reject)
    })
  }
}

/**
 * Create a new CLI runner instance.
 */
export function createCLIRunner() {
  return new CLIRunner()
}