const { exec, spawn } = require('child_process');
const { promisify } = require('util');
const path = require('path');
const platformUtils = require('../shared/platform-utils');
const DownloadManager = require('./download-manager');

const execAsync = promisify(exec);

class DependencyChecker {
  /**
   * Checks if Docker is installed and running
   * @returns {Promise<Object>} { installed: boolean, running: boolean, version: string }
   */
  async checkDocker() {
    try {
      // Check if Docker is installed
      const versionResult = await this.executeCommand('docker --version');
      
      if (!versionResult.success) {
        return {
          installed: false,
          running: false,
          version: null
        };
      }

      // Extract version from output (e.g., "Docker version 24.0.7, build afdd53b")
      const versionMatch = versionResult.stdout.match(/Docker version ([\d.]+)/);
      const version = versionMatch ? versionMatch[1] : 'unknown';

      // Check if Docker daemon is running
      const infoResult = await this.executeCommand('docker info');
      
      return {
        installed: true,
        running: infoResult.success,
        version: version
      };
    } catch (error) {
      return {
        installed: false,
        running: false,
        version: null,
        error: error.message
      };
    }
  }

  /**
   * Checks if Caddy is installed
   * @param {string[]} [additionalPaths] - Additional paths to search for caddy binary
   * @returns {Promise<Object>} { installed: boolean, version: string, path: string }
   */
  async checkCaddy(additionalPaths) {
    try {
      // First try caddy in PATH
      const result = await this.executeCommand('caddy version');

      if (result.success) {
        const versionMatch = result.stdout.match(/v([\d.]+)/);
        const version = versionMatch ? versionMatch[1] : 'unknown';

        // Try to get Caddy path
        let caddyPath = null;
        if (platformUtils.isWindows()) {
          const whereResult = await this.executeCommand('where caddy');
          if (whereResult.success) {
            caddyPath = whereResult.stdout.trim().split('\n')[0];
          }
        } else {
          const whichResult = await this.executeCommand('which caddy');
          if (whichResult.success) {
            caddyPath = whichResult.stdout.trim();
          }
        }

        return { installed: true, version, path: caddyPath };
      }

      // Not in PATH - search common locations
      const searchResult = await this.findCaddyBinary(additionalPaths);
      if (searchResult) {
        return searchResult;
      }

      return { installed: false, version: null, path: null };
    } catch (error) {
      return {
        installed: false,
        version: null,
        path: null,
        error: error.message
      };
    }
  }

  /**
   * Searches common locations for the Caddy binary
   * @param {string[]} [additionalPaths] - Additional paths to search
   * @returns {Promise<Object|null>} Caddy info if found, null otherwise
   */
  async findCaddyBinary(additionalPaths) {
    const fs = require('fs');
    const binaryName = platformUtils.isWindows() ? 'caddy.exe' : 'caddy';

    // Build list of candidate paths
    const searchPaths = [];

    if (platformUtils.isWindows()) {
      searchPaths.push(
        'C:\\caddy',
        'C:\\Program Files\\Caddy',
        'C:\\Program Files (x86)\\Caddy',
        path.join(process.env.LOCALAPPDATA || '', 'Caddy'),
        path.join(process.env.USERPROFILE || '', 'caddy'),
        'C:\\DashCaddy\\caddy'
      );
    } else {
      searchPaths.push(
        '/usr/local/bin',
        '/usr/bin',
        '/opt/caddy',
        '/opt/dashcaddy/caddy',
        path.join(process.env.HOME || '', '.local/bin')
      );
    }

    // Add any user-specified paths
    if (additionalPaths) {
      searchPaths.push(...additionalPaths);
    }

    for (const dir of searchPaths) {
      if (!dir) continue;
      const fullPath = path.join(dir, binaryName);
      try {
        fs.accessSync(fullPath, fs.constants.X_OK);
        // Found the binary - verify it works
        const result = await this.executeCommand(`"${fullPath}" version`);
        if (result.success) {
          const versionMatch = result.stdout.match(/v([\d.]+)/);
          const version = versionMatch ? versionMatch[1] : 'unknown';
          return { installed: true, version, path: fullPath };
        }
      } catch {
        // Not found here, continue searching
      }
    }

    return null;
  }

  /**
   * Checks platform information
   * @returns {Promise<Object>} Platform details
   */
  async checkPlatform() {
    return platformUtils.getPlatformInfo();
  }

