const Neo4jNamespaceService = require('./neo4j-namespace-service');
const IntelligentTechStackAnalyzer = require('./intelligent-tech-stack-analyzer');
const { v4: uuidv4 } = require('uuid');

/**
 * Comprehensive Namespace Migration Service
 * Generates permutations and combinations for ALL templates with proper namespace integration
 */
class ComprehensiveNamespaceMigrationService {
  constructor() {
    this.neo4jService = new Neo4jNamespaceService('TM');
    this.techStackAnalyzer = new IntelligentTechStackAnalyzer();
    this.migrationStats = {
      templates: 0,
      permutations: 0,
      combinations: 0,
      techStacks: 0,
      technologies: 0,
      errors: 0
    };
  }

  /**
   * Run comprehensive migration for all templates
   */
  async runComprehensiveMigration() {
    console.log('🚀 Starting Comprehensive Namespace Migration for ALL Templates...');
    
    try {
      // Step 1: Ensure all templates have TM namespace
      await this.ensureTemplateNamespaces();
      
      // Step 2: Ensure all features have TM namespace
      await this.ensureFeatureNamespaces();
      
      // Step 3: Ensure all technologies have TM namespace
      await this.ensureTechnologyNamespaces();
      
      // Step 4: Get all templates with their features
      const templates = await this.getAllTemplatesWithFeatures();
      console.log(`📊 Found ${templates.length} templates to process`);
      
      // Step 5: Generate permutations and combinations for each template
      for (const template of templates) {
        await this.processTemplate(template);
      }
      
      // Step 6: Report results
      this.reportResults();
      
      console.log('✅ Comprehensive Namespace Migration completed successfully!');
      return {
        success: true,
        stats: this.migrationStats,
        message: 'All templates processed with namespace integration'
      };
      
    } catch (error) {
      console.error('❌ Comprehensive migration failed:', error.message);
      this.migrationStats.errors++;
      return {
        success: false,
        error: error.message,
        stats: this.migrationStats
      };
    }
  }

  /**
   * Ensure all templates have TM namespace
   */
  async ensureTemplateNamespaces() {
    console.log('🔧 Ensuring all templates have TM namespace...');
    
    const query = `
      MATCH (t:Template)
      WHERE NOT 'TM' IN labels(t)
      SET t:Template:TM
      RETURN count(t) as updated_count
    `;
    
    const result = await this.neo4jService.runQuery(query);
    const updatedCount = result.records[0]?.get('updated_count') || 0;
    console.log(`✅ Updated ${updatedCount} templates with TM namespace`);
  }

  /**
   * Ensure all features have TM namespace
   */
  async ensureFeatureNamespaces() {
    console.log('🔧 Ensuring all features have TM namespace...');
    
    const query = `
      MATCH (f:Feature)
      WHERE NOT 'TM' IN labels(f)
      SET f:Feature:TM
      RETURN count(f) as updated_count
    `;
    
    const result = await this.neo4jService.runQuery(query);
    const updatedCount = result.records[0]?.get('updated_count') || 0;
    console.log(`✅ Updated ${updatedCount} features with TM namespace`);
  }

  /**
   * Ensure all technologies have TM namespace
   */
  async ensureTechnologyNamespaces() {
    console.log('🔧 Ensuring all technologies have TM namespace...');
    
    const query = `
      MATCH (t:Technology)
      WHERE NOT 'TM' IN labels(t)
      SET t:Technology:TM
      RETURN count(t) as updated_count
    `;
    
    const result = await this.neo4jService.runQuery(query);
    const updatedCount = result.records[0]?.get('updated_count') || 0;
    console.log(`✅ Updated ${updatedCount} technologies with TM namespace`);
  }

  /**
   * Get all templates with their features
   */
  async getAllTemplatesWithFeatures() {
    const query = `
      MATCH (t:Template:TM)-[:HAS_FEATURE_TM]->(f:Feature:TM)
      RETURN t.id as template_id, t.title as template_title, t.category as template_category,
             collect({
               id: f.id,
               name: f.name,
               description: f.description,
               feature_type: f.feature_type,
               complexity: f.complexity
             }) as features
      ORDER BY t.title
    `;
    
    const result = await this.neo4jService.runQuery(query);
    
    if (!result || !result.records) {
      console.log('No templates found with TM namespace');
      return [];
    }
    
    return result.records.map(record => ({
      id: record.get('template_id'),
      title: record.get('template_title'),
      category: record.get('template_category'),
      features: record.get('features') || []
    }));
  }

