import * as plugins from '../../plugins.js';
import '../../core/models/socket-augmentation.js';
import type { IRouteContext, IHttpRouteContext, IHttp2RouteContext } from '../../core/models/route-context.js';

/**
 * Context creator for NetworkProxy
 * Creates route contexts for matching and function evaluation
 */
export class ContextCreator {
  /**
   * Create a route context from HTTP request information
   */
  public createHttpRouteContext(req: any, options: {
    tlsVersion?: string;
    connectionId: string;
    clientIp: string;
    serverIp: string;
  }): IHttpRouteContext {
    // Parse headers
    const headers: Record<string, string> = {};
    for (const [key, value] of Object.entries(req.headers)) {
      if (typeof value === 'string') {
        headers[key.toLowerCase()] = value;
      } else if (Array.isArray(value) && value.length > 0) {
        headers[key.toLowerCase()] = value[0];
      }
    }

    // Parse domain from Host header
    const domain = headers['host']?.split(':')[0] || '';

    // Parse URL
    const url = new URL(`http://${domain}${req.url || '/'}`);

    return {
      // Connection basics
      port: req.socket.localPort || 0,
      domain,
      clientIp: options.clientIp,
      serverIp: options.serverIp,

      // HTTP specifics
      path: url.pathname,
      query: url.search ? url.search.substring(1) : '',
      headers,

      // TLS information
      isTls: !!req.socket.encrypted,
      tlsVersion: options.tlsVersion,

      // Request objects
      req,

      // Metadata
      timestamp: Date.now(),
      connectionId: options.connectionId
    };
  }

  /**
   * Create a route context from HTTP/2 stream and headers
   */
  public createHttp2RouteContext(
    stream: plugins.http2.ServerHttp2Stream,
    headers: plugins.http2.IncomingHttpHeaders,
    options: {
      connectionId: string;
      clientIp: string;
      serverIp: string;
    }
  ): IHttp2RouteContext {
    // Parse headers, excluding HTTP/2 pseudo-headers
    const processedHeaders: Record<string, string> = {};
    for (const [key, value] of Object.entries(headers)) {
      if (!key.startsWith(':') && typeof value === 'string') {
        processedHeaders[key.toLowerCase()] = value;
      }
    }

    // Get domain from :authority pseudo-header
    const authority = headers[':authority'] as string || '';
    const domain = authority.split(':')[0];

    // Get path from :path pseudo-header
    const path = headers[':path'] as string || '/';

    // Parse the path to extract query string
    const pathParts = path.split('?');
    const pathname = pathParts[0];
    const query = pathParts.length > 1 ? pathParts[1] : '';

    // Get the socket from the session
    const socket = (stream.session as any)?.socket;

    return {
      // Connection basics
      port: socket?.localPort || 0,
      domain,
      clientIp: options.clientIp,
      serverIp: options.serverIp,

      // HTTP specifics
      path: pathname,
      query,
      headers: processedHeaders,

      // HTTP/2 specific properties
      method: headers[':method'] as string,
      stream,

      // TLS information - HTTP/2 is always on TLS in browsers
      isTls: true,
      tlsVersion: socket?.getTLSVersion?.() || 'TLSv1.3',

      // Metadata
      timestamp: Date.now(),
      connectionId: options.connectionId
    };
  }

  /**
   * Create a basic route context from socket information
   */
  public createSocketRouteContext(socket: plugins.net.Socket, options: {
    domain?: string;
    tlsVersion?: string;
    connectionId: string;
  }): IRouteContext {
    return {
      // Connection basics
      port: socket.localPort || 0,
      domain: options.domain,
      clientIp: socket.remoteAddress?.replace('::ffff:', '') || '0.0.0.0',
      serverIp: socket.localAddress?.replace('::ffff:', '') || '0.0.0.0',

      // TLS information
      isTls: options.tlsVersion !== undefined,
      tlsVersion: options.tlsVersion,

      // Metadata
      timestamp: Date.now(),
      connectionId: options.connectionId
    };
  }
}