  /**
   * Provides installation instructions for Docker
   * @param {string} platform - Platform identifier (windows, macos, linux)
   * @returns {Object} Installation instructions
   */
  getDockerInstallInstructions(platform) {
    const instructions = {
      windows: {
        title: 'Install Docker Desktop for Windows',
        steps: [
          'Download Docker Desktop from https://www.docker.com/products/docker-desktop',
          'Run the installer and follow the setup wizard',
          'Restart your computer if prompted',
          'Launch Docker Desktop from the Start menu',
          'Wait for Docker to start (you\'ll see the whale icon in the system tray)',
          'Click "Retry" in the installer once Docker is running'
        ],
        url: 'https://docs.docker.com/desktop/install/windows-install/',
        requiresWSL2: true,
        wsl2Instructions: [
          'Docker Desktop requires WSL 2',
          'The installer will guide you through enabling WSL 2 if needed',
          'You may need to enable virtualization in your BIOS'
        ]
      },
      macos: {
        title: 'Install Docker Desktop for Mac',
        steps: [
          'Download Docker Desktop from https://www.docker.com/products/docker-desktop',
          'Open the .dmg file and drag Docker to Applications',
          'Launch Docker from Applications',
          'Grant necessary permissions when prompted',
          'Wait for Docker to start (you\'ll see the whale icon in the menu bar)',
          'Click "Retry" in the installer once Docker is running'
        ],
        url: 'https://docs.docker.com/desktop/install/mac-install/',
        requiresWSL2: false
      },
      linux: {
        title: 'Install Docker Engine for Linux',
        steps: [
          'Open a terminal',
          'Run: curl -fsSL https://get.docker.com -o get-docker.sh',
          'Run: sudo sh get-docker.sh',
          'Run: sudo systemctl start docker',
          'Run: sudo systemctl enable docker',
          'Run: sudo usermod -aG docker $USER',
          'Log out and back in for group changes to take effect',
          'Click "Retry" in the installer'
        ],
        url: 'https://docs.docker.com/engine/install/',
        requiresWSL2: false,
        packageManagers: {
          ubuntu: 'sudo apt-get update && sudo apt-get install docker.io',
          fedora: 'sudo dnf install docker',
          arch: 'sudo pacman -S docker'
        }
      }
    };

    return instructions[platform] || instructions.linux;
  }

  /**
   * Provides installation instructions for Caddy
   * @param {string} platform - Platform identifier (windows, macos, linux)
   * @returns {Object} Installation instructions
   */
  getCaddyInstallInstructions(platform) {
    const instructions = {
      windows: {
        title: 'Install Caddy for Windows',
        steps: [
          'Download Caddy from https://caddyserver.com/download',
          'Extract the caddy.exe file',
          'Move caddy.exe to C:\\Windows\\System32 (requires admin)',
          'Or add the Caddy folder to your PATH environment variable',
          'Open a new PowerShell window',
          'Run: caddy version',
          'Click "Retry" in the installer'
        ],
        url: 'https://caddyserver.com/docs/install#windows',
        automated: false
      },
      macos: {
        title: 'Install Caddy for macOS',
        steps: [
          'Open Terminal',
          'Install Homebrew if not already installed: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
          'Run: brew install caddy',
          'Run: caddy version',
          'Click "Retry" in the installer'
        ],
        url: 'https://caddyserver.com/docs/install#mac',
        automated: true,
        command: 'brew install caddy'
      },
      linux: {
        title: 'Install Caddy for Linux',
        steps: [
          'Open a terminal',
          'Run the appropriate command for your distribution (see below)',
          'Run: caddy version',
          'Click "Retry" in the installer'
        ],
        url: 'https://caddyserver.com/docs/install#debian-ubuntu-raspbian',
        automated: true,
        packageManagers: {
          ubuntu: 'sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https && curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/gpg.key" | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg && curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt" | sudo tee /etc/apt/sources.list.d/caddy-stable.list && sudo apt update && sudo apt install caddy',
          fedora: 'dnf install "dnf-command(copr)" && dnf copr enable @caddy/caddy && dnf install caddy',
          arch: 'pacman -S caddy'
        }
      }
    };

    return instructions[platform] || instructions.linux;
  }

