import { Buffer } from 'node:buffer';
import { 
  TlsRecordType,
  TlsHandshakeType,
  TlsExtensionType,
  TlsUtils
} from '../../protocols/tls/utils/tls-utils.js';
import {
  ClientHelloParser,
  type LoggerFunction
} from '../../protocols/tls/sni/client-hello-parser.js';
import {
  SniExtraction,
  type ConnectionInfo
} from '../../protocols/tls/sni/sni-extraction.js';

/**
 * SNI (Server Name Indication) handler for TLS connections.
 * Provides robust extraction of SNI values from TLS ClientHello messages
 * with support for fragmented packets, TLS 1.3 resumption, Chrome-specific
 * connection behaviors, and tab hibernation/reactivation scenarios.
 * 
 * This class retains the original API but leverages the new modular implementation
 * for better maintainability and testability.
 */
export class SniHandler {
  // Re-export constants for backward compatibility
  private static readonly TLS_HANDSHAKE_RECORD_TYPE = TlsRecordType.HANDSHAKE;
  private static readonly TLS_APPLICATION_DATA_TYPE = TlsRecordType.APPLICATION_DATA;
  private static readonly TLS_CLIENT_HELLO_HANDSHAKE_TYPE = TlsHandshakeType.CLIENT_HELLO;
  private static readonly TLS_SNI_EXTENSION_TYPE = TlsExtensionType.SERVER_NAME;
  private static readonly TLS_SESSION_TICKET_EXTENSION_TYPE = TlsExtensionType.SESSION_TICKET;
  private static readonly TLS_SNI_HOST_NAME_TYPE = 0; // NameType.HOST_NAME in RFC 6066
  private static readonly TLS_PSK_EXTENSION_TYPE = TlsExtensionType.PRE_SHARED_KEY;
  private static readonly TLS_PSK_KE_MODES_EXTENSION_TYPE = TlsExtensionType.PSK_KEY_EXCHANGE_MODES;
  private static readonly TLS_EARLY_DATA_EXTENSION_TYPE = TlsExtensionType.EARLY_DATA;

  /**
   * Checks if a buffer contains a TLS handshake message (record type 22)
   * @param buffer - The buffer to check
   * @returns true if the buffer starts with a TLS handshake record type
   */
  public static isTlsHandshake(buffer: Buffer): boolean {
    return TlsUtils.isTlsHandshake(buffer);
  }

  /**
   * Checks if a buffer contains TLS application data (record type 23)
   * @param buffer - The buffer to check
   * @returns true if the buffer starts with a TLS application data record type
   */
  public static isTlsApplicationData(buffer: Buffer): boolean {
    return TlsUtils.isTlsApplicationData(buffer);
  }

  /**
   * Creates a connection ID based on source/destination information
   * Used to track fragmented ClientHello messages across multiple packets
   *
   * @param connectionInfo - Object containing connection identifiers (IP/port)
   * @returns A string ID for the connection
   */
  public static createConnectionId(connectionInfo: {
    sourceIp?: string;
    sourcePort?: number;
    destIp?: string;
    destPort?: number;
  }): string {
    return TlsUtils.createConnectionId(connectionInfo);
  }

  /**
   * Handles potential fragmented ClientHello messages by buffering and reassembling
   * TLS record fragments that might span multiple TCP packets.
   *
   * @param buffer - The current buffer fragment
   * @param connectionId - Unique identifier for the connection
   * @param enableLogging - Whether to enable logging
   * @returns A complete buffer if reassembly is successful, or undefined if more fragments are needed
   */
  public static handleFragmentedClientHello(
    buffer: Buffer,
    connectionId: string,
    enableLogging: boolean = false
  ): Buffer | undefined {
    const logger = enableLogging ? 
      (message: string) => console.log(`[SNI Fragment] ${message}`) : 
      undefined;
    
    return ClientHelloParser.handleFragmentedClientHello(buffer, connectionId, logger);
  }

  /**
   * Checks if a buffer contains a TLS ClientHello message
   * @param buffer - The buffer to check
   * @returns true if the buffer appears to be a ClientHello message
   */
  public static isClientHello(buffer: Buffer): boolean {
    return TlsUtils.isClientHello(buffer);
  }

  /**
   * Checks if a ClientHello message contains session resumption indicators
   * such as session tickets or PSK (Pre-Shared Key) extensions.
   *
   * @param buffer - The buffer containing a ClientHello message
   * @param enableLogging - Whether to enable logging
   * @returns Object containing details about session resumption and SNI presence
   */
  public static hasSessionResumption(
    buffer: Buffer,
    enableLogging: boolean = false
  ): { isResumption: boolean; hasSNI: boolean } {
    const logger = enableLogging ? 
      (message: string) => console.log(`[Session Resumption] ${message}`) : 
      undefined;
    
    return ClientHelloParser.hasSessionResumption(buffer, logger);
  }

