/**
 * Download Manager
 * Handles downloading Docker Desktop and Caddy binaries
 */

const https = require('https');
const http = require('http');
const fs = require('fs');
const path = require('path');
const { URL } = require('url');

class DownloadManager {
  constructor() {
    // Docker Desktop download URLs
    this.dockerUrls = {
      windows: 'https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe',
      macos_amd64: 'https://desktop.docker.com/mac/main/amd64/Docker.dmg',
      macos_arm64: 'https://desktop.docker.com/mac/main/arm64/Docker.dmg'
    };

    // Caddy releases API
    this.caddyReleasesUrl = 'https://api.github.com/repos/caddyserver/caddy/releases/latest';

    // Temporary download directory
    this.tempDir = path.join(require('os').tmpdir(), 'dashcaddy-installer');
  }

  /**
   * Ensure temp directory exists
   */
  async ensureTempDir() {
    try {
      await fs.promises.mkdir(this.tempDir, { recursive: true });
    } catch (err) {
      if (err.code !== 'EEXIST') throw err;
    }
  }

  /**
   * Download a file with progress tracking
   */
  downloadFile(url, destPath, progressCallback) {
    return new Promise((resolve, reject) => {
      const urlObj = new URL(url);
      const protocol = urlObj.protocol === 'https:' ? https : http;

      const request = protocol.get(url, {
        headers: {
          'User-Agent': 'DashCaddy-Installer/1.0'
        }
      }, (response) => {
        // Handle redirects
        if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
          return this.downloadFile(response.headers.location, destPath, progressCallback)
            .then(resolve)
            .catch(reject);
        }

        if (response.statusCode !== 200) {
          reject(new Error(`Download failed: HTTP ${response.statusCode}`));
          return;
        }

        const totalSize = parseInt(response.headers['content-length'], 10) || 0;
        let downloadedSize = 0;

        const fileStream = fs.createWriteStream(destPath);

        response.on('data', (chunk) => {
          downloadedSize += chunk.length;
          if (progressCallback && totalSize > 0) {
            progressCallback({
              downloaded: downloadedSize,
              total: totalSize,
              percent: Math.round((downloadedSize / totalSize) * 100)
            });
          }
        });

        response.pipe(fileStream);

        fileStream.on('finish', () => {
          fileStream.close();
          resolve({
            success: true,
            path: destPath,
            size: downloadedSize
          });
        });

        fileStream.on('error', (err) => {
          fs.unlink(destPath, () => {}); // Clean up partial file
          reject(err);
        });
      });

      request.on('error', reject);
      request.setTimeout(60000, () => {
        request.destroy();
        reject(new Error('Download timeout'));
      });
    });
  }

  /**
   * Get Docker Desktop download URL for platform
   */
  getDockerDownloadUrl(platform, arch) {
    if (platform === 'windows') {
      return this.dockerUrls.windows;
    } else if (platform === 'macos') {
      return arch === 'arm64' ? this.dockerUrls.macos_arm64 : this.dockerUrls.macos_amd64;
    } else {
      // Linux uses package managers, return null
      return null;
    }
  }

  /**
   * Download Docker Desktop
   */
  async downloadDocker(platform, arch, progressCallback) {
    await this.ensureTempDir();

    const url = this.getDockerDownloadUrl(platform, arch);

    if (!url) {
      return {
        success: false,
        automated: false,
        message: 'Docker installation on Linux uses package managers',
        instructions: this.getLinuxDockerInstructions()
      };
    }

    const filename = platform === 'windows' ? 'DockerDesktopInstaller.exe' : 'Docker.dmg';
    const destPath = path.join(this.tempDir, filename);

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

      const result = await this.downloadFile(url, destPath, (progress) => {
        progressCallback?.({
          status: 'downloading',
          message: `Downloading Docker Desktop... ${progress.percent}%`,
          ...progress
        });
      });

      return {
        success: true,
        path: result.path,
        size: result.size
      };
    } catch (err) {
      return {
        success: false,
        error: err.message
      };
    }
  }

  /**
   * Get latest Caddy release info from GitHub
   */
  async getCaddyLatestRelease() {
    return new Promise((resolve, reject) => {
      const urlObj = new URL(this.caddyReleasesUrl);

      const request = https.get({
        hostname: urlObj.hostname,
        path: urlObj.pathname,
        headers: {
          'User-Agent': 'DashCaddy-Installer/1.0',
          'Accept': 'application/vnd.github.v3+json'
        }
      }, (response) => {
        let data = '';

        response.on('data', chunk => {
          data += chunk;
        });

        response.on('end', () => {
          try {
            const release = JSON.parse(data);
            resolve(release);
          } catch (err) {
            reject(new Error('Failed to parse GitHub API response'));
          }
        });
      });

      request.on('error', reject);
      request.setTimeout(30000, () => {
        request.destroy();
        reject(new Error('GitHub API timeout'));
      });
    });
  }

  /**
   * Get Caddy download URL for platform/arch
   */
  getCaddyAssetName(platform, arch) {
    const archMap = {
      'x64': 'amd64',
      'arm64': 'arm64',
      'ia32': '386'
    };

    const mappedArch = archMap[arch] || 'amd64';

    if (platform === 'windows') {
      return `caddy_*_windows_${mappedArch}.zip`;
    } else if (platform === 'macos') {
      return `caddy_*_darwin_${mappedArch}.tar.gz`;
    } else {
      return `caddy_*_linux_${mappedArch}.tar.gz`;
    }
  }

  /**
   * Find matching asset in release
   */
  findCaddyAsset(release, platform, arch) {
    const pattern = this.getCaddyAssetName(platform, arch);
    const regex = new RegExp(pattern.replace('*', '.*'));

    return release.assets?.find(asset => regex.test(asset.name));
  }

  /**
   * Download Caddy binary
   */
  async downloadCaddy(platform, arch, progressCallback) {
    await this.ensureTempDir();

    try {
      // Get latest release info
      progressCallback?.({ status: 'fetching', message: 'Fetching latest Caddy version...' });
      const release = await this.getCaddyLatestRelease();

      // Find the right asset
      const asset = this.findCaddyAsset(release, platform, arch);

      if (!asset) {
        return {
          success: false,
          error: `No Caddy binary found for ${platform} ${arch}`
        };
      }

      const destPath = path.join(this.tempDir, asset.name);

      // Download the asset
      progressCallback?.({ status: 'downloading', message: `Downloading Caddy ${release.tag_name}...` });

      const result = await this.downloadFile(asset.browser_download_url, destPath, (progress) => {
        progressCallback?.({
          status: 'downloading',
          message: `Downloading Caddy ${release.tag_name}... ${progress.percent}%`,
          ...progress
        });
      });

      return {
        success: true,
        path: result.path,
        version: release.tag_name,
        size: result.size
      };
    } catch (err) {
      return {
        success: false,
        error: err.message
      };
    }
  }

  /**
   * Extract Caddy from archive
   */
  async extractCaddy(archivePath, targetDir, platform) {
    const { exec } = require('child_process');
    const util = require('util');
    const execAsync = util.promisify(exec);

    await fs.promises.mkdir(targetDir, { recursive: true });

    try {
      if (platform === 'windows') {
        // Use PowerShell to extract zip
        await execAsync(`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${targetDir}' -Force"`);
        return {
          success: true,
          binaryPath: path.join(targetDir, 'caddy.exe')
        };
      } else {
        // Use tar on Unix
        await execAsync(`tar -xzf "${archivePath}" -C "${targetDir}"`);
        const binaryPath = path.join(targetDir, 'caddy');

        // Make executable
        await execAsync(`chmod +x "${binaryPath}"`);

        return {
          success: true,
          binaryPath
        };
      }
    } catch (err) {
      return {
        success: false,
        error: err.message
      };
    }
  }

  /**
   * Clean up temp files
   */
  async cleanup() {
    try {
      const files = await fs.promises.readdir(this.tempDir);
      for (const file of files) {
        await fs.promises.unlink(path.join(this.tempDir, file));
      }
    } catch (err) {
      // Ignore cleanup errors
    }
  }

  /**
   * Linux Docker installation instructions
   */
  getLinuxDockerInstructions() {
    return `
Linux Docker Installation:

For Ubuntu/Debian:
  curl -fsSL https://get.docker.com -o get-docker.sh
  sudo sh get-docker.sh
  sudo usermod -aG docker $USER

For Fedora:
  sudo dnf install docker-ce docker-ce-cli containerd.io
  sudo systemctl enable --now docker

For Arch Linux:
  sudo pacman -S docker
  sudo systemctl enable --now docker
`;
  }
}

module.exports = DownloadManager;