  /**
   * Attempts automated installation of Docker (platform-specific)
   * @param {string} platform - Platform identifier
   * @param {Function} progressCallback - Progress callback
   * @returns {Promise<Object>} Installation result
   */
  async installDocker(platform, progressCallback) {
    const downloadManager = new DownloadManager();
    const platformInfo = platformUtils.getPlatformInfo();

    try {
      // Download Docker Desktop
      progressCallback?.({ status: 'downloading', message: 'Downloading Docker Desktop...' });

      const downloadResult = await downloadManager.downloadDocker(
        platform,
        platformInfo.arch,
        progressCallback
      );

      if (!downloadResult.success) {
        // Fallback to instructions if download fails
        return {
          success: false,
          automated: false,
          message: downloadResult.message || 'Download failed',
          instructions: this.getDockerInstallInstructions(platform)
        };
      }

      // Run the installer
      progressCallback?.({ status: 'installing', message: 'Running Docker installer...' });

      if (platform === 'windows') {
        // Run Docker Desktop installer silently
        const installerPath = downloadResult.path;

        try {
          // The Docker Desktop installer supports silent install
          await this.runInstallerWindows(installerPath);

          // Wait for Docker to be ready
          progressCallback?.({ status: 'waiting', message: 'Waiting for Docker to start...' });
          await this.waitForDocker(120000); // 2 minute timeout

          // Verify installation
          const verification = await this.checkDocker();

          if (verification.installed) {
            await downloadManager.cleanup();
            return {
              success: true,
              automated: true,
              message: 'Docker Desktop installed successfully',
              version: verification.version
            };
          }
        } catch (err) {
          // Installation might require user interaction or admin
          return {
            success: false,
            automated: true,
            message: 'Docker installer launched. Please complete the installation and restart.',
            instructions: this.getDockerInstallInstructions(platform)
          };
        }
      } else if (platform === 'macos') {
        // Mount and install DMG
        const dmgPath = downloadResult.path;

        try {
          await this.runInstallerMacOS(dmgPath);

          progressCallback?.({ status: 'waiting', message: 'Waiting for Docker to start...' });
          await this.waitForDocker(120000);

          const verification = await this.checkDocker();

          if (verification.installed) {
            await downloadManager.cleanup();
            return {
              success: true,
              automated: true,
              message: 'Docker Desktop installed successfully',
              version: verification.version
            };
          }
        } catch (err) {
          return {
            success: false,
            automated: true,
            message: 'Docker installation started. Please complete any prompts.',
            instructions: this.getDockerInstallInstructions(platform)
          };
        }
      } else {
        // Linux - use package manager
        return await this.installDockerLinux(progressCallback);
      }

      return {
        success: false,
        automated: false,
        message: 'Docker installation requires manual completion',
        instructions: this.getDockerInstallInstructions(platform)
      };
    } catch (error) {
      return {
        success: false,
        automated: false,
        message: error.message,
        instructions: this.getDockerInstallInstructions(platform)
      };
    }
  }

  /**
   * Run Docker installer on Windows
   */
  async runInstallerWindows(installerPath) {
    return new Promise((resolve, reject) => {
      // Start installer with install flag
      const installer = spawn(installerPath, ['install', '--quiet', '--accept-license'], {
        detached: true,
        stdio: 'ignore'
      });

      installer.unref();

      // Give the installer time to start
      setTimeout(() => resolve(), 5000);
    });
  }

  /**
   * Run Docker installer on macOS
   */
  async runInstallerMacOS(dmgPath) {
    // Mount the DMG
    await this.executeCommand(`hdiutil attach "${dmgPath}" -nobrowse`);

    // Copy to Applications
    await this.executeCommand('cp -R "/Volumes/Docker/Docker.app" /Applications/');

    // Unmount
    await this.executeCommand('hdiutil detach "/Volumes/Docker"');

    // Launch Docker
    await this.executeCommand('open /Applications/Docker.app');
  }

  /**
   * Install Docker on Linux using package manager
   */
  async installDockerLinux(progressCallback) {
    const distro = await this.detectLinuxDistro();

    progressCallback?.({ status: 'installing', message: `Installing Docker for ${distro}...` });

    try {
      if (distro === 'ubuntu' || distro === 'debian') {
        await this.executeCommand('curl -fsSL https://get.docker.com -o /tmp/get-docker.sh');
        await this.executeCommand('sudo sh /tmp/get-docker.sh');
        await this.executeCommand('sudo systemctl enable docker');
        await this.executeCommand('sudo systemctl start docker');
      } else if (distro === 'fedora') {
        await this.executeCommand('sudo dnf install -y docker-ce docker-ce-cli containerd.io');
        await this.executeCommand('sudo systemctl enable docker');
        await this.executeCommand('sudo systemctl start docker');
      } else if (distro === 'arch') {
        await this.executeCommand('sudo pacman -S --noconfirm docker');
        await this.executeCommand('sudo systemctl enable docker');
        await this.executeCommand('sudo systemctl start docker');
      } else {
        return {
          success: false,
          automated: false,
          message: 'Unsupported Linux distribution',
          instructions: this.getDockerInstallInstructions('linux')
        };
      }

      // Add user to docker group
      await this.executeCommand('sudo usermod -aG docker $USER');

      const verification = await this.checkDocker();

      return {
        success: verification.installed,
        automated: true,
        message: verification.installed ?
          'Docker installed successfully. You may need to log out and back in for group changes.' :
          'Docker installed but may require restart',
        version: verification.version
      };
    } catch (error) {
      return {
        success: false,
        automated: true,
        message: error.message,
        instructions: this.getDockerInstallInstructions('linux')
      };
    }
  }

