import * as plugins from './plugins.js';
import { NetworkProxy } from './classes.networkproxy.js';
import type { IConnectionRecord, IPortProxySettings, IDomainConfig } from './classes.pp.interfaces.js';

/**
 * Manages NetworkProxy integration for TLS termination
 */
export class NetworkProxyBridge {
  private networkProxy: NetworkProxy | null = null;
  
  constructor(private settings: IPortProxySettings) {}
  
  /**
   * Initialize NetworkProxy instance
   */
  public async initialize(): Promise<void> {
    if (!this.networkProxy && this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) {
      // Configure NetworkProxy options based on PortProxy settings
      const networkProxyOptions: any = {
        port: this.settings.networkProxyPort!,
        portProxyIntegration: true,
        logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info',
      };

      // Add ACME settings if configured
      if (this.settings.acme) {
        networkProxyOptions.acme = { ...this.settings.acme };
      }

      this.networkProxy = new NetworkProxy(networkProxyOptions);

      console.log(`Initialized NetworkProxy on port ${this.settings.networkProxyPort}`);

      // Convert and apply domain configurations to NetworkProxy
      await this.syncDomainConfigsToNetworkProxy();
    }
  }
  
  /**
   * Get the NetworkProxy instance
   */
  public getNetworkProxy(): NetworkProxy | null {
    return this.networkProxy;
  }
  
  /**
   * Get the NetworkProxy port
   */
  public getNetworkProxyPort(): number {
    return this.networkProxy ? this.networkProxy.getListeningPort() : this.settings.networkProxyPort || 8443;
  }
  
  /**
   * Start NetworkProxy
   */
  public async start(): Promise<void> {
    if (this.networkProxy) {
      await this.networkProxy.start();
      console.log(`NetworkProxy started on port ${this.settings.networkProxyPort}`);

      // Log ACME status
      if (this.settings.acme?.enabled) {
        console.log(
          `ACME certificate management is enabled (${
            this.settings.acme.useProduction ? 'Production' : 'Staging'
          } mode)`
        );
        console.log(`ACME HTTP challenge server on port ${this.settings.acme.port}`);

        // Register domains for ACME certificates if enabled
        if (this.networkProxy.options.acme?.enabled) {
          console.log('Registering domains with ACME certificate manager...');
          // The NetworkProxy will handle this internally via registerDomainsWithAcmeManager()
        }
      }
    }
  }
  
  /**
   * Stop NetworkProxy
   */
  public async stop(): Promise<void> {
    if (this.networkProxy) {
      try {
        console.log('Stopping NetworkProxy...');
        await this.networkProxy.stop();
        console.log('NetworkProxy stopped successfully');

        // Log ACME shutdown if it was enabled
        if (this.settings.acme?.enabled) {
          console.log('ACME certificate manager stopped');
        }
      } catch (err) {
        console.log(`Error stopping NetworkProxy: ${err}`);
      }
    }
  }
  
