import { execSync } from 'child_process';
import * as os from 'os';
import * as fs from 'fs';
import * as path from 'path';
import { Logger } from './logger.js';

export interface SecretDialogOptions {
  secretName: string;
  description: string;
  isPassword?: boolean;
  title?: string;
}

export interface SecretDialogResult {
  success: boolean;
  secret?: string;
  cancelled?: boolean;
  error?: string;
}

export class SecretDialog {
  private logger: Logger;

  constructor(logger: Logger) {
    this.logger = logger;
  }

  /**
   * Show platform-specific secret input dialog
   */
  async showSecretDialog(options: SecretDialogOptions): Promise<SecretDialogResult> {
    const { secretName, description, isPassword = true, title = 'Enter Secret' } = options;
    
    this.logger.info('Showing secret dialog', { 
      secretName, 
      platform: process.platform,
      isPassword 
    });

    try {
      const platform = process.platform;
      
      if (platform === 'win32') {
        return await this.showWindowsDialog(title, description, isPassword);
      } else if (platform === 'darwin') {
        return await this.showMacDialog(title, description, isPassword);
      } else {
        return await this.showLinuxDialog(title, description, isPassword);
      }
    } catch (error: any) {
      this.logger.error('Failed to show secret dialog', { error: error.message });
      return {
        success: false,
        error: error.message
      };
    }
  }

