import * as plugins from '../../plugins.js';
import { HttpProxy } from '../http-proxy/index.js';
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
import type { IRouteConfig } from './models/route-types.js';

export class HttpProxyBridge {
  private httpProxy: HttpProxy | null = null;

  constructor(private settings: ISmartProxyOptions) {}
  
  /**
   * Get the HttpProxy instance
   */
  public getHttpProxy(): HttpProxy | null {
    return this.httpProxy;
  }
  
  /**
   * Initialize HttpProxy instance
   */
  public async initialize(): Promise<void> {
    if (!this.httpProxy && this.settings.useHttpProxy && this.settings.useHttpProxy.length > 0) {
      const httpProxyOptions: any = {
        port: this.settings.httpProxyPort!,
        portProxyIntegration: true,
        logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info'
      };

      this.httpProxy = new HttpProxy(httpProxyOptions);
      console.log(`Initialized HttpProxy on port ${this.settings.httpProxyPort}`);

      // Apply route configurations to HttpProxy
      await this.syncRoutesToHttpProxy(this.settings.routes || []);
    }
  }
  
  /**
   * Sync routes to HttpProxy
   */
  public async syncRoutesToHttpProxy(routes: IRouteConfig[]): Promise<void> {
    if (!this.httpProxy) return;
    
    // Convert routes to HttpProxy format
    const httpProxyConfigs = routes
      .filter(route => {
        // Check if this route matches any of the specified network proxy ports
        const routePorts = Array.isArray(route.match.ports) 
          ? route.match.ports 
          : [route.match.ports];
        
        return routePorts.some(port => 
          this.settings.useHttpProxy?.includes(port)
        );
      })
      .map(route => this.routeToHttpProxyConfig(route));
    
    // Apply configurations to HttpProxy
    await this.httpProxy.updateRouteConfigs(httpProxyConfigs);
  }
  
  /**
   * Convert route to HttpProxy configuration
   */
  private routeToHttpProxyConfig(route: IRouteConfig): any {
    // Convert route to HttpProxy domain config format
    let domain = '*';
    if (route.match.domains) {
      if (Array.isArray(route.match.domains)) {
        domain = route.match.domains[0] || '*';
      } else {
        domain = route.match.domains;
      }
    }
    
    return {
      domain,
      target: route.action.target,
      tls: route.action.tls,
      security: route.action.security,
      match: {
        ...route.match,
        domains: domain  // Ensure domains is always set for HttpProxy
      }
    };
  }
  
  /**
   * Check if connection should use HttpProxy
   */
  public shouldUseHttpProxy(connection: IConnectionRecord, routeMatch: any): boolean {
    // Only use HttpProxy for TLS termination
    return (
      routeMatch.route.action.tls?.mode === 'terminate' ||
      routeMatch.route.action.tls?.mode === 'terminate-and-reencrypt'
    ) && this.httpProxy !== null;
  }
  
  /**
   * Forward connection to HttpProxy
   */
  public async forwardToHttpProxy(
    connectionId: string,
    socket: plugins.net.Socket,
    record: IConnectionRecord,
    initialChunk: Buffer,
    httpProxyPort: number,
    cleanupCallback: (reason: string) => void
  ): Promise<void> {
    if (!this.httpProxy) {
      throw new Error('HttpProxy not initialized');
    }
    
    const proxySocket = new plugins.net.Socket();
    
    await new Promise<void>((resolve, reject) => {
      proxySocket.connect(httpProxyPort, 'localhost', () => {
        console.log(`[${connectionId}] Connected to HttpProxy for termination`);
        resolve();
      });
      
      proxySocket.on('error', reject);
    });
    
    // Send initial chunk if present
    if (initialChunk) {
      proxySocket.write(initialChunk);
    }
    
    // Pipe the sockets together
    socket.pipe(proxySocket);
    proxySocket.pipe(socket);
    
    // Handle cleanup
    const cleanup = (reason: string) => {
      socket.unpipe(proxySocket);
      proxySocket.unpipe(socket);
      proxySocket.destroy();
      cleanupCallback(reason);
    };
    
    socket.on('end', () => cleanup('socket_end'));
    socket.on('error', () => cleanup('socket_error'));
    proxySocket.on('end', () => cleanup('proxy_end'));
    proxySocket.on('error', () => cleanup('proxy_error'));
  }
  
  /**
   * Start HttpProxy
   */
  public async start(): Promise<void> {
    if (this.httpProxy) {
      await this.httpProxy.start();
    }
  }
  
  /**
   * Stop HttpProxy
   */
  public async stop(): Promise<void> {
    if (this.httpProxy) {
      await this.httpProxy.stop();
      this.httpProxy = null;
    }
  }
}