  /**
   * Forwards a TLS connection to a NetworkProxy for handling
   */
  public forwardToNetworkProxy(
    connectionId: string,
    socket: plugins.net.Socket,
    record: IConnectionRecord,
    initialData: Buffer,
    customProxyPort?: number,
    onError?: (reason: string) => void
  ): void {
    // Ensure NetworkProxy is initialized
    if (!this.networkProxy) {
      console.log(
        `[${connectionId}] NetworkProxy not initialized. Cannot forward connection.`
      );
      if (onError) {
        onError('network_proxy_not_initialized');
      }
      return;
    }

    // Use the custom port if provided, otherwise use the default NetworkProxy port
    const proxyPort = customProxyPort || this.networkProxy.getListeningPort();
    const proxyHost = 'localhost'; // Assuming NetworkProxy runs locally

    if (this.settings.enableDetailedLogging) {
      console.log(
        `[${connectionId}] Forwarding TLS connection to NetworkProxy at ${proxyHost}:${proxyPort}`
      );
    }

    // Create a connection to the NetworkProxy
    const proxySocket = plugins.net.connect({
      host: proxyHost,
      port: proxyPort,
    });

    // Store the outgoing socket in the record
    record.outgoing = proxySocket;
    record.outgoingStartTime = Date.now();
    record.usingNetworkProxy = true;

    // Set up error handlers
    proxySocket.on('error', (err) => {
      console.log(`[${connectionId}] Error connecting to NetworkProxy: ${err.message}`);
      if (onError) {
        onError('network_proxy_connect_error');
      }
    });

    // Handle connection to NetworkProxy
    proxySocket.on('connect', () => {
      if (this.settings.enableDetailedLogging) {
        console.log(`[${connectionId}] Connected to NetworkProxy at ${proxyHost}:${proxyPort}`);
      }

      // First send the initial data that contains the TLS ClientHello
      proxySocket.write(initialData);

      // Now set up bidirectional piping between client and NetworkProxy
      socket.pipe(proxySocket);
      proxySocket.pipe(socket);

      // Update activity on data transfer (caller should handle this)
      if (this.settings.enableDetailedLogging) {
        console.log(`[${connectionId}] TLS connection successfully forwarded to NetworkProxy`);
      }
    });
  }
  
  /**
   * Synchronizes domain configurations to NetworkProxy
   */
  public async syncDomainConfigsToNetworkProxy(): Promise<void> {
    if (!this.networkProxy) {
      console.log('Cannot sync configurations - NetworkProxy not initialized');
      return;
    }

    try {
      // Get SSL certificates from assets
      // Import fs directly since it's not in plugins
      const fs = await import('fs');

      let certPair;
      try {
        certPair = {
          key: fs.readFileSync('assets/certs/key.pem', 'utf8'),
          cert: fs.readFileSync('assets/certs/cert.pem', 'utf8'),
        };
      } catch (certError) {
        console.log(`Warning: Could not read default certificates: ${certError}`);
        console.log(
          'Using empty certificate placeholders - ACME will generate proper certificates if enabled'
        );

        // Use empty placeholders - NetworkProxy will use its internal defaults
        // or ACME will generate proper ones if enabled
        certPair = {
          key: '',
          cert: '',
        };
      }

      // Convert domain configs to NetworkProxy configs
      const proxyConfigs = this.networkProxy.convertPortProxyConfigs(
        this.settings.domainConfigs,
        certPair
      );

      // Log ACME-eligible domains if ACME is enabled
      if (this.settings.acme?.enabled) {
        const acmeEligibleDomains = proxyConfigs
          .filter((config) => !config.hostName.includes('*')) // Exclude wildcards
          .map((config) => config.hostName);

        if (acmeEligibleDomains.length > 0) {
          console.log(`Domains eligible for ACME certificates: ${acmeEligibleDomains.join(', ')}`);
        } else {
          console.log('No domains eligible for ACME certificates found in configuration');
        }
      }

      // Update NetworkProxy with the converted configs
      await this.networkProxy.updateProxyConfigs(proxyConfigs);
      console.log(`Successfully synchronized ${proxyConfigs.length} domain configurations to NetworkProxy`);
    } catch (err) {
      console.log(`Failed to sync configurations: ${err}`);
    }
  }
  
  /**
   * Request a certificate for a specific domain
   */
  public async requestCertificate(domain: string): Promise<boolean> {
    if (!this.networkProxy) {
      console.log('Cannot request certificate - NetworkProxy not initialized');
      return false;
    }

    if (!this.settings.acme?.enabled) {
      console.log('Cannot request certificate - ACME is not enabled');
      return false;
    }

    try {
      const result = await this.networkProxy.requestCertificate(domain);
      if (result) {
        console.log(`Certificate request for ${domain} submitted successfully`);
      } else {
        console.log(`Certificate request for ${domain} failed`);
      }
      return result;
    } catch (err) {
      console.log(`Error requesting certificate: ${err}`);
      return false;
    }
  }
}