/**
 * Service Manager
 * Handles starting, stopping, and managing Caddy and Docker services
 */

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

const execAsync = promisify(exec);

class ServiceManager {
  constructor() {
    this.platform = platformUtils.getPlatformInfo().os;
  }

  /**
   * Start Caddy with the specified Caddyfile
   * @param {string} caddyfilePath - Path to Caddyfile
   * @param {string} caddyBinaryPath - Optional path to Caddy binary
   * @returns {Promise<Object>} { success: boolean, message: string }
   */
  async startCaddy(caddyfilePath, caddyBinaryPath) {
    try {
      const caddyCmd = caddyBinaryPath || 'caddy';

      // Check if Caddyfile exists
      try {
        await fs.access(caddyfilePath);
      } catch {
        return {
          success: false,
          error: 'Caddyfile not found'
        };
      }

      if (this.platform === 'windows') {
        // On Windows, start Caddy in background
        const caddy = spawn(caddyCmd, ['run', '--config', caddyfilePath], {
          detached: true,
          stdio: 'ignore',
          windowsHide: true
        });

        caddy.unref();

        // Wait a moment for Caddy to start
        await new Promise(resolve => setTimeout(resolve, 2000));

        // Check if Caddy is running
        const status = await this.checkCaddyStatus();

        return {
          success: status.running,
          message: status.running ? 'Caddy started successfully' : 'Caddy may have failed to start',
          pid: caddy.pid
        };
      } else {
        // On Unix, use systemd if available, otherwise start directly
        const systemdAvailable = await this.isSystemdAvailable();

        if (systemdAvailable) {
          // Create systemd service file
          await this.createCaddySystemdService(caddyfilePath, caddyBinaryPath);

          // Start the service
          await execAsync('sudo systemctl daemon-reload');
          await execAsync('sudo systemctl enable caddy');
          await execAsync('sudo systemctl start caddy');

          return {
            success: true,
            message: 'Caddy started via systemd'
          };
        } else {
          // Start directly
          const caddy = spawn(caddyCmd, ['run', '--config', caddyfilePath], {
            detached: true,
            stdio: 'ignore'
          });

          caddy.unref();

          await new Promise(resolve => setTimeout(resolve, 2000));

          return {
            success: true,
            message: 'Caddy started in background',
            pid: caddy.pid
          };
        }
      }
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  /**
   * Stop Caddy
   * @returns {Promise<Object>} { success: boolean, message: string }
   */
  async stopCaddy() {
    try {
      if (this.platform === 'windows') {
        await execAsync('taskkill /IM caddy.exe /F');
      } else {
        const systemdAvailable = await this.isSystemdAvailable();

        if (systemdAvailable) {
          await execAsync('sudo systemctl stop caddy');
        } else {
          await execAsync('pkill caddy');
        }
      }

      return {
        success: true,
        message: 'Caddy stopped'
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  /**
   * Check if Caddy is running
   * @returns {Promise<Object>} { running: boolean }
   */
  async checkCaddyStatus() {
    try {
      if (this.platform === 'windows') {
        const result = await execAsync('tasklist /FI "IMAGENAME eq caddy.exe"');
        const running = result.stdout.includes('caddy.exe');

        return { running };
      } else {
        const result = await execAsync('pgrep caddy');
        return { running: result.stdout.trim().length > 0 };
      }
    } catch {
      return { running: false };
    }
  }

  /**
   * Start Docker Compose services
   * @param {string} installPath - Installation directory containing docker-compose.yml
   * @returns {Promise<Object>} { success: boolean, message: string }
   */
  async startDockerCompose(installPath) {
    try {
      const composePath = path.join(installPath, 'sites', 'dashcaddy-api', 'docker-compose.yml');

      // Check if docker-compose.yml exists
      try {
        await fs.access(composePath);
      } catch {
        return {
          success: false,
          error: 'docker-compose.yml not found'
        };
      }

      // Start containers (cwd must be the compose directory for build context)
      const composeDir = path.dirname(composePath);
      await execAsync(`docker compose -f "${composePath}" up -d --build`, {
        cwd: composeDir,
        timeout: 300000 // 5 minute timeout for initial build
      });

      return {
        success: true,
        message: 'Docker containers started'
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  /**
   * Stop Docker Compose services
   * @param {string} installPath - Installation directory containing docker-compose.yml
   * @returns {Promise<Object>} { success: boolean, message: string }
   */
  async stopDockerCompose(installPath) {
    try {
      const composePath = path.join(installPath, 'sites', 'dashcaddy-api', 'docker-compose.yml');

      await execAsync(`docker compose -f "${composePath}" down`, {
        cwd: path.dirname(composePath),
        timeout: 60000
      });

      return {
        success: true,
        message: 'Docker containers stopped'
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  /**
   * Check Docker Compose status
   * @param {string} installPath - Installation directory
   * @returns {Promise<Object>} { running: boolean, containers: array }
   */
  async checkDockerComposeStatus(installPath) {
    try {
      const composePath = path.join(installPath, 'sites', 'dashcaddy-api', 'docker-compose.yml');

      const result = await execAsync(`docker compose -f "${composePath}" ps --format json`, {
        cwd: path.dirname(composePath)
      });

      const containers = result.stdout.trim()
        .split('\n')
        .filter(line => line.trim())
        .map(line => {
          try {
            return JSON.parse(line);
          } catch {
            return null;
          }
        })
        .filter(Boolean);

      return {
        running: containers.some(c => c.State === 'running'),
        containers
      };
    } catch {
      return {
        running: false,
        containers: []
      };
    }
  }

  /**
   * Start all services
   * @param {string} installPath - Installation directory
   * @param {Object} options - Options including caddyfilePath
   * @returns {Promise<Object>} { success: boolean, services: object }
   */
  async startAll(installPath, options = {}) {
    const results = {
      caddy: null,
      docker: null
    };

    // Start Caddy
    const caddyfilePath = options.caddyfilePath || path.join(installPath, 'Caddyfile');
    results.caddy = await this.startCaddy(caddyfilePath, options.caddyBinaryPath);

    // Start Docker Compose
    results.docker = await this.startDockerCompose(installPath);

    return {
      success: results.caddy.success && results.docker.success,
      services: results
    };
  }

  /**
   * Stop all services
   * @param {string} installPath - Installation directory
   * @returns {Promise<Object>} { success: boolean, services: object }
   */
  async stopAll(installPath) {
    const results = {
      caddy: null,
      docker: null
    };

    results.caddy = await this.stopCaddy();
    results.docker = await this.stopDockerCompose(installPath);

    return {
      success: results.caddy.success && results.docker.success,
      services: results
    };
  }

  /**
   * Check if systemd is available
   * @returns {Promise<boolean>}
   */
  async isSystemdAvailable() {
    try {
      await execAsync('systemctl --version');
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Create systemd service file for Caddy
   * @param {string} caddyfilePath - Path to Caddyfile
   * @param {string} caddyBinaryPath - Path to Caddy binary
   */
  async createCaddySystemdService(caddyfilePath, caddyBinaryPath) {
    const caddyCmd = caddyBinaryPath || '/usr/bin/caddy';

    const serviceContent = `[Unit]
Description=Caddy Web Server (DashCaddy)
After=network.target network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root
ExecStart=${caddyCmd} run --config ${caddyfilePath}
ExecReload=${caddyCmd} reload --config ${caddyfilePath}
Restart=on-failure
RestartSec=5
LimitNOFILE=1048576
LimitNPROC=512

[Install]
WantedBy=multi-user.target
`;

    const servicePath = '/etc/systemd/system/caddy.service';

    // Write service file (requires sudo)
    await execAsync(`echo '${serviceContent}' | sudo tee ${servicePath}`);
  }

  /**
   * Check if Docker daemon is running
   * @returns {Promise<boolean>}
   */
  async isDockerRunning() {
    try {
      await execAsync('docker info');
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Wait for a service to become available
   * @param {string} url - URL to check
   * @param {number} timeout - Timeout in milliseconds
   * @returns {Promise<boolean>}
   */
  async waitForService(url, timeout = 30000) {
    const startTime = Date.now();
    const http = require('http');
    const https = require('https');

    while (Date.now() - startTime < timeout) {
      try {
        await new Promise((resolve, reject) => {
          const protocol = url.startsWith('https') ? https : http;
          const req = protocol.get(url, (res) => {
            resolve(res.statusCode);
          });
          req.on('error', reject);
          req.setTimeout(5000, () => {
            req.destroy();
            reject(new Error('Timeout'));
          });
        });
        return true;
      } catch {
        await new Promise(resolve => setTimeout(resolve, 1000));
      }
    }

    return false;
  }
}

module.exports = ServiceManager;