  /**
   * Wait for Docker to become available
   */
  async waitForDocker(timeout = 60000) {
    const startTime = Date.now();
    const pollInterval = 3000;

    while (Date.now() - startTime < timeout) {
      const status = await this.checkDocker();
      if (status.installed && status.running) {
        return true;
      }
      await new Promise(resolve => setTimeout(resolve, pollInterval));
    }

    throw new Error('Docker did not become available within timeout');
  }

  /**
   * Attempts automated installation of Caddy (platform-specific)
   * @param {string} platform - Platform identifier
   * @param {Function} progressCallback - Progress callback
   * @param {string} targetPath - Optional target path for Caddy binary
   * @returns {Promise<Object>} Installation result
   */
  async installCaddy(platform, progressCallback, targetPath) {
    const downloadManager = new DownloadManager();
    const platformInfo = platformUtils.getPlatformInfo();
    const instructions = this.getCaddyInstallInstructions(platform);

    try {
      // Download Caddy from GitHub
      progressCallback?.({ status: 'downloading', message: 'Downloading Caddy...' });

      const downloadResult = await downloadManager.downloadCaddy(
        platform,
        platformInfo.arch,
        progressCallback
      );

      if (!downloadResult.success) {
        // Try package manager as fallback
        return await this.installCaddyWithPackageManager(platform, progressCallback);
      }

      // Extract the archive
      progressCallback?.({ status: 'extracting', message: 'Extracting Caddy...' });

      // Determine target directory
      let extractTarget;
      if (targetPath) {
        extractTarget = targetPath;
      } else if (platform === 'windows') {
        extractTarget = 'C:\\Program Files\\Caddy';
      } else {
        extractTarget = '/usr/local/bin';
      }

      const extractResult = await downloadManager.extractCaddy(
        downloadResult.path,
        extractTarget,
        platform
      );

      if (!extractResult.success) {
        return {
          success: false,
          automated: true,
          message: 'Failed to extract Caddy',
          error: extractResult.error,
          instructions
        };
      }

      // On Windows, add to PATH if needed
      if (platform === 'windows') {
        progressCallback?.({ status: 'configuring', message: 'Adding Caddy to PATH...' });
        await this.addToWindowsPath(extractTarget);
      }

      // Verify installation
      progressCallback?.({ status: 'verifying', message: 'Verifying installation...' });

      // Give a moment for PATH changes to take effect
      await new Promise(resolve => setTimeout(resolve, 1000));

      const verification = await this.checkCaddy([extractTarget]);

      await downloadManager.cleanup();

      if (verification.installed) {
        return {
          success: true,
          automated: true,
          message: 'Caddy installed successfully',
          version: downloadResult.version || verification.version,
          path: verification.path || extractResult.binaryPath
        };
      } else {
        // Binary exists but not in PATH yet
        return {
          success: true,
          automated: true,
          message: 'Caddy installed. Restart terminal for PATH changes.',
          version: downloadResult.version,
          path: extractResult.binaryPath
        };
      }
    } catch (error) {
      // Fallback to package manager
      return await this.installCaddyWithPackageManager(platform, progressCallback);
    }
  }

