/**
 * Terraform Executor
 * Wraps Terraform CLI operations
 */

import { exec } from 'child_process'
import { promisify } from 'util'
import { promises as fs } from 'fs'
import { join } from 'path'
import { logger } from './logger.js'

const execAsync = promisify(exec)

export interface TerraformOptions {
  workingDirectory: string
  variables?: Record<string, any>
  backendConfig?: Record<string, string>
  stateFile?: string
}

export class TerraformExecutor {
  /**
   * Check if Terraform is installed
   */
  async checkTerraformInstalled(): Promise<boolean> {
    try {
      await execAsync('terraform version')
      return true
    } catch {
      return false
    }
  }

  /**
   * Initialize Terraform
   */
  async init(options: TerraformOptions): Promise<{ stdout: string; stderr: string }> {
    const args: string[] = ['init']

    if (options.backendConfig) {
      for (const [key, value] of Object.entries(options.backendConfig)) {
        args.push('-backend-config', `${key}=${value}`)
      }
    }

    logger.info('Executing Terraform init', { workingDirectory: options.workingDirectory })
    return execAsync(`terraform ${args.join(' ')}`, {
      cwd: options.workingDirectory,
    })
  }

  /**
   * Plan Terraform changes
   */
  async plan(options: TerraformOptions, planFile?: string): Promise<{ stdout: string; stderr: string }> {
    const args: string[] = ['plan']

    if (planFile) {
      args.push('-out', planFile)
    }

    // Add variables
    if (options.variables) {
      for (const [key, value] of Object.entries(options.variables)) {
        args.push('-var', `${key}=${JSON.stringify(value)}`)
      }
    }

    logger.info('Executing Terraform plan', { workingDirectory: options.workingDirectory })
    return execAsync(`terraform ${args.join(' ')}`, {
      cwd: options.workingDirectory,
    })
  }

  /**
   * Apply Terraform changes
   */
  async apply(options: TerraformOptions, planFile?: string, autoApprove: boolean = false): Promise<{ stdout: string; stderr: string }> {
    const args: string[] = ['apply']

    if (autoApprove) {
      args.push('-auto-approve')
    }

    if (planFile) {
      args.push(planFile)
    } else {
      // Add variables
      if (options.variables) {
        for (const [key, value] of Object.entries(options.variables)) {
          args.push('-var', `${key}=${JSON.stringify(value)}`)
        }
      }
    }

    logger.info('Executing Terraform apply', { workingDirectory: options.workingDirectory })
    return execAsync(`terraform ${args.join(' ')}`, {
      cwd: options.workingDirectory,
    })
  }

  /**
   * Destroy Terraform resources
   */
  async destroy(options: TerraformOptions, autoApprove: boolean = false): Promise<{ stdout: string; stderr: string }> {
    const args: string[] = ['destroy']

    if (autoApprove) {
      args.push('-auto-approve')
    }

    if (options.variables) {
      for (const [key, value] of Object.entries(options.variables)) {
        args.push('-var', `${key}=${JSON.stringify(value)}`)
      }
    }

    logger.info('Executing Terraform destroy', { workingDirectory: options.workingDirectory })
    return execAsync(`terraform ${args.join(' ')}`, {
      cwd: options.workingDirectory,
    })
  }

  /**
   * Get Terraform output
   */
  async output(options: TerraformOptions, outputName?: string): Promise<any> {
    const args: string[] = ['output', '-json']

    if (outputName) {
      args.push(outputName)
    }

    try {
      const { stdout } = await execAsync(`terraform ${args.join(' ')}`, {
        cwd: options.workingDirectory,
      })
      return JSON.parse(stdout)
    } catch (error) {
      logger.error('Failed to get Terraform output', { error })
      throw error
    }
  }

  /**
   * Get Terraform state
   */
  async state(options: TerraformOptions): Promise<any> {
    try {
      const { stdout } = await execAsync('terraform show -json', {
        cwd: options.workingDirectory,
      })
      return JSON.parse(stdout)
    } catch (error) {
      logger.error('Failed to get Terraform state', { error })
      throw error
    }
  }

  /**
   * Validate Terraform configuration
   */
  async validate(options: TerraformOptions): Promise<{ valid: boolean; errors: string[] }> {
    try {
      const { stdout, stderr } = await execAsync('terraform validate', {
        cwd: options.workingDirectory,
      })

      return {
        valid: true,
        errors: [],
      }
    } catch (error: any) {
      return {
        valid: false,
        errors: [error.message || 'Validation failed'],
      }
    }
  }

  /**
   * Write Terraform files to working directory
   */
  async writeFiles(workingDirectory: string, files: Record<string, string>): Promise<void> {
    await fs.mkdir(workingDirectory, { recursive: true })

    for (const [filename, content] of Object.entries(files)) {
      const filePath = join(workingDirectory, filename)
      await fs.writeFile(filePath, content, 'utf-8')
    }
  }
}

export const terraformExecutor = new TerraformExecutor()

