/**
 * Combinatorial Engine
 * Handles generation of permutations and combinations for features
 * Provides intelligent analysis of feature interactions
 */
class CombinatorialEngine {
  constructor() {
    this.cache = new Map();
    this.maxCacheSize = 1000;
  }

  /**
   * Generate all permutations of features (ordered sequences)
   */
  generatePermutations(features) {
    if (!features || features.length === 0) {
      return [];
    }

    const cacheKey = `perm_${features.map(f => f.id).join('_')}`;
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }

    const permutations = [];
    
    // Generate permutations of all lengths (1 to n)
    for (let length = 1; length <= features.length; length++) {
      const perms = this.getPermutationsOfLength(features, length);
      permutations.push(...perms);
    }

    // Cache the result
    this.cacheResult(cacheKey, permutations);
    
    return permutations;
  }

  /**
   * Generate permutations of specific length
   */
  getPermutationsOfLength(features, length) {
    if (length === 0) return [[]];
    if (length === 1) return features.map(f => [f]);
    if (length > features.length) return [];

    const permutations = [];
    
    for (let i = 0; i < features.length; i++) {
      const current = features[i];
      const remaining = features.filter((_, index) => index !== i);
      const subPermutations = this.getPermutationsOfLength(remaining, length - 1);
      
      for (const subPerm of subPermutations) {
        permutations.push([current, ...subPerm]);
      }
    }
    
    return permutations;
  }

  /**
   * Generate all combinations of features (unordered sets)
   */
  generateCombinations(features) {
    if (!features || features.length === 0) {
      return [];
    }

    const cacheKey = `comb_${features.map(f => f.id).join('_')}`;
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }

    const combinations = [];
    
    // Generate combinations of all sizes (1 to n)
    for (let size = 1; size <= features.length; size++) {
      const combs = this.getCombinationsOfSize(features, size);
      combinations.push(...combs);
    }

    // Cache the result
    this.cacheResult(cacheKey, combinations);
    
    return combinations;
  }

  /**
   * Generate combinations of specific size
   */
  getCombinationsOfSize(features, size) {
    if (size === 0) return [[]];
    if (size === 1) return features.map(f => [f]);
    if (size === features.length) return [features];
    if (size > features.length) return [];

    const combinations = [];
    
    for (let i = 0; i <= features.length - size; i++) {
      const current = features[i];
      const remaining = features.slice(i + 1);
      const subCombinations = this.getCombinationsOfSize(remaining, size - 1);
      
      for (const subComb of subCombinations) {
        combinations.push([current, ...subComb]);
      }
    }
    
    return combinations;
  }

  /**
   * Generate smart permutations based on feature dependencies
   */
  generateSmartPermutations(features) {
    if (!features || features.length === 0) {
      return [];
    }

    // Sort features by dependencies and complexity
    const sortedFeatures = this.sortFeaturesByDependencies(features);
    
    // Generate permutations with dependency awareness
    const permutations = [];
    
    for (let length = 1; length <= sortedFeatures.length; length++) {
      const perms = this.getSmartPermutationsOfLength(sortedFeatures, length);
      permutations.push(...perms);
    }
    
    return permutations;
  }

  /**
   * Generate smart combinations based on feature compatibility
   */
  generateSmartCombinations(features) {
    if (!features || features.length === 0) {
      return [];
    }

    // Filter out incompatible features
    const compatibleFeatures = this.filterCompatibleFeatures(features);
    
    // Generate combinations with compatibility awareness
    const combinations = [];
    
    for (let size = 1; size <= compatibleFeatures.length; size++) {
      const combs = this.getSmartCombinationsOfSize(compatibleFeatures, size);
      combinations.push(...combs);
    }
    
    return combinations;
  }

  /**
   * Sort features by dependencies and complexity
   */
  sortFeaturesByDependencies(features) {
    return features.sort((a, b) => {
      // First by feature type (essential, suggested, custom)
      const typeOrder = { essential: 1, suggested: 2, custom: 3 };
      const typeDiff = (typeOrder[a.feature_type] || 3) - (typeOrder[b.feature_type] || 3);
      if (typeDiff !== 0) return typeDiff;
      
      // Then by complexity
      const complexityOrder = { low: 1, medium: 2, high: 3 };
      const complexityDiff = (complexityOrder[a.complexity] || 2) - (complexityOrder[b.complexity] || 2);
      if (complexityDiff !== 0) return complexityDiff;
      
      // Finally by display order
      return (a.display_order || 0) - (b.display_order || 0);
    });
  }

  /**
   * Filter out incompatible features
   */
  filterCompatibleFeatures(features) {
    const incompatiblePairs = this.getIncompatibleFeaturePairs();
    
    return features.filter(feature => {
      // Check if this feature is incompatible with any other feature
      return !features.some(otherFeature => {
        if (feature.id === otherFeature.id) return false;
        
        const pair = [feature.name.toLowerCase(), otherFeature.name.toLowerCase()].sort();
        return incompatiblePairs.has(pair.join('|'));
      });
    });
  }

  /**
   * Get incompatible feature pairs
   */
  getIncompatibleFeaturePairs() {
    const incompatiblePairs = new Set([
      'auth|payment', // Example: Some auth methods incompatible with certain payment methods
      'mobile|desktop', // Example: Mobile-specific features incompatible with desktop
      // Add more incompatible pairs as needed
    ]);
    
    return incompatiblePairs;
  }

  /**
   * Get smart permutations of specific length with dependency awareness
   */
  getSmartPermutationsOfLength(features, length) {
    if (length === 0) return [[]];
    if (length === 1) return features.map(f => [f]);
    if (length > features.length) return [];

    const permutations = [];
    
    for (let i = 0; i < features.length; i++) {
      const current = features[i];
      const remaining = features.filter((_, index) => index !== i);
      const subPermutations = this.getSmartPermutationsOfLength(remaining, length - 1);
      
      for (const subPerm of subPermutations) {
        // Check if this permutation makes sense based on dependencies
        if (this.isValidPermutation([current, ...subPerm])) {
          permutations.push([current, ...subPerm]);
        }
      }
    }
    
    return permutations;
  }

  /**
   * Get smart combinations of specific size with compatibility awareness
   */
  getSmartCombinationsOfSize(features, size) {
    if (size === 0) return [[]];
    if (size === 1) return features.map(f => [f]);
    if (size === features.length) return [features];
    if (size > features.length) return [];

    const combinations = [];
    
    for (let i = 0; i <= features.length - size; i++) {
      const current = features[i];
      const remaining = features.slice(i + 1);
      const subCombinations = this.getSmartCombinationsOfSize(remaining, size - 1);
      
      for (const subComb of subCombinations) {
        // Check if this combination makes sense
        if (this.isValidCombination([current, ...subComb])) {
          combinations.push([current, ...subComb]);
        }
      }
    }
    
    return combinations;
  }

  /**
   * Check if a permutation is valid based on dependencies
   */
  isValidPermutation(permutation) {
    // Check if features are in logical order
    for (let i = 0; i < permutation.length - 1; i++) {
      const current = permutation[i];
      const next = permutation[i + 1];
      
      // Example: Auth should come before payment
      if (current.name.toLowerCase().includes('auth') && 
          next.name.toLowerCase().includes('payment')) {
        return true;
      }
      
      // Example: Dashboard should come after auth
      if (current.name.toLowerCase().includes('dashboard') && 
          !permutation.slice(0, i).some(f => f.name.toLowerCase().includes('auth'))) {
        return false;
      }
    }
    
    return true;
  }

  /**
   * Check if a combination is valid based on compatibility
   */
  isValidCombination(combination) {
    // Check for incompatible feature pairs
    const incompatiblePairs = this.getIncompatibleFeaturePairs();
    
    for (let i = 0; i < combination.length; i++) {
      for (let j = i + 1; j < combination.length; j++) {
        const pair = [combination[i].name.toLowerCase(), combination[j].name.toLowerCase()].sort();
        if (incompatiblePairs.has(pair.join('|'))) {
          return false;
        }
      }
    }
    
    return true;
  }

  /**
   * Calculate complexity score for a feature set
   */
  calculateComplexityScore(features) {
    if (!features || features.length === 0) {
      return 0;
    }

    const complexityMap = { low: 1, medium: 2, high: 3 };
    const totalScore = features.reduce((sum, feature) => {
      return sum + (complexityMap[feature.complexity] || 2);
    }, 0);
    
    return totalScore / features.length;
  }

  /**
   * Calculate interaction score between features
   */
  calculateInteractionScore(features) {
    if (!features || features.length < 2) {
      return 0;
    }

    let interactionScore = 0;
    
    for (let i = 0; i < features.length; i++) {
      for (let j = i + 1; j < features.length; j++) {
        const feature1 = features[i];
        const feature2 = features[j];
        
        // Calculate interaction based on feature types and names
        const interaction = this.getFeatureInteraction(feature1, feature2);
        interactionScore += interaction;
      }
    }
    
    return interactionScore / (features.length * (features.length - 1) / 2);
  }

  /**
   * Get interaction score between two features
   */
  getFeatureInteraction(feature1, feature2) {
    const name1 = feature1.name.toLowerCase();
    const name2 = feature2.name.toLowerCase();
    
    // High interaction features
    if ((name1.includes('auth') && name2.includes('user')) ||
        (name1.includes('payment') && name2.includes('order')) ||
        (name1.includes('dashboard') && name2.includes('analytics'))) {
      return 0.8;
    }
    
    // Medium interaction features
    if ((name1.includes('api') && name2.includes('integration')) ||
        (name1.includes('notification') && name2.includes('user'))) {
      return 0.6;
    }
    
    // Low interaction features
    return 0.3;
  }

  /**
   * Get feature recommendations based on existing features
   */
  getFeatureRecommendations(existingFeatures, allFeatures) {
    const recommendations = [];
    
    for (const feature of allFeatures) {
      if (existingFeatures.some(f => f.id === feature.id)) {
        continue; // Skip already selected features
      }
      
      // Calculate compatibility score
      const compatibilityScore = this.calculateCompatibilityScore(existingFeatures, feature);
      
      if (compatibilityScore > 0.5) {
        recommendations.push({
          feature: feature,
          compatibility_score: compatibilityScore,
          reason: this.getRecommendationReason(existingFeatures, feature)
        });
      }
    }
    
    return recommendations.sort((a, b) => b.compatibility_score - a.compatibility_score);
  }

  /**
   * Calculate compatibility score between existing features and a new feature
   */
  calculateCompatibilityScore(existingFeatures, newFeature) {
    let totalScore = 0;
    
    for (const existingFeature of existingFeatures) {
      const interaction = this.getFeatureInteraction(existingFeature, newFeature);
      totalScore += interaction;
    }
    
    return totalScore / existingFeatures.length;
  }

  /**
   * Get recommendation reason
   */
  getRecommendationReason(existingFeatures, newFeature) {
    const existingNames = existingFeatures.map(f => f.name.toLowerCase());
    const newName = newFeature.name.toLowerCase();
    
    if (existingNames.some(name => name.includes('auth')) && newName.includes('user')) {
      return 'Complements authentication features';
    }
    
    if (existingNames.some(name => name.includes('payment')) && newName.includes('order')) {
      return 'Enhances payment functionality';
    }
    
    if (existingNames.some(name => name.includes('dashboard')) && newName.includes('analytics')) {
      return 'Improves dashboard capabilities';
    }
    
    return 'Good compatibility with existing features';
  }

  /**
   * Cache result to improve performance
   */
  cacheResult(key, result) {
    if (this.cache.size >= this.maxCacheSize) {
      // Remove oldest entry
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    
    this.cache.set(key, result);
  }

  /**
   * Clear cache
   */
  clearCache() {
    this.cache.clear();
  }

  /**
   * Get cache statistics
   */
  getCacheStats() {
    return {
      size: this.cache.size,
      maxSize: this.maxCacheSize,
      keys: Array.from(this.cache.keys())
    };
  }
}

module.exports = CombinatorialEngine;