  /**
   * Windows PowerShell dialog using Windows Forms
   */
  private async showWindowsDialog(title: string, description: string, isPassword: boolean): Promise<SecretDialogResult> {
    const passwordChar = isPassword ? '$textBox.UseSystemPasswordChar = $true' : '';
    
    // Escape strings for PowerShell
    const escapedTitle = title.replace(/'/g, "''");
    const escapedDescription = description.replace(/'/g, "''");
    
    // Try simpler approach first for debugging
    if (process.env.LUDUS_DEBUG_DIALOG) {
      return this.showSimpleWindowsDialog(escapedTitle, escapedDescription, isPassword);
    }
    
    const psScript = `
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form = New-Object System.Windows.Forms.Form
$form.Text = '${escapedTitle}'
$form.Size = New-Object System.Drawing.Size(400, 200)
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = "FixedDialog"
[void]($form.MaximizeBox = $false)
[void]($form.MinimizeBox = $false)  
[void]($form.TopMost = $true)

$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10, 20)
$label.Size = New-Object System.Drawing.Size(370, 40)
$label.Text = '${escapedDescription}'
[void]$form.Controls.Add($label)

$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10, 70)
$textBox.Size = New-Object System.Drawing.Size(360, 20)
${passwordChar}
[void]$form.Controls.Add($textBox)

$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(225, 120)
$okButton.Size = New-Object System.Drawing.Size(75, 23)
$okButton.Text = "OK"
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $okButton
[void]$form.Controls.Add($okButton)

$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Point(310, 120)
$cancelButton.Size = New-Object System.Drawing.Size(75, 23)
$cancelButton.Text = "Cancel"
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $cancelButton
[void]$form.Controls.Add($cancelButton)

[void]$textBox.Focus()
$result = $form.ShowDialog()

if ($result -eq [System.Windows.Forms.DialogResult]::OK) {
    Write-Output $textBox.Text
} else {
    Write-Output "__CANCELLED__"
}

$form.Dispose()
    `;

    try {
      // Use temporary file approach to avoid quote escaping issues
      const tempDir = os.tmpdir();
      const tempFile = path.join(tempDir, `ludus-dialog-${Date.now()}.ps1`);
      
      fs.writeFileSync(tempFile, psScript, 'utf8');
      
      const output = execSync(`powershell -ExecutionPolicy Bypass -File "${tempFile}"`, {
        encoding: 'utf8',
        timeout: 120000
      }).trim();

      // Clean up temp file
      try {
        fs.unlinkSync(tempFile);
      } catch (e) {
        // Ignore cleanup errors
      }

      if (output === '__CANCELLED__') {
        return { success: false, cancelled: true };
      }

      if (!output || output.length === 0) {
        return { success: false, error: 'No input received from dialog' };
      }

      return { success: true, secret: output };
    } catch (error: any) {
      if (error.signal === 'SIGTERM') {
        return { success: false, cancelled: true };
      }
      throw error;
    }
  }

  /**
   * Simple Windows dialog for debugging
   */
  private async showSimpleWindowsDialog(title: string, description: string, isPassword: boolean): Promise<SecretDialogResult> {
    const passwordFlag = isPassword ? ' -AsSecureString' : '';
    const simpleScript = `
$credential = Read-Host -Prompt '${description}'${passwordFlag}
if ($credential) {
    if ($credential.GetType().Name -eq 'SecureString') {
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credential)
        $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
        Write-Output $PlainPassword
    } else {
        Write-Output $credential
    }
} else {
    Write-Output "__CANCELLED__"
}
    `;

    try {
      const output = execSync(`powershell -ExecutionPolicy Bypass -Command "${simpleScript.replace(/"/g, '""')}"`, {
        encoding: 'utf8',
        timeout: 120000
      }).trim();

      if (output === '__CANCELLED__' || !output) {
        return { success: false, cancelled: true };
      }

      return { success: true, secret: output };
    } catch (error: any) {
      if (error.signal === 'SIGTERM') {
        return { success: false, cancelled: true };
      }
      throw error;
    }
  }

  /**
   * macOS AppleScript dialog
   */
  private async showMacDialog(title: string, description: string, isPassword: boolean): Promise<SecretDialogResult> {
    const dialogType = isPassword ? 'with hidden answer' : '';
    
    const script = `osascript -e 'display dialog "${description}" default answer "" ${dialogType} with title "${title}" buttons {"Cancel", "OK"} default button "OK"' 2>/dev/null || echo "__CANCELLED__"`;

    try {
      const output = execSync(script, {
        encoding: 'utf8',
        timeout: 120000
      }).trim();

      if (output === '__CANCELLED__' || output.includes('User canceled')) {
        return { success: false, cancelled: true };
      }

      // AppleScript output format: "button returned:OK, text returned:SECRET"
      const match = output.match(/text returned:(.*)$/);
      if (match) {
        return { success: true, secret: match[1] };
      }

      throw new Error('Failed to parse AppleScript output');
    } catch (error: any) {
      if (error.status === 1) {
        return { success: false, cancelled: true };
      }
      throw error;
    }
  }

  /**
   * Linux dialog using zenity or kdialog
   */
  private async showLinuxDialog(title: string, description: string, isPassword: boolean): Promise<SecretDialogResult> {
    // Try zenity first
    if (this.hasCommand('zenity')) {
      return this.showZenityDialog(title, description, isPassword);
    }
    
    // Fallback to kdialog
    if (this.hasCommand('kdialog')) {
      return this.showKDialogDialog(title, description, isPassword);
    }

    throw new Error('No GUI dialog tool available (zenity or kdialog required on Linux)');
  }

  private async showZenityDialog(title: string, description: string, isPassword: boolean): Promise<SecretDialogResult> {
    const entryType = isPassword ? '--password' : '--entry';
    const command = `zenity ${entryType} --title="${title}" --text="${description}" --width=400`;

    try {
      const output = execSync(command, {
        encoding: 'utf8',
        timeout: 120000
      }).trim();

      return { success: true, secret: output };
    } catch (error: any) {
      if (error.status === 1) {
        return { success: false, cancelled: true };
      }
      throw error;
    }
  }

  private async showKDialogDialog(title: string, description: string, isPassword: boolean): Promise<SecretDialogResult> {
    const dialogType = isPassword ? '--password' : '--inputbox';
    const command = `kdialog ${dialogType} "${description}" --title "${title}"`;

    try {
      const output = execSync(command, {
        encoding: 'utf8',
        timeout: 120000
      }).trim();

      return { success: true, secret: output };
    } catch (error: any) {
      if (error.status === 1) {
        return { success: false, cancelled: true };
      }
      throw error;
    }
  }

  /**
   * Check if a command is available
   */
  private hasCommand(command: string): boolean {
    try {
      execSync(`which ${command}`, { stdio: 'ignore' });
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Check if GUI is available on the current system
   */
  static hasGUI(): boolean {
    const platform = process.platform;
    
    if (platform === 'win32') {
      // Windows almost always has GUI available
      return true;
    } else if (platform === 'darwin') {
      // macOS always has GUI available
      return true;
    } else {
      // Linux - check for display
      return !!process.env.DISPLAY || !!process.env.WAYLAND_DISPLAY;
    }
  }
} 