// LLM-Based Fuzzy Component Name Matcher

import { ComponentMetadata, componentRegistry } from './component-registry';

export interface FuzzyMatchResult {
  component: ComponentMetadata;
  confidence: number;
  reason: string;
}

export class FuzzyComponentMatcher {
  /**
   * Find the best matching component for a given name using fuzzy matching
   * @param componentName The name to match against
   * @returns The best matching component with confidence score
   */
  static findBestMatch(componentName: string): FuzzyMatchResult | null {
    // First try exact match
    const exactMatch = Object.values(componentRegistry).find(
      c => c.name.toLowerCase() === componentName.toLowerCase()
    );
    
    if (exactMatch) {
      return {
        component: exactMatch,
        confidence: 1.0,
        reason: 'Exact match found'
      };
    }
    
    // If no exact match, try fuzzy matching
    const candidates = this.getFuzzyCandidates(componentName);
    
    if (candidates.length === 0) {
      return null;
    }
    
    // Sort by confidence and return the best match
    candidates.sort((a, b) => b.confidence - a.confidence);
    return candidates[0];
  }
  
  /**
   * Get fuzzy matching candidates for a component name
   * @param componentName The name to match against
   * @returns Array of fuzzy match results
   */
  private static getFuzzyCandidates(componentName: string): FuzzyMatchResult[] {
    const candidates: FuzzyMatchResult[] = [];
    const lowerComponentName = componentName.toLowerCase();
    
    // Get all components
    const allComponents = Object.values(componentRegistry);
    
    for (const component of allComponents) {
      const lowerComponent = component.name.toLowerCase();
      
      // Calculate confidence score based on various factors
      let confidence = 0;
      const reasons: string[] = [];
      
      // Exact substring match
      if (lowerComponent.includes(lowerComponentName) || lowerComponentName.includes(lowerComponent)) {
        confidence += 0.4;
        reasons.push('substring match');
      }
      
      // Levenshtein distance (simplified)
      const distance = this.levenshteinDistance(lowerComponentName, lowerComponent);
      const maxLength = Math.max(lowerComponentName.length, lowerComponent.length);
      const similarity = 1 - (distance / maxLength);
      
      if (similarity > 0.5) {
        confidence += similarity * 0.3;
        reasons.push(`similarity: ${(similarity * 100).toFixed(1)}%`);
      }
      
      // Shared prefix/suffix
      const sharedPrefix = this.getSharedPrefix(lowerComponentName, lowerComponent);
      const sharedSuffix = this.getSharedSuffix(lowerComponentName, lowerComponent);
      
      if (sharedPrefix.length > 0) {
        const prefixRatio = sharedPrefix.length / Math.min(lowerComponentName.length, lowerComponent.length);
        confidence += prefixRatio * 0.15;
        reasons.push(`shared prefix: ${sharedPrefix}`);
      }
      
      if (sharedSuffix.length > 0) {
        const suffixRatio = sharedSuffix.length / Math.min(lowerComponentName.length, lowerComponent.length);
        confidence += suffixRatio * 0.15;
        reasons.push(`shared suffix: ${sharedSuffix}`);
      }
      
      // Only include candidates with reasonable confidence
      if (confidence > 0.3) {
        candidates.push({
          component,
          confidence,
          reason: reasons.join(', ')
        });
      }
    }
    
    return candidates;
  }
  
  /**
   * Calculate Levenshtein distance between two strings
   * @param a First string
   * @param b Second string
   * @returns The Levenshtein distance
   */
  private static levenshteinDistance(a: string, b: string): number {
    if (a.length === 0) return b.length;
    if (b.length === 0) return a.length;
    
    const matrix: number[][] = [];
    
    // Increment along the first column of each row
    for (let i = 0; i <= b.length; i++) {
      matrix[i] = [i];
    }
    
    // Increment each column in the first row
    for (let j = 0; j <= a.length; j++) {
      matrix[0][j] = j;
    }
    
    // Fill in the rest of the matrix
    for (let i = 1; i <= b.length; i++) {
      for (let j = 1; j <= a.length; j++) {
        if (b.charAt(i - 1) === a.charAt(j - 1)) {
          matrix[i][j] = matrix[i - 1][j - 1];
        } else {
          matrix[i][j] = Math.min(
            matrix[i - 1][j - 1] + 1, // substitution
            matrix[i][j - 1] + 1,     // insertion
            matrix[i - 1][j] + 1      // deletion
          );
        }
      }
    }
    
    return matrix[b.length][a.length];
  }
  
  /**
   * Get the shared prefix between two strings
   * @param a First string
   * @param b Second string
   * @returns The shared prefix
   */
  private static getSharedPrefix(a: string, b: string): string {
    let i = 0;
    while (i < a.length && i < b.length && a.charAt(i) === b.charAt(i)) {
      i++;
    }
    return a.substring(0, i);
  }
  
  /**
   * Get the shared suffix between two strings
   * @param a First string
   * @param b Second string
   * @returns The shared suffix
   */
  private static getSharedSuffix(a: string, b: string): string {
    let i = 0;
    const minLen = Math.min(a.length, b.length);
    while (i < minLen && a.charAt(a.length - 1 - i) === b.charAt(b.length - 1 - i)) {
      i++;
    }
    return a.substring(a.length - i);
  }
}

// Usage example
export const FuzzyMatcher = {
  findBestMatch: (componentName: string) => FuzzyComponentMatcher.findBestMatch(componentName)
};