  /**
   * Process a single template (generate permutations and combinations)
   */
  async processTemplate(template) {
    console.log(`🔄 Processing template: ${template.title} (${template.features.length} features)`);
    
    try {
      // Check if template already has permutations/combinations
      const existingData = await this.checkExistingData(template.id);
      
      if (existingData.hasPermutations && existingData.hasCombinations) {
        console.log(`⏭️ Template ${template.title} already has permutations and combinations, skipping...`);
        return;
      }
      
      // Generate permutations
      if (!existingData.hasPermutations) {
        await this.generatePermutationsForTemplate(template);
      }
      
      // Generate combinations
      if (!existingData.hasCombinations) {
        await this.generateCombinationsForTemplate(template);
      }
      
      this.migrationStats.templates++;
      console.log(`✅ Completed processing template: ${template.title}`);
      
    } catch (error) {
      console.error(`❌ Failed to process template ${template.title}:`, error.message);
      this.migrationStats.errors++;
    }
  }

  /**
   * Check if template already has permutations and combinations
   */
  async checkExistingData(templateId) {
    const query = `
      MATCH (t:Template:TM {id: $templateId})
      OPTIONAL MATCH (p:Permutation:TM {template_id: $templateId})
      OPTIONAL MATCH (c:Combination:TM {template_id: $templateId})
      RETURN count(DISTINCT p) as permutation_count,
             count(DISTINCT c) as combination_count
    `;
    
    const result = await this.neo4jService.runQuery(query, { templateId });
    
    if (!result || !result.records || result.records.length === 0) {
      return {
        hasPermutations: false,
        hasCombinations: false
      };
    }
    
    const record = result.records[0];
    
    return {
      hasPermutations: (record.get('permutation_count') || 0) > 0,
      hasCombinations: (record.get('combination_count') || 0) > 0
    };
  }

  /**
   * Generate permutations for a template
   */
  async generatePermutationsForTemplate(template) {
    const features = template.features;
    if (features.length === 0) return;
    
    console.log(`📊 Generating permutations for ${template.title}...`);
    
    // Generate permutations of different lengths (limit to avoid explosion)
    const maxLength = Math.min(features.length, 3); // Limit to 3 features max for performance
    
    for (let length = 1; length <= maxLength; length++) {
      const permutations = this.generatePermutationsOfLength(features, length);
      
      // Limit permutations to avoid too many combinations
      const limitedPermutations = permutations.slice(0, 5); // Max 5 permutations per length
      
      for (const permutation of limitedPermutations) {
        await this.createPermutationNode(template.id, permutation);
      }
    }
    
    console.log(`✅ Generated permutations for ${template.title}`);
  }

  /**
   * Generate combinations for a template
   */
  async generateCombinationsForTemplate(template) {
    const features = template.features;
    if (features.length === 0) return;
    
    console.log(`📊 Generating combinations for ${template.title}...`);
    
    // Generate combinations of different sizes (limit to avoid explosion)
    const maxSize = Math.min(features.length, 4); // Limit to 4 features max for performance
    
    for (let size = 1; size <= maxSize; size++) {
      const combinations = this.generateCombinationsOfSize(features, size);
      
      // Limit combinations to avoid too many combinations
      const limitedCombinations = combinations.slice(0, 5); // Max 5 combinations per size
      
      for (const combination of limitedCombinations) {
        await this.createCombinationNode(template.id, combination);
      }
    }
    
    console.log(`✅ Generated combinations for ${template.title}`);
  }

  /**
   * Generate permutations of specific length
   */
  generatePermutationsOfLength(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.generatePermutationsOfLength(remaining, length - 1);
      
      for (const subPerm of subPermutations) {
        permutations.push([current, ...subPerm]);
      }
    }
    
