import type { IMatcher, IDomainMatchOptions } from '../types.js';

/**
 * DomainMatcher provides comprehensive domain matching functionality
 * Supporting exact matches, wildcards, and case-insensitive matching
 */
export class DomainMatcher implements IMatcher<boolean, IDomainMatchOptions> {
  private static wildcardToRegex(pattern: string): RegExp {
    // Escape special regex characters except *
    const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
    // Replace * with regex equivalent
    const regexPattern = escaped.replace(/\*/g, '.*');
    return new RegExp(`^${regexPattern}$`, 'i');
  }

  /**
   * Match a domain pattern against a hostname
   * @param pattern The pattern to match (supports wildcards like *.example.com)
   * @param hostname The hostname to test
   * @param options Matching options
   * @returns true if the hostname matches the pattern
   */
  static match(
    pattern: string, 
    hostname: string, 
    options: IDomainMatchOptions = {}
  ): boolean {
    // Handle null/undefined cases
    if (!pattern || !hostname) {
      return false;
    }

    // Normalize inputs
    const normalizedPattern = pattern.toLowerCase().trim();
    const normalizedHostname = hostname.toLowerCase().trim();

    // Remove trailing dots (FQDN normalization)
    const cleanPattern = normalizedPattern.replace(/\.$/, '');
    const cleanHostname = normalizedHostname.replace(/\.$/, '');

    // Exact match (most common case)
    if (cleanPattern === cleanHostname) {
      return true;
    }

    // Wildcard matching
    if (options.allowWildcards !== false && cleanPattern.includes('*')) {
      const regex = this.wildcardToRegex(cleanPattern);
      return regex.test(cleanHostname);
    }

    // No match
    return false;
  }

  /**
   * Check if a pattern contains wildcards
   */
  static isWildcardPattern(pattern: string): boolean {
    return pattern.includes('*');
  }

  /**
   * Calculate the specificity of a domain pattern
   * Higher values mean more specific patterns
   */
  static calculateSpecificity(pattern: string): number {
    if (!pattern) return 0;

    let score = 0;
    
    // Exact domains are most specific
    if (!pattern.includes('*')) {
      score += 100;
    }
    
    // Count domain segments
    const segments = pattern.split('.');
    score += segments.length * 10;
    
    // Penalize wildcards based on position
    if (pattern.startsWith('*')) {
      score -= 50; // Leading wildcard is very generic
    } else if (pattern.includes('*')) {
      score -= 20; // Wildcard elsewhere is less generic
    }
    
    // Bonus for longer patterns
    score += pattern.length;
    
    return score;
  }

  /**
   * Find all matching patterns from a list
   * Returns patterns sorted by specificity (most specific first)
   */
  static findAllMatches(
    patterns: string[], 
    hostname: string,
    options: IDomainMatchOptions = {}
  ): string[] {
    const matches = patterns.filter(pattern => 
      this.match(pattern, hostname, options)
    );

    // Sort by specificity (highest first)
    return matches.sort((a, b) => 
      this.calculateSpecificity(b) - this.calculateSpecificity(a)
    );
  }

  /**
   * Instance method for interface compliance
   */
  match(pattern: string, hostname: string, options?: IDomainMatchOptions): boolean {
    return DomainMatcher.match(pattern, hostname, options);
  }
}