import * as plugins from '../../plugins.js';

/**
 * WrappedSocket wraps a regular net.Socket to provide transparent access
 * to the real client IP and port when behind a proxy using PROXY protocol.
 * 
 * This is the FOUNDATION for all PROXY protocol support and must be implemented
 * before any protocol parsing can occur.
 * 
 * This implementation uses a Proxy to delegate all properties and methods
 * to the underlying socket while allowing override of specific properties.
 */
export class WrappedSocket {
  public readonly socket: plugins.net.Socket;
  private realClientIP?: string;
  private realClientPort?: number;
  
  // Make TypeScript happy by declaring the Socket methods that will be proxied
  [key: string]: any;
  
  constructor(
    socket: plugins.net.Socket,
    realClientIP?: string,
    realClientPort?: number
  ) {
    this.socket = socket;
    this.realClientIP = realClientIP;
    this.realClientPort = realClientPort;
    
    // Create a proxy that delegates everything to the underlying socket
    return new Proxy(this, {
      get(target, prop, receiver) {
        // Override specific properties
        if (prop === 'remoteAddress') {
          return target.remoteAddress;
        }
        if (prop === 'remotePort') {
          return target.remotePort;
        }
        if (prop === 'socket') {
          return target.socket;
        }
        if (prop === 'realClientIP') {
          return target.realClientIP;
        }
        if (prop === 'realClientPort') {
          return target.realClientPort;
        }
        if (prop === 'isFromTrustedProxy') {
          return target.isFromTrustedProxy;
        }
        if (prop === 'setProxyInfo') {
          return target.setProxyInfo.bind(target);
        }
        if (prop === 'remoteFamily') {
          return target.remoteFamily;
        }
        
        // For all other properties/methods, delegate to the underlying socket
        const value = target.socket[prop as keyof plugins.net.Socket];
        if (typeof value === 'function') {
          return value.bind(target.socket);
        }
        return value;
      },
      set(target, prop, value) {
        // Set on the underlying socket
        (target.socket as any)[prop] = value;
        return true;
      }
    }) as any;
  }
  
  /**
   * Returns the real client IP if available, otherwise the socket's remote address
   */
  get remoteAddress(): string | undefined {
    return this.realClientIP || this.socket.remoteAddress;
  }
  
  /**
   * Returns the real client port if available, otherwise the socket's remote port
   */
  get remotePort(): number | undefined {
    return this.realClientPort || this.socket.remotePort;
  }
  
  /**
   * Indicates if this connection came through a trusted proxy
   */
  get isFromTrustedProxy(): boolean {
    return !!this.realClientIP;
  }
  
  /**
   * Returns the address family of the remote IP
   */
  get remoteFamily(): string | undefined {
    const ip = this.realClientIP || this.socket.remoteAddress;
    if (!ip) return undefined;
    
    // Check if it's IPv6
    if (ip.includes(':')) {
      return 'IPv6';
    }
    // Otherwise assume IPv4
    return 'IPv4';
  }
  
  /**
   * Updates the real client information (called after parsing PROXY protocol)
   */
  setProxyInfo(ip: string, port: number): void {
    this.realClientIP = ip;
    this.realClientPort = port;
  }
}