  /**
   * Detects characteristics of a tab reactivation TLS handshake
   * These often have specific patterns in Chrome and other browsers
   *
   * @param buffer - The buffer containing a ClientHello message
   * @param enableLogging - Whether to enable logging
   * @returns true if this appears to be a tab reactivation handshake
   */
  public static isTabReactivationHandshake(
    buffer: Buffer,
    enableLogging: boolean = false
  ): boolean {
    const logger = enableLogging ? 
      (message: string) => console.log(`[Tab Reactivation] ${message}`) : 
      undefined;
    
    return ClientHelloParser.isTabReactivationHandshake(buffer, logger);
  }

  /**
   * Extracts the SNI (Server Name Indication) from a TLS ClientHello message.
   * Implements robust parsing with support for session resumption edge cases.
   *
   * @param buffer - The buffer containing the TLS ClientHello message
   * @param enableLogging - Whether to enable detailed debug logging
   * @returns The extracted server name or undefined if not found
   */
  public static extractSNI(buffer: Buffer, enableLogging: boolean = false): string | undefined {
    const logger = enableLogging ? 
      (message: string) => console.log(`[SNI Extraction] ${message}`) : 
      undefined;
    
    return SniExtraction.extractSNI(buffer, logger);
  }

  /**
   * Attempts to extract SNI from the PSK extension in a TLS 1.3 ClientHello.
   *
   * In TLS 1.3, when a client attempts to resume a session, it may include
   * the server name in the PSK identity hint rather than in the SNI extension.
   *
   * @param buffer - The buffer containing the TLS ClientHello message
   * @param enableLogging - Whether to enable detailed debug logging
   * @returns The extracted server name or undefined if not found
   */
  public static extractSNIFromPSKExtension(
    buffer: Buffer,
    enableLogging: boolean = false
  ): string | undefined {
    const logger = enableLogging ? 
      (message: string) => console.log(`[PSK-SNI Extraction] ${message}`) : 
      undefined;
    
    return SniExtraction.extractSNIFromPSKExtension(buffer, logger);
  }

  /**
   * Checks if the buffer contains TLS 1.3 early data (0-RTT)
   * @param buffer - The buffer to check
   * @param enableLogging - Whether to enable logging
   * @returns true if early data is detected
   */
  public static hasEarlyData(buffer: Buffer, enableLogging: boolean = false): boolean {
    // This functionality has been moved to ClientHelloParser
    // We can implement it in terms of the parse result if needed
    const logger = enableLogging ? 
      (message: string) => console.log(`[Early Data] ${message}`) : 
      undefined;
    
    const parseResult = ClientHelloParser.parseClientHello(buffer, logger);
    return parseResult.isValid && parseResult.hasEarlyData;
  }

  /**
   * Attempts to extract SNI from an initial ClientHello packet and handles
   * session resumption edge cases more robustly than the standard extraction.
   *
   * This method handles:
   * 1. Standard SNI extraction
   * 2. TLS 1.3 PSK-based resumption (Chrome, Firefox, etc.)
   * 3. Session ticket-based resumption
   * 4. Fragmented ClientHello messages
   * 5. TLS 1.3 Early Data (0-RTT)
   * 6. Chrome's connection racing behaviors
   *
   * @param buffer - The buffer containing the TLS ClientHello message
   * @param connectionInfo - Optional connection information for fragment handling
   * @param enableLogging - Whether to enable detailed debug logging
   * @returns The extracted server name or undefined if not found or more data needed
   */
  public static extractSNIWithResumptionSupport(
    buffer: Buffer,
    connectionInfo?: {
      sourceIp?: string;
      sourcePort?: number;
      destIp?: string;
      destPort?: number;
    },
    enableLogging: boolean = false
  ): string | undefined {
    const logger = enableLogging ? 
      (message: string) => console.log(`[SNI Extraction] ${message}`) : 
      undefined;
    
    return SniExtraction.extractSNIWithResumptionSupport(
      buffer, 
      connectionInfo as ConnectionInfo, 
      logger
    );
  }

  /**
   * Main entry point for SNI extraction that handles all edge cases.
   * This should be called for each TLS packet received from a client.
   *
   * The method uses connection tracking to handle fragmented ClientHello
   * messages and various TLS 1.3 behaviors, including Chrome's connection
   * racing patterns and tab reactivation behaviors.
   *
   * @param buffer - The buffer containing TLS data
   * @param connectionInfo - Connection metadata (IPs and ports)
   * @param enableLogging - Whether to enable detailed debug logging
   * @param cachedSni - Optional cached SNI from previous connections (for racing detection)
   * @returns The extracted server name or undefined if not found or more data needed
   */
  public static processTlsPacket(
    buffer: Buffer,
    connectionInfo: {
      sourceIp: string;
      sourcePort: number;
      destIp: string;
      destPort: number;
      timestamp?: number;
    },
    enableLogging: boolean = false,
    cachedSni?: string
  ): string | undefined {
    const logger = enableLogging ? 
      (message: string) => console.log(`[TLS Packet] ${message}`) : 
      undefined;
    
    return SniExtraction.processTlsPacket(buffer, connectionInfo, logger, cachedSni);
  }
}