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

/**
 * TLS record types as defined in various RFCs
 */
export enum TlsRecordType {
  CHANGE_CIPHER_SPEC = 20,
  ALERT = 21,
  HANDSHAKE = 22,
  APPLICATION_DATA = 23,
  HEARTBEAT = 24, // RFC 6520
}

/**
 * TLS handshake message types
 */
export enum TlsHandshakeType {
  HELLO_REQUEST = 0,
  CLIENT_HELLO = 1,
  SERVER_HELLO = 2,
  NEW_SESSION_TICKET = 4,
  ENCRYPTED_EXTENSIONS = 8, // TLS 1.3
  CERTIFICATE = 11,
  SERVER_KEY_EXCHANGE = 12,
  CERTIFICATE_REQUEST = 13,
  SERVER_HELLO_DONE = 14,
  CERTIFICATE_VERIFY = 15,
  CLIENT_KEY_EXCHANGE = 16,
  FINISHED = 20,
}

/**
 * TLS extension types
 */
export enum TlsExtensionType {
  SERVER_NAME = 0, // SNI
  MAX_FRAGMENT_LENGTH = 1,
  CLIENT_CERTIFICATE_URL = 2,
  TRUSTED_CA_KEYS = 3,
  TRUNCATED_HMAC = 4,
  STATUS_REQUEST = 5, // OCSP
  SUPPORTED_GROUPS = 10, // Previously named "elliptic_curves"
  EC_POINT_FORMATS = 11,
  SIGNATURE_ALGORITHMS = 13,
  APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16, // ALPN
  SIGNED_CERTIFICATE_TIMESTAMP = 18, // Certificate Transparency
  PADDING = 21,
  SESSION_TICKET = 35,
  PRE_SHARED_KEY = 41, // TLS 1.3
  EARLY_DATA = 42, // TLS 1.3 0-RTT
  SUPPORTED_VERSIONS = 43, // TLS 1.3
  COOKIE = 44, // TLS 1.3
  PSK_KEY_EXCHANGE_MODES = 45, // TLS 1.3
  CERTIFICATE_AUTHORITIES = 47, // TLS 1.3
  POST_HANDSHAKE_AUTH = 49, // TLS 1.3
  SIGNATURE_ALGORITHMS_CERT = 50, // TLS 1.3
  KEY_SHARE = 51, // TLS 1.3
}

/**
 * TLS alert levels
 */
export enum TlsAlertLevel {
  WARNING = 1,
  FATAL = 2,
}

/**
 * TLS alert description codes
 */
export enum TlsAlertDescription {
  CLOSE_NOTIFY = 0,
  UNEXPECTED_MESSAGE = 10,
  BAD_RECORD_MAC = 20,
  DECRYPTION_FAILED = 21, // TLS 1.0 only
  RECORD_OVERFLOW = 22,
  DECOMPRESSION_FAILURE = 30, // TLS 1.2 and below
  HANDSHAKE_FAILURE = 40,
  NO_CERTIFICATE = 41, // SSLv3 only
  BAD_CERTIFICATE = 42,
  UNSUPPORTED_CERTIFICATE = 43,
  CERTIFICATE_REVOKED = 44,
  CERTIFICATE_EXPIRED = 45,
  CERTIFICATE_UNKNOWN = 46,
  ILLEGAL_PARAMETER = 47,
  UNKNOWN_CA = 48,
  ACCESS_DENIED = 49,
  DECODE_ERROR = 50,
  DECRYPT_ERROR = 51,
  EXPORT_RESTRICTION = 60, // TLS 1.0 only
  PROTOCOL_VERSION = 70,
  INSUFFICIENT_SECURITY = 71,
  INTERNAL_ERROR = 80,
  INAPPROPRIATE_FALLBACK = 86,
  USER_CANCELED = 90,
  NO_RENEGOTIATION = 100, // TLS 1.2 and below
  MISSING_EXTENSION = 109, // TLS 1.3
  UNSUPPORTED_EXTENSION = 110, // TLS 1.3
  CERTIFICATE_REQUIRED = 111, // TLS 1.3
  UNRECOGNIZED_NAME = 112,
  BAD_CERTIFICATE_STATUS_RESPONSE = 113,
  BAD_CERTIFICATE_HASH_VALUE = 114, // TLS 1.2 and below
  UNKNOWN_PSK_IDENTITY = 115,
  CERTIFICATE_REQUIRED_1_3 = 116, // TLS 1.3
  NO_APPLICATION_PROTOCOL = 120,
}

/**
 * TLS version codes (major.minor)
 */
export const TlsVersion = {
  SSL3: [0x03, 0x00],
  TLS1_0: [0x03, 0x01],
  TLS1_1: [0x03, 0x02],
  TLS1_2: [0x03, 0x03],
  TLS1_3: [0x03, 0x04],
};

/**
 * Utility functions for TLS protocol operations
 */
export class TlsUtils {
  /**
   * Checks if a buffer contains a TLS handshake record
   * @param buffer The buffer to check
   * @returns true if the buffer starts with a TLS handshake record
   */
  public static isTlsHandshake(buffer: Buffer): boolean {
    return buffer.length > 0 && buffer[0] === TlsRecordType.HANDSHAKE;
  }
  
  /**
   * Checks if a buffer contains TLS application data
   * @param buffer The buffer to check
   * @returns true if the buffer starts with a TLS application data record
   */
  public static isTlsApplicationData(buffer: Buffer): boolean {
    return buffer.length > 0 && buffer[0] === TlsRecordType.APPLICATION_DATA;
  }
  
  /**
   * Checks if a buffer contains a TLS alert record
   * @param buffer The buffer to check
   * @returns true if the buffer starts with a TLS alert record
   */
  public static isTlsAlert(buffer: Buffer): boolean {
    return buffer.length > 0 && buffer[0] === TlsRecordType.ALERT;
  }
  
  /**
   * 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 {
    // Minimum ClientHello size (TLS record header + handshake header)
    if (buffer.length < 9) {
      return false;
    }
    
    // Check record type (must be TLS_HANDSHAKE_RECORD_TYPE)
    if (buffer[0] !== TlsRecordType.HANDSHAKE) {
      return false;
    }
    
    // Skip version and length in TLS record header (5 bytes total)
    // Check handshake type at byte 5 (must be CLIENT_HELLO)
    return buffer[5] === TlsHandshakeType.CLIENT_HELLO;
  }
  
  /**
   * Gets the record length from a TLS record header
   * @param buffer Buffer containing a TLS record
   * @returns The record length if the buffer is valid, -1 otherwise
   */
  public static getTlsRecordLength(buffer: Buffer): number {
    if (buffer.length < 5) {
      return -1;
    }
    
    // Bytes 3-4 contain the record length (big-endian)
    return (buffer[3] << 8) + buffer[4];
  }
  
  /**
   * Creates a connection ID based on source/destination information
   * Used to track fragmented ClientHello messages across multiple packets
   *
   * @param connectionInfo Object containing connection identifiers
   * @returns A string ID for the connection
   */
  public static createConnectionId(connectionInfo: {
    sourceIp?: string;
    sourcePort?: number;
    destIp?: string;
    destPort?: number;
  }): string {
    const { sourceIp, sourcePort, destIp, destPort } = connectionInfo;
    return `${sourceIp}:${sourcePort}-${destIp}:${destPort}`;
  }
}