import { promises as fs } from 'fs';
import { execSync } from 'child_process';
import { NupstDaemon } from './daemon.js';

/**
 * Class for managing systemd service
 * Handles installation, removal, and control of the NUPST systemd service
 */
export class NupstSystemd {
  /** Path to the systemd service file */
  private readonly serviceFilePath = '/etc/systemd/system/nupst.service';
  private readonly daemon: NupstDaemon;

  /** Template for the systemd service file */
  private readonly serviceTemplate = `[Unit]
Description=Node.js UPS Shutdown Tool
After=network.target

[Service]
ExecStart=/opt/nupst/bin/nupst daemon-start
Restart=always
User=root
Group=root
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/tmp

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

  /**
   * Create a new systemd manager
   * @param daemon The daemon instance to manage
   */
  constructor(daemon: NupstDaemon) {
    this.daemon = daemon;
  }

  /**
   * Check if a configuration file exists
   * @private
   * @throws Error if configuration not found
   */
  private async checkConfigExists(): Promise<void> {
    const configPath = '/etc/nupst/config.json';
    try {
      await fs.access(configPath);
    } catch (error) {
      console.error('┌─ Configuration Error ─────────────────────┐');
      console.error(`│ No configuration file found at ${configPath}`);
      console.error('│ Please run \'nupst setup\' first to create a configuration.');
      console.error('└──────────────────────────────────────────┘');
      throw new Error('Configuration not found');
    }
  }

  /**
   * Install the systemd service file
   * @throws Error if installation fails
   */
  public async install(): Promise<void> {
    try {
      // Check if configuration exists before installing
      await this.checkConfigExists();
      
      // Write the service file
      await fs.writeFile(this.serviceFilePath, this.serviceTemplate);
      console.log('┌─ Service Installation ─────────────────────┐');
      console.log(`│ Service file created at ${this.serviceFilePath}`);

      // Reload systemd daemon
      execSync('systemctl daemon-reload');
      console.log('│ Systemd daemon reloaded');

      // Enable the service
      execSync('systemctl enable nupst.service');
      console.log('│ Service enabled to start on boot');
      console.log('└──────────────────────────────────────────┘');
    } catch (error) {
      if (error.message === 'Configuration not found') {
        // Just rethrow the error as the message has already been displayed
        throw error;
      }
      console.error('Failed to install systemd service:', error);
      throw error;
    }
  }

  /**
   * Start the systemd service
   * @throws Error if start fails
   */
  public async start(): Promise<void> {
    try {
      // Check if configuration exists before starting
      await this.checkConfigExists();
      
      execSync('systemctl start nupst.service');
      console.log('┌─ Service Status ─────────────────────────┐');
      console.log('│ NUPST service started successfully');
      console.log('└──────────────────────────────────────────┘');
    } catch (error) {
      if (error.message === 'Configuration not found') {
        // Exit with error code since configuration is required
        process.exit(1);
      }
      console.error('Failed to start service:', error);
      throw error;
    }
  }

  /**
   * Stop the systemd service
   * @throws Error if stop fails
   */
  public async stop(): Promise<void> {
    try {
      execSync('systemctl stop nupst.service');
      console.log('NUPST service stopped');
    } catch (error) {
      console.error('Failed to stop service:', error);
      throw error;
    }
  }

  /**
   * Get status of the systemd service and UPS
   * @param debugMode Whether to enable debug mode for SNMP
   */
  public async getStatus(debugMode: boolean = false): Promise<void> {
    try {
      // Enable debug mode if requested
      if (debugMode) {
        console.log('┌─ Debug Mode ─────────────────────────────┐');
        console.log('│ SNMP debugging enabled - detailed logs will be shown');
        console.log('└──────────────────────────────────────────┘');
        this.daemon.getNupstSnmp().enableDebug();
      }
      
      // Display version information
      this.daemon.getNupstSnmp().getNupst().logVersionInfo();
      
      // Check if config exists first
      try {
        await this.checkConfigExists();
      } catch (error) {
        // Error message already displayed by checkConfigExists
        return;
      }
      
      await this.displayServiceStatus();
      await this.displayUpsStatus();
    } catch (error) {
      console.error(`Failed to get status: ${error.message}`);
    }
  }

  /**
   * Display the systemd service status
   * @private
   */
  private async displayServiceStatus(): Promise<void> {
    try {
      const serviceStatus = execSync('systemctl status nupst.service').toString();
      console.log('┌─ Service Status ─────────────────────────┐');
      console.log(serviceStatus.split('\n').map(line => `│ ${line}`).join('\n'));
      console.log('└──────────────────────────────────────────┘');
    } catch (error) {
      console.error('┌─ Service Status ─────────────────────────┐');
      console.error('│ Service is not running');
      console.error('└──────────────────────────────────────────┘');
    }
  }

  /**
   * Display the UPS status
   * @private
   */
  private async displayUpsStatus(): Promise<void> {
    try {
      // Explicitly load the configuration first to ensure it's up-to-date
      await this.daemon.loadConfig();
      const config = this.daemon.getConfig();
      const snmp = this.daemon.getNupstSnmp();
      
      // Create a test config with appropriate timeout, similar to the test command
      const snmpConfig = { 
        ...config.snmp,
        timeout: Math.min(config.snmp.timeout, 10000) // Use at most 10 seconds for status check
      };
      
      console.log('┌─ Connecting to UPS... ────────────────────┐');
      console.log(`│ Host: ${config.snmp.host}:${config.snmp.port}`);
      console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`);
      console.log('└──────────────────────────────────────────┘');
      
      const status = await snmp.getUpsStatus(snmpConfig);
      
      console.log('┌─ UPS Status ───────────────────────────────┐');
      console.log(`│ Power Status: ${status.powerStatus}`);
      console.log(`│ Battery Capacity: ${status.batteryCapacity}%`);
      console.log(`│ Runtime Remaining: ${status.batteryRuntime} minutes`);
      console.log('└──────────────────────────────────────────┘');
    } catch (error) {
      console.error('┌─ UPS Status ───────────────────────────────┐');
      console.error(`│ Failed to retrieve UPS status: ${error.message}`);
      console.error('└──────────────────────────────────────────┘');
    }
  }

  /**
   * Disable and uninstall the systemd service
   * @throws Error if disabling fails
   */
  public async disable(): Promise<void> {
    try {
      await this.stopService();
      await this.disableService();
      await this.removeServiceFile();
      
      // Reload systemd daemon
      execSync('systemctl daemon-reload');
      console.log('Systemd daemon reloaded');
      console.log('NUPST service has been successfully uninstalled');
    } catch (error) {
      console.error('Failed to disable and uninstall service:', error);
      throw error;
    }
  }

  /**
   * Stop the service if it's running
   * @private
   */
  private async stopService(): Promise<void> {
    try {
      console.log('Stopping NUPST service...');
      execSync('systemctl stop nupst.service');
    } catch (error) {
      // Service might not be running, that's okay
      console.log('Service was not running or could not be stopped');
    }
  }

  /**
   * Disable the service
   * @private
   */
  private async disableService(): Promise<void> {
    try {
      console.log('Disabling NUPST service...');
      execSync('systemctl disable nupst.service');
    } catch (error) {
      console.log('Service was not enabled or could not be disabled');
    }
  }

  /**
   * Remove the service file if it exists
   * @private
   */
  private async removeServiceFile(): Promise<void> {
    if (await fs.stat(this.serviceFilePath).catch(() => null)) {
      console.log(`Removing service file ${this.serviceFilePath}...`);
      await fs.unlink(this.serviceFilePath);
      console.log('Service file removed');
    } else {
      console.log('Service file did not exist');
    }
  }
}