  /**
   * Install Caddy using system package manager
   */
  async installCaddyWithPackageManager(platform, progressCallback) {
    const instructions = this.getCaddyInstallInstructions(platform);

    try {
      let command;

      if (platform === 'macos') {
        // Check if Homebrew is available
        const brewCheck = await this.executeCommand('which brew');
        if (brewCheck.success) {
          progressCallback?.({ status: 'installing', message: 'Installing Caddy via Homebrew...' });
          command = 'brew install caddy';
        } else {
          return {
            success: false,
            automated: false,
            message: 'Homebrew not found. Please install Homebrew first.',
            instructions
          };
        }
      } else if (platform === 'linux') {
        const distro = await this.detectLinuxDistro();
        progressCallback?.({ status: 'installing', message: `Installing Caddy for ${distro}...` });

        if (distro === 'ubuntu' || distro === 'debian') {
          // Add Caddy repository and install
          await this.executeCommand('sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https');
          await this.executeCommand('curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/gpg.key" | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg');
          await this.executeCommand('curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt" | sudo tee /etc/apt/sources.list.d/caddy-stable.list');
          await this.executeCommand('sudo apt update');
          command = 'sudo apt install -y caddy';
        } else if (distro === 'fedora') {
          await this.executeCommand('sudo dnf install -y "dnf-command(copr)"');
          await this.executeCommand('sudo dnf copr enable -y @caddy/caddy');
          command = 'sudo dnf install -y caddy';
        } else if (distro === 'arch') {
          command = 'sudo pacman -S --noconfirm caddy';
        } else {
          return {
            success: false,
            automated: false,
            message: 'Unsupported Linux distribution',
            instructions
          };
        }
      } else {
        return {
          success: false,
          automated: false,
          message: 'Automated installation not supported',
          instructions
        };
      }

      const result = await this.executeCommand(command);

      if (result.success) {
        const verification = await this.checkCaddy();

        return {
          success: verification.installed,
          automated: true,
          message: verification.installed ? 'Caddy installed successfully' : 'Installation completed but verification failed',
          version: verification.version
        };
      } else {
        return {
          success: false,
          automated: true,
          message: 'Installation command failed',
          error: result.stderr,
          instructions
        };
      }
    } catch (error) {
      return {
        success: false,
        automated: true,
        message: 'Installation failed',
        error: error.message,
        instructions
      };
    }
  }

  /**
   * Add directory to Windows PATH
   */
  async addToWindowsPath(directory) {
    try {
      // Add to user PATH using PowerShell
      const script = `
        $currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
        if ($currentPath -notlike '*${directory.replace(/\\/g, '\\\\')}*') {
          [Environment]::SetEnvironmentVariable('Path', "$currentPath;${directory.replace(/\\/g, '\\\\')}", 'User')
        }
      `;

      await this.executeCommand(`powershell -Command "${script}"`);
      return { success: true };
    } catch (error) {
      return { success: false, error: error.message };
    }
  }

  /**
   * Detects Linux distribution
   * @returns {Promise<string>} Distribution name (ubuntu, fedora, arch, etc.)
   */
  async detectLinuxDistro() {
    try {
      const result = await this.executeCommand('cat /etc/os-release');
      
      if (result.success) {
        const output = result.stdout.toLowerCase();
        
        if (output.includes('ubuntu') || output.includes('debian')) {
          return 'ubuntu';
        } else if (output.includes('fedora') || output.includes('rhel') || output.includes('centos')) {
          return 'fedora';
        } else if (output.includes('arch')) {
          return 'arch';
        }
      }
      
      return 'unknown';
    } catch (error) {
      return 'unknown';
    }
  }

  /**
   * Executes a shell command
   * @param {string} command - Command to execute
   * @returns {Promise<Object>} { success: boolean, stdout: string, stderr: string }
   */
  async executeCommand(command) {
    try {
      const { stdout, stderr } = await execAsync(command, {
        timeout: 30000, // 30 second timeout
        maxBuffer: 1024 * 1024 // 1MB buffer
      });

      return {
        success: true,
        stdout: stdout,
        stderr: stderr
      };
    } catch (error) {
      return {
        success: false,
        stdout: error.stdout || '',
        stderr: error.stderr || error.message,
        error: error.message
      };
    }
  }

  /**
   * Checks if WSL2 is available on Windows
   * @returns {Promise<Object>} WSL2 status
   */
  async checkWSL2() {
    if (!platformUtils.isWindows()) {
      return {
        available: false,
        required: false,
        message: 'WSL2 is only relevant on Windows'
      };
    }

    try {
      const result = await this.executeCommand('wsl --status');
      
      return {
        available: result.success,
        required: true,
        message: result.success ? 'WSL2 is available' : 'WSL2 is not installed',
        details: result.stdout
      };
    } catch (error) {
      return {
        available: false,
        required: true,
        message: 'WSL2 is not installed or not accessible',
        error: error.message
      };
    }
  }
}

module.exports = DependencyChecker;
