/**
 * Systemd Service Manager for Onebox
 *
 * Handles systemd unit file installation, enabling, starting, stopping,
 * and status checking. Modeled on nupst's direct systemctl approach —
 * no external library dependencies.
 */

import { logger } from '../logging.ts';
import { getErrorMessage } from '../utils/error.ts';

const SERVICE_NAME = 'onebox';
const SERVICE_FILE_PATH = '/etc/systemd/system/onebox.service';

const SERVICE_UNIT_TEMPLATE = `[Unit]
Description=Onebox - Self-hosted container platform
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service

[Service]
Type=simple
ExecStart=/usr/local/bin/onebox systemd start-daemon
Restart=always
RestartSec=10
WorkingDirectory=/var/lib/onebox
Environment=PATH=/usr/bin:/usr/local/bin
Environment=HOME=/root
Environment=DENO_DIR=/root/.cache/deno

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

export class OneboxSystemd {
  /**
   * Install and enable the systemd service
   */
  async enable(): Promise<void> {
    try {
      // Ensure Docker is installed before writing unit file (it requires docker.service)
      await this.ensureDocker();

      // Write the unit file
      logger.info('Writing systemd unit file...');
      await Deno.writeTextFile(SERVICE_FILE_PATH, SERVICE_UNIT_TEMPLATE);
      logger.info(`Unit file written to ${SERVICE_FILE_PATH}`);

      // Reload systemd daemon
      await this.runSystemctl(['daemon-reload']);

      // Enable the service
      const result = await this.runSystemctl(['enable', `${SERVICE_NAME}.service`]);
      if (!result.success) {
        throw new Error(`Failed to enable service: ${result.stderr}`);
      }

      logger.success('Onebox systemd service enabled');
      logger.info('Start with: onebox systemd start');
    } catch (error) {
      logger.error(`Failed to enable service: ${getErrorMessage(error)}`);
      throw error;
    }
  }

  /**
   * Stop, disable, and remove the systemd service
   */
  async disable(): Promise<void> {
    try {
      // Stop the service (ignore errors if not running)
      await this.runSystemctl(['stop', `${SERVICE_NAME}.service`]);

      // Disable the service
      await this.runSystemctl(['disable', `${SERVICE_NAME}.service`]);

      // Remove the unit file
      try {
        await Deno.remove(SERVICE_FILE_PATH);
        logger.info(`Removed ${SERVICE_FILE_PATH}`);
      } catch {
        // File might not exist
      }

      // Reload systemd daemon
      await this.runSystemctl(['daemon-reload']);

      logger.success('Onebox systemd service disabled and removed');
    } catch (error) {
      logger.error(`Failed to disable service: ${getErrorMessage(error)}`);
      throw error;
    }
  }

  /**
   * Start the service via systemctl
   */
  async start(): Promise<void> {
    const result = await this.runSystemctl(['start', `${SERVICE_NAME}.service`]);
    if (!result.success) {
      logger.error(`Failed to start service: ${result.stderr}`);
      throw new Error(`Failed to start onebox service`);
    }
    logger.success('Onebox service started');
  }

  /**
   * Stop the service via systemctl
   */
  async stop(): Promise<void> {
    const result = await this.runSystemctl(['stop', `${SERVICE_NAME}.service`]);
    if (!result.success) {
      logger.error(`Failed to stop service: ${result.stderr}`);
      throw new Error(`Failed to stop onebox service`);
    }
    logger.success('Onebox service stopped');
  }

  /**
   * Get and display service status
   */
  async getStatus(): Promise<string> {
    const result = await this.runSystemctl(['status', `${SERVICE_NAME}.service`]);
    const output = result.stdout;

    let status: string;
    if (output.includes('active (running)')) {
      status = 'running';
    } else if (output.includes('inactive') || output.includes('dead')) {
      status = 'stopped';
    } else if (output.includes('failed')) {
      status = 'failed';
    } else if (!result.success && result.stderr.includes('could not be found')) {
      status = 'not-installed';
    } else {
      status = 'unknown';
    }

    // Print the raw systemctl output for full details
    if (output.trim()) {
      console.log(output);
    }

    return status;
  }

  /**
   * Show service logs via journalctl
   */
  async showLogs(): Promise<void> {
    const cmd = new Deno.Command('journalctl', {
      args: ['-u', `${SERVICE_NAME}.service`, '-f'],
      stdout: 'inherit',
      stderr: 'inherit',
    });
    await cmd.output();
  }

  /**
   * Check if the service unit file is installed
   */
  async isInstalled(): Promise<boolean> {
    try {
      await Deno.stat(SERVICE_FILE_PATH);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Ensure Docker is installed, installing it if necessary
   */
  private async ensureDocker(): Promise<void> {
    try {
      const cmd = new Deno.Command('docker', {
        args: ['--version'],
        stdout: 'piped',
        stderr: 'piped',
      });
      const result = await cmd.output();
      if (result.success) {
        const version = new TextDecoder().decode(result.stdout).trim();
        logger.info(`Docker found: ${version}`);
        return;
      }
    } catch {
      // docker command not found
    }

    logger.info('Docker not found. Installing Docker...');
    const installCmd = new Deno.Command('bash', {
      args: ['-c', 'curl -fsSL https://get.docker.com | sh'],
      stdin: 'inherit',
      stdout: 'inherit',
      stderr: 'inherit',
    });
    const installResult = await installCmd.output();
    if (!installResult.success) {
      throw new Error('Failed to install Docker. Please install it manually: curl -fsSL https://get.docker.com | sh');
    }
    logger.success('Docker installed successfully');

    // Initialize Docker Swarm
    logger.info('Initializing Docker Swarm...');
    const swarmCmd = new Deno.Command('docker', {
      args: ['swarm', 'init'],
      stdout: 'piped',
      stderr: 'piped',
    });
    const swarmResult = await swarmCmd.output();
    if (swarmResult.success) {
      logger.success('Docker Swarm initialized');
    } else {
      const stderr = new TextDecoder().decode(swarmResult.stderr);
      if (stderr.includes('already part of a swarm')) {
        logger.info('Docker Swarm already initialized');
      } else {
        logger.warn(`Docker Swarm init warning: ${stderr.trim()}`);
      }
    }
  }

  /**
   * Run a systemctl command and return results
   */
  private async runSystemctl(
    args: string[]
  ): Promise<{ success: boolean; stdout: string; stderr: string }> {
    const cmd = new Deno.Command('systemctl', {
      args,
      stdout: 'piped',
      stderr: 'piped',
    });

    const result = await cmd.output();
    return {
      success: result.success,
      stdout: new TextDecoder().decode(result.stdout),
      stderr: new TextDecoder().decode(result.stderr),
    };
  }
}
