import { NupstSnmp } from './snmp/manager.js';
import { NupstDaemon } from './daemon.js';
import { NupstSystemd } from './systemd.js';
import { commitinfo } from './00_commitinfo_data.js';
import { spawn } from 'child_process';
import * as https from 'https';
import { logger } from './logger.js';

/**
 * Main Nupst class that coordinates all components
 * Acts as a facade to access SNMP, Daemon, and Systemd functionality
 */
export class Nupst {
  private readonly snmp: NupstSnmp;
  private readonly daemon: NupstDaemon;
  private readonly systemd: NupstSystemd;
  private updateAvailable: boolean = false;
  private latestVersion: string = '';

  /**
   * Create a new Nupst instance with all necessary components
   */
  constructor() {
    this.snmp = new NupstSnmp();
    this.snmp.setNupst(this); // Set up bidirectional reference
    this.daemon = new NupstDaemon(this.snmp);
    this.systemd = new NupstSystemd(this.daemon);
  }

  /**
   * Get the SNMP manager for UPS communication
   */
  public getSnmp(): NupstSnmp {
    return this.snmp;
  }

  /**
   * Get the daemon manager for background monitoring
   */
  public getDaemon(): NupstDaemon {
    return this.daemon;
  }

  /**
   * Get the systemd manager for service operations
   */
  public getSystemd(): NupstSystemd {
    return this.systemd;
  }
  
  /**
   * Get the current version of NUPST
   * @returns The current version string
   */
  public getVersion(): string {
    return commitinfo.version;
  }
  
  /**
   * Check if an update is available
   * @returns Promise resolving to true if an update is available
   */
  public async checkForUpdates(): Promise<boolean> {
    try {
      const latestVersion = await this.getLatestVersion();
      const currentVersion = this.getVersion();
      
      // Compare versions
      this.updateAvailable = this.compareVersions(latestVersion, currentVersion) > 0;
      this.latestVersion = latestVersion;
      
      return this.updateAvailable;
    } catch (error) {
      logger.error(`Error checking for updates: ${error.message}`);
      return false;
    }
  }
  
  /**
   * Get update status information
   * @returns Object with update status information
   */
  public getUpdateStatus(): { 
    currentVersion: string, 
    latestVersion: string, 
    updateAvailable: boolean 
  } {
    return {
      currentVersion: this.getVersion(),
      latestVersion: this.latestVersion || this.getVersion(),
      updateAvailable: this.updateAvailable
    };
  }
  
  /**
   * Get the latest version from npm registry
   * @returns Promise resolving to the latest version string
   */
  private async getLatestVersion(): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      const options = {
        hostname: 'registry.npmjs.org',
        path: '/@serve.zone/nupst',
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'User-Agent': `nupst/${this.getVersion()}`
        }
      };
      
      const req = https.request(options, (res) => {
        let data = '';
        
        res.on('data', (chunk) => {
          data += chunk;
        });
        
        res.on('end', () => {
          try {
            const response = JSON.parse(data);
            if (response['dist-tags'] && response['dist-tags'].latest) {
              resolve(response['dist-tags'].latest);
            } else {
              reject(new Error('Failed to parse version from npm registry response'));
            }
          } catch (error) {
            reject(error);
          }
        });
      });
      
      req.on('error', (error) => {
        reject(error);
      });
      
      req.end();
    });
  }
  
  /**
   * Compare two semantic version strings
   * @param versionA First version
   * @param versionB Second version
   * @returns -1 if versionA < versionB, 0 if equal, 1 if versionA > versionB
   */
  private compareVersions(versionA: string, versionB: string): number {
    const partsA = versionA.split('.').map(part => parseInt(part, 10));
    const partsB = versionB.split('.').map(part => parseInt(part, 10));
    
    for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
      const partA = i < partsA.length ? partsA[i] : 0;
      const partB = i < partsB.length ? partsB[i] : 0;
      
      if (partA > partB) return 1;
      if (partA < partB) return -1;
    }
    
    return 0; // Versions are equal
  }
  
  /**
   * Log the current version and update status
   */
  public logVersionInfo(checkForUpdates: boolean = true): void {
    const version = this.getVersion();
    const boxWidth = 45;
    
    logger.logBoxTitle('NUPST Version', boxWidth);
    logger.logBoxLine(`Current Version: ${version}`);
    
    if (this.updateAvailable && this.latestVersion) {
      logger.logBoxLine(`Update Available: ${this.latestVersion}`);
      logger.logBoxLine('Run "sudo nupst update" to update');
      logger.logBoxEnd();
    } else if (checkForUpdates) {
      logger.logBoxLine('Checking for updates...');
      
      // We can't end the box yet since we're in an async operation
      this.checkForUpdates().then(updateAvailable => {
        if (updateAvailable) {
          logger.logBoxLine(`Update Available: ${this.latestVersion}`);
          logger.logBoxLine('Run "sudo nupst update" to update');
        } else {
          logger.logBoxLine('You are running the latest version');
        }
        logger.logBoxEnd();
      }).catch(() => {
        logger.logBoxLine('Could not check for updates');
        logger.logBoxEnd();
      });
    } else {
      logger.logBoxEnd();
    }
  }
}