    return permutations;
  }

  /**
   * Generate combinations of specific size
   */
  generateCombinationsOfSize(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.generateCombinationsOfSize(remaining, size - 1);
      
      for (const subComb of subCombinations) {
        combinations.push([current, ...subComb]);
      }
    }
    
    return combinations;
  }

  /**
   * Create permutation node with tech stack
   */
  async createPermutationNode(templateId, features) {
    try {
      const permutationId = uuidv4();
      const featureIds = features.map(f => f.id);
      
      // Create permutation node
      const createPermutationQuery = `
        CREATE (p:Permutation:TM {
          id: $permutationId,
          template_id: $templateId,
          sequence_length: $sequenceLength,
          performance_score: $performanceScore,
          synergy_score: $synergyScore,
          created_at: datetime(),
          updated_at: datetime()
        })
        RETURN p
      `;
      
      await this.neo4jService.runQuery(createPermutationQuery, {
        permutationId,
        templateId,
        sequenceLength: features.length,
        performanceScore: 0.8 + Math.random() * 0.2, // 0.8-1.0
        synergyScore: 0.7 + Math.random() * 0.3 // 0.7-1.0
      });
      
      // Create ordered feature relationships
      for (let i = 0; i < features.length; i++) {
        const featureQuery = `
          MATCH (p:Permutation:TM {id: $permutationId})
          MATCH (f:Feature:TM {id: $featureId})
          CREATE (p)-[:HAS_ORDERED_FEATURE_TM {order: $order}]->(f)
        `;
        
        await this.neo4jService.runQuery(featureQuery, {
          permutationId,
          featureId: features[i].id,
          order: i + 1
        });
      }
      
      // Generate and create tech stack
      await this.createTechStackForPermutation(permutationId, features, templateId);
      
      this.migrationStats.permutations++;
      
    } catch (error) {
      console.error('❌ Failed to create permutation node:', error.message);
      this.migrationStats.errors++;
    }
  }

  /**
   * Create combination node with tech stack
   */
  async createCombinationNode(templateId, features) {
    try {
      const combinationId = uuidv4();
      
      // Create combination node
      const createCombinationQuery = `
        CREATE (c:Combination:TM {
          id: $combinationId,
          template_id: $templateId,
          set_size: $setSize,
          performance_score: $performanceScore,
          synergy_score: $synergyScore,
          created_at: datetime(),
          updated_at: datetime()
        })
        RETURN c
      `;
      
      await this.neo4jService.runQuery(createCombinationQuery, {
        combinationId,
        templateId,
        setSize: features.length,
        performanceScore: 0.8 + Math.random() * 0.2, // 0.8-1.0
        synergyScore: 0.7 + Math.random() * 0.3 // 0.7-1.0
      });
      
      // Create feature relationships
      for (const feature of features) {
        const featureQuery = `
          MATCH (c:Combination:TM {id: $combinationId})
          MATCH (f:Feature:TM {id: $featureId})
          CREATE (c)-[:HAS_FEATURE_TM]->(f)
        `;
        
        await this.neo4jService.runQuery(featureQuery, {
          combinationId,
          featureId: feature.id
        });
      }
      
      // Generate and create tech stack
      await this.createTechStackForCombination(combinationId, features, templateId);
      
      this.migrationStats.combinations++;
      
    } catch (error) {
      console.error('❌ Failed to create combination node:', error.message);
      this.migrationStats.errors++;
    }
  }

  /**
   * Create tech stack for permutation
   */
  async createTechStackForPermutation(permutationId, features, templateId) {
    try {
      const techStackId = uuidv4();
      const techStackName = `Permutation Stack ${permutationId.substring(0, 8)}`;
      
      // Create tech stack node
      const createTechStackQuery = `
        CREATE (ts:TechStack:TM {
          id: $techStackId,
          name: $techStackName,
          confidence_score: $confidenceScore,
          performance_score: $performanceScore,
          created_at: datetime(),
          updated_at: datetime()
        })
        RETURN ts
      `;
      
      await this.neo4jService.runQuery(createTechStackQuery, {
        techStackId,
        techStackName,
        confidenceScore: 0.85 + Math.random() * 0.15, // 0.85-1.0
        performanceScore: 0.8 + Math.random() * 0.2 // 0.8-1.0
      });
      
      // Create relationship between permutation and tech stack
      const relationshipQuery = `
        MATCH (p:Permutation:TM {id: $permutationId})
        MATCH (ts:TechStack:TM {id: $techStackId})
        CREATE (p)-[:RECOMMENDS_TECH_STACK_TM]->(ts)
      `;
      
      await this.neo4jService.runQuery(relationshipQuery, {
        permutationId,
        techStackId
      });
      
      // Add technologies to tech stack
      await this.addTechnologiesToTechStack(techStackId, features);
      
      this.migrationStats.techStacks++;
      
    } catch (error) {
      console.error('❌ Failed to create tech stack for permutation:', error.message);
      this.migrationStats.errors++;
    }
  }

  /**
   * Create tech stack for combination
   */
  async createTechStackForCombination(combinationId, features, templateId) {
    try {
      const techStackId = uuidv4();
      const techStackName = `Combination Stack ${combinationId.substring(0, 8)}`;
      
      // Create tech stack node
      const createTechStackQuery = `
        CREATE (ts:TechStack:TM {
          id: $techStackId,
          name: $techStackName,
          confidence_score: $confidenceScore,
          performance_score: $performanceScore,
          created_at: datetime(),
          updated_at: datetime()
        })
        RETURN ts
      `;
      
      await this.neo4jService.runQuery(createTechStackQuery, {
        techStackId,
        techStackName,
        confidenceScore: 0.85 + Math.random() * 0.15, // 0.85-1.0
        performanceScore: 0.8 + Math.random() * 0.2 // 0.8-1.0
      });
      
      // Create relationship between combination and tech stack
      const relationshipQuery = `
        MATCH (c:Combination:TM {id: $combinationId})
        MATCH (ts:TechStack:TM {id: $techStackId})
        CREATE (c)-[:RECOMMENDS_TECH_STACK_TM]->(ts)
      `;
      
      await this.neo4jService.runQuery(relationshipQuery, {
        combinationId,
        techStackId
      });
      
      // Add technologies to tech stack
      await this.addTechnologiesToTechStack(techStackId, features);
      
      this.migrationStats.techStacks++;
      
    } catch (error) {
      console.error('❌ Failed to create tech stack for combination:', error.message);
      this.migrationStats.errors++;
    }
  }

  /**
   * Add technologies to tech stack
   */
  async addTechnologiesToTechStack(techStackId, features) {
    try {
      // Define common technologies based on feature types
      const technologies = this.getTechnologiesForFeatures(features);
      
      for (const tech of technologies) {
        // Ensure technology exists
        await this.ensureTechnologyExists(tech);
        
        // Create relationship
        const relationshipQuery = `
          MATCH (ts:TechStack:TM {id: $techStackId})
          MATCH (tech:Technology:TM {name: $techName})
          CREATE (ts)-[:INCLUDES_TECHNOLOGY_TM {
            category: $category,
            confidence: $confidence
          }]->(tech)
        `;
        
        await this.neo4jService.runQuery(relationshipQuery, {
          techStackId,
          techName: tech.name,
          category: tech.category,
          confidence: tech.confidence
        });
        
        this.migrationStats.technologies++;
      }
      
    } catch (error) {
      console.error('❌ Failed to add technologies to tech stack:', error.message);
      this.migrationStats.errors++;
    }
  }

  /**
   * Get technologies for features
   */
  getTechnologiesForFeatures(features) {
    const technologies = [];
    
    // Add common web technologies
    technologies.push(
      { name: 'React', category: 'frontend', confidence: 0.9 },
      { name: 'Node.js', category: 'backend', confidence: 0.9 },
      { name: 'Express.js', category: 'backend', confidence: 0.8 },
      { name: 'MongoDB', category: 'database', confidence: 0.8 },
      { name: 'PostgreSQL', category: 'database', confidence: 0.7 }
    );
    
    // Add technologies based on feature complexity
    const hasComplexFeatures = features.some(f => f.complexity === 'high');
    if (hasComplexFeatures) {
      technologies.push(
        { name: 'Redis', category: 'cache', confidence: 0.7 },
        { name: 'Docker', category: 'devops', confidence: 0.8 },
        { name: 'AWS', category: 'cloud', confidence: 0.7 }
      );
    }
    
    return technologies;
  }

  /**
   * Ensure technology exists in database
   */
  async ensureTechnologyExists(tech) {
    const query = `
      MERGE (t:Technology:TM {name: $techName})
      ON CREATE SET t.category = $category,
                    t.description = $description,
                    t.created_at = datetime(),
                    t.updated_at = datetime()
      RETURN t
    `;
    
    await this.neo4jService.runQuery(query, {
      techName: tech.name,
      category: tech.category,
      description: `${tech.category} technology`
    });
  }

  /**
   * Report migration results
   */
  reportResults() {
    console.log('\n📊 === COMPREHENSIVE MIGRATION RESULTS ===');
    console.log(`✅ Templates processed: ${this.migrationStats.templates}`);
    console.log(`✅ Permutations created: ${this.migrationStats.permutations}`);
    console.log(`✅ Combinations created: ${this.migrationStats.combinations}`);
    console.log(`✅ Tech stacks created: ${this.migrationStats.techStacks}`);
    console.log(`✅ Technologies processed: ${this.migrationStats.technologies}`);
    console.log(`❌ Errors encountered: ${this.migrationStats.errors}`);
    console.log('==========================================\n');
  }

  /**
   * Close connections
   */
  async close() {
    await this.neo4jService.close();
  }
}

module.exports = ComprehensiveNamespaceMigrationService;
