const neo4j = require('neo4j-driver');
const { v4: uuidv4 } = require('uuid');
const Neo4jNamespaceService = require('./neo4j-namespace-service');

/**
 * Enhanced Neo4j Template Knowledge Graph (TKG) Service
 * Provides robust template-feature relationships with intelligent tech recommendations
 * Now uses namespace service for data isolation
 */
class EnhancedTKGService {
  constructor() {
    this.neo4jService = new Neo4jNamespaceService('TM');
    // Ensure legacy methods that use this.driver still work by exposing the underlying driver
    this.driver = this.neo4jService.driver;
  }

  /**
   * Clear all existing TKG data
   */
  async clearTKG() {
    try {
      console.log('🧹 Clearing existing TKG data...');
      await this.neo4jService.clearNamespaceData();
      console.log('✅ Cleared existing TKG data');
    } catch (error) {
      console.error('❌ Failed to clear TKG data:', error.message);
      throw error;
    }
  }

  /**
   * Create enhanced template node with comprehensive metadata
   */
  async createTemplateNode(templateData) {
    try {
      return await this.neo4jService.createTemplateNode(templateData);
    } catch (error) {
      console.error('❌ Failed to create template node:', error.message);
      throw error;
    }
  }

  /**
   * Create enhanced feature node with dependencies and conflicts
   */
  async createFeatureNode(featureData) {
    try {
      return await this.neo4jService.createFeatureNode(featureData);
    } catch (error) {
      console.error('❌ Failed to create feature node:', error.message);
      throw error;
    }
  }

  /**
   * Create enhanced technology node with comprehensive metadata
   */
  async createTechnologyNode(techData) {
    try {
      return await this.neo4jService.createTechnologyNode(techData);
    } catch (error) {
      console.error('❌ Failed to create technology node:', error.message);
      throw error;
    }
  }

  /**
   * Create enhanced tech stack node with AI analysis
   */
  async createTechStackNode(techStackData) {
    try {
      return await this.neo4jService.createTechStackNode(techStackData);
    } catch (error) {
      console.error('❌ Failed to create tech stack node:', error.message);
      throw error;
    }
  }

  /**
   * Create template-feature relationship with properties
   */
  async createTemplateFeatureRelationship(templateId, featureId, properties = {}) {
    try {
      return await this.neo4jService.createTemplateFeatureRelationship(templateId, featureId);
    } catch (error) {
      console.error('❌ Failed to create template-feature relationship:', error.message);
      throw error;
    }
  }

  /**
   * Create feature-technology relationship with confidence
   */
  async createFeatureTechnologyRelationship(featureId, techName, properties = {}) {
    try {
      const confidence = Number(properties.confidence) || 0.8;
      return await this.neo4jService.createFeatureTechnologyRelationship(featureId, techName, confidence);
    } catch (error) {
      console.error('❌ Failed to create feature-technology relationship:', error.message);
      throw error;
    }
  }

  /**
   * Create tech stack-technology relationship with category and confidence
   */
  async createTechStackTechnologyRelationship(techStackId, techName, category, properties = {}) {
    try {
      const confidence = Number(properties.confidence) || 0.8;
      return await this.neo4jService.createTechStackTechnologyRelationship(techStackId, techName, category, confidence);
    } catch (error) {
      console.error('❌ Failed to create tech stack-technology relationship:', error.message);
      throw error;
    }
  }

  /**
   * Create template-tech stack relationship
   */
  async createTemplateTechStackRelationship(templateId, techStackId) {
    try {
      return await this.neo4jService.createTemplateTechStackRelationship(templateId, techStackId);
    } catch (error) {
      console.error('❌ Failed to create template-tech stack relationship:', error.message);
      throw error;
    }
  }

  /**
   * Create technology synergy relationships
   */
  async createTechnologySynergyRelationships(tech1Name, tech2Name, synergyScore) {
    const session = this.driver.session();
    try {
      await session.run(`
        MATCH (t1:Technology {name: $tech1Name})
        MATCH (t2:Technology {name: $tech2Name})
        MERGE (t1)-[r:SYNERGY {score: $synergyScore}]->(t2)
        MERGE (t2)-[r2:SYNERGY {score: $synergyScore}]->(t1)
      `, {
        tech1Name,
        tech2Name,
        synergyScore
      });
    } catch (error) {
      console.error('❌ Failed to create technology synergy relationships:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Create technology conflict relationships
   */
  async createTechnologyConflictRelationships(tech1Name, tech2Name, severity) {
    const session = this.driver.session();
    try {
      await session.run(`
        MATCH (t1:Technology {name: $tech1Name})
        MATCH (t2:Technology {name: $tech2Name})
        MERGE (t1)-[r:CONFLICTS {severity: $severity}]->(t2)
        MERGE (t2)-[r2:CONFLICTS {severity: $severity}]->(t1)
      `, {
        tech1Name,
        tech2Name,
        severity
      });
    } catch (error) {
      console.error('❌ Failed to create technology conflict relationships:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Create feature dependency relationships
   */
  async createFeatureDependencyRelationships(featureId, dependencies) {
    const session = this.driver.session();
    try {
      for (const dependency of dependencies) {
        await session.run(`
          MATCH (f1:Feature {id: $featureId})
          MATCH (f2:Feature {id: $dependencyId})
          MERGE (f1)-[r:DEPENDS_ON {strength: $strength}]->(f2)
        `, {
          featureId,
          dependencyId: dependency.id,
          strength: dependency.strength || 0.5
        });
      }
    } catch (error) {
      console.error('❌ Failed to create feature dependency relationships:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Create feature conflict relationships
   */
  async createFeatureConflictRelationships(featureId, conflicts) {
    const session = this.driver.session();
    try {
      for (const conflict of conflicts) {
        await session.run(`
          MATCH (f1:Feature {id: $featureId})
          MATCH (f2:Feature {id: $conflictId})
          MERGE (f1)-[r:CONFLICTS_WITH {severity: $severity}]->(f2)
        `, {
          featureId,
          conflictId: conflict.id,
          severity: conflict.severity || 'medium'
        });
      }
    } catch (error) {
      console.error('❌ Failed to create feature conflict relationships:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Get comprehensive template tech stack with relationships
   */
  async getTemplateTechStack(templateId) {
    const session = this.driver.session();
    try {
      const result = await session.run(`
        MATCH (t:Template {id: $templateId})
        MATCH (t)-[:HAS_TECH_STACK]->(ts)
        MATCH (ts)-[r:RECOMMENDS_TECHNOLOGY]->(tech)
        OPTIONAL MATCH (tech)-[syn:SYNERGY]->(otherTech)
        OPTIONAL MATCH (tech)-[conf:CONFLICTS]->(conflictTech)
        RETURN ts, tech, r.category as category, r.confidence as confidence,
               collect(DISTINCT {synergy: otherTech.name, score: syn.score}) as synergies,
               collect(DISTINCT {conflict: conflictTech.name, severity: conf.severity}) as conflicts
        ORDER BY r.category, r.confidence DESC
      `, { templateId });
      
      return result.records.map(record => ({
        techStack: record.get('ts').properties,
        technology: record.get('tech').properties,
        category: record.get('category'),
        confidence: record.get('confidence'),
        synergies: record.get('synergies'),
        conflicts: record.get('conflicts')
      }));
    } catch (error) {
      console.error('❌ Failed to get template tech stack:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Get template features with technology requirements
   */
  async getTemplateFeatures(templateId) {
    const session = this.driver.session();
    try {
      const result = await session.run(`
        MATCH (t:Template {id: $templateId})
        MATCH (t)-[:HAS_FEATURE]->(f)
        MATCH (f)-[:REQUIRES_TECHNOLOGY]->(tech)
        OPTIONAL MATCH (f)-[dep:DEPENDS_ON]->(depFeature)
        OPTIONAL MATCH (f)-[conf:CONFLICTS_WITH]->(conflictFeature)
        RETURN f, tech, 
               collect(DISTINCT {dependency: depFeature.name, strength: dep.strength}) as dependencies,
               collect(DISTINCT {conflict: conflictFeature.name, severity: conf.severity}) as conflicts
        ORDER BY f.display_order, f.name
      `, { templateId });
      
      return result.records.map(record => ({
        feature: record.get('f').properties,
        technology: record.get('tech').properties,
        dependencies: record.get('dependencies'),
        conflicts: record.get('conflicts')
      }));
    } catch (error) {
      console.error('❌ Failed to get template features:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Get similar templates based on features and tech stack
   */
  async getSimilarTemplates(templateId, limit = 5) {
    const session = this.driver.session();
    try {
      const result = await session.run(`
        MATCH (t1:Template {id: $templateId})
        MATCH (t1)-[:HAS_FEATURE]->(f1)
        MATCH (t2:Template)
        WHERE t2.id <> $templateId AND t2.id <> $templateId
        MATCH (t2)-[:HAS_FEATURE]->(f2)
        WITH t1, t2, collect(DISTINCT f1) as features1, collect(DISTINCT f2) as features2
        MATCH (t1)-[:HAS_TECH_STACK]->(ts1)
        MATCH (t2)-[:HAS_TECH_STACK]->(ts2)
        WITH t1, t2, features1, features2, ts1, ts2
        MATCH (ts1)-[:RECOMMENDS_TECHNOLOGY]->(tech1)
        MATCH (ts2)-[:RECOMMENDS_TECHNOLOGY]->(tech2)
        WITH t1, t2, features1, features2, 
             collect(DISTINCT tech1.name) as techs1, 
             collect(DISTINCT tech2.name) as techs2
        WITH t1, t2, features1, features2, techs1, techs2,
             size(apoc.coll.intersection(features1, features2)) as commonFeatures,
             size(apoc.coll.intersection(techs1, techs2)) as commonTechs
        WITH t1, t2, commonFeatures, commonTechs,
             size(features1) as totalFeatures1,
             size(features2) as totalFeatures2,
             size(techs1) as totalTechs1,
             size(techs2) as totalTechs2
        WITH t1, t2, commonFeatures, commonTechs, totalFeatures1, totalFeatures2, totalTechs1, totalTechs2,
             (commonFeatures * 1.0 / (totalFeatures1 + totalFeatures2 - commonFeatures)) as featureSimilarity,
             (commonTechs * 1.0 / (totalTechs1 + totalTechs2 - commonTechs)) as techSimilarity
        WITH t1, t2, (featureSimilarity * 0.6 + techSimilarity * 0.4) as similarity
        WHERE similarity > 0.3
        RETURN t2, similarity
        ORDER BY similarity DESC
        LIMIT $limit
      `, { templateId, limit });
      
      return result.records.map(record => ({
        template: record.get('t2').properties,
        similarity: record.get('similarity')
      }));
    } catch (error) {
      console.error('❌ Failed to get similar templates:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Get technology synergies and conflicts
   */
  async getTechnologyRelationships(techNames) {
    const session = this.driver.session();
    try {
      const result = await session.run(`
        MATCH (t1:Technology)
        WHERE t1.name IN $techNames
        MATCH (t2:Technology)
        WHERE t2.name IN $techNames AND t1.name <> t2.name
        OPTIONAL MATCH (t1)-[r1:SYNERGY]->(t2)
        OPTIONAL MATCH (t1)-[r2:CONFLICTS]->(t2)
        WITH t1, t2, r1, r2
        RETURN t1, t2,
               CASE WHEN r1 IS NOT NULL THEN 'synergy'
                    WHEN r2 IS NOT NULL THEN 'conflict'
                    ELSE 'neutral' END as relationship_type,
               COALESCE(r1.score, 0) as synergy_score,
               COALESCE(r2.severity, 'none') as conflict_severity
      `, { techNames });
      
      const relationships = {
        synergies: [],
        conflicts: [],
        neutral: []
      };
      
      for (const record of result.records) {
        const t1 = record.get('t1').properties;
        const t2 = record.get('t2').properties;
        const relationshipType = record.get('relationship_type');
        const synergyScore = record.get('synergy_score');
        const conflictSeverity = record.get('conflict_severity');
        
        const analysis = {
          tech1: t1,
          tech2: t2,
          relationshipType,
          synergyScore,
          conflictSeverity
        };
        
        if (relationshipType === 'synergy') {
          relationships.synergies.push(analysis);
        } else if (relationshipType === 'conflict') {
          relationships.conflicts.push(analysis);
        } else {
          relationships.neutral.push(analysis);
        }
      }
      
      return relationships;
    } catch (error) {
      console.error('❌ Failed to get technology relationships:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Get comprehensive TKG statistics
   */
  async getTKGStats() {
    const session = this.driver.session();
    try {
      const result = await session.run(`
        MATCH (t:Template)
        MATCH (f:Feature)
        MATCH (tech:Technology)
        MATCH (ts:TechStack)
        RETURN 
          count(DISTINCT t) as templates,
          count(DISTINCT f) as features,
          count(DISTINCT tech) as technologies,
          count(DISTINCT ts) as tech_stacks,
          avg(t.success_rate) as avg_success_rate,
          avg(t.usage_count) as avg_usage_count
      `);
      
      return result.records[0];
    } catch (error) {
      console.error('❌ Failed to get TKG stats:', error.message);
      throw error;
    } finally {
      await session.close();
    }
  }

  /**
   * Test TKG connection
   */
  async testConnection() {
    const session = this.driver.session();
    try {
      const result = await session.run('RETURN 1 as test');
      console.log('✅ Enhanced TKG Neo4j connection successful');
      return true;
    } catch (error) {
      console.error('❌ Enhanced TKG Neo4j connection failed:', error.message);
      return false;
    } finally {
      await session.close();
    }
  }

  /**
   * Clean up duplicate templates and ensure data integrity
   */
  async cleanupDuplicates() {
    const session = this.driver.session();
    try {
      console.log('🧹 Starting TKG duplicate cleanup...');
      
      // Step 1: Remove templates without categories (keep the ones with categories)
      const removeResult = await session.run(`
        MATCH (t:Template)
        WHERE t.category IS NULL OR t.category = ''
        DETACH DELETE t
        RETURN count(t) as removed_count
      `);
      
      const removedCount = removeResult.records[0].get('removed_count');
      console.log(`✅ Removed ${removedCount} duplicate templates without categories`);
      
      // Step 2: Verify no duplicates remain
      const verifyResult = await session.run(`
        MATCH (t:Template)
        WITH t.id as id, count(t) as count
        WHERE count > 1
        RETURN count(*) as duplicate_count
      `);
      
      const duplicateCount = verifyResult.records[0].get('duplicate_count');
      
      if (duplicateCount === 0) {
        console.log('✅ No duplicate templates found in TKG');
      } else {
        console.log(`⚠️ Found ${duplicateCount} template IDs with duplicates in TKG`);
      }
      
      // Step 3: Get final template count
      const finalResult = await session.run(`
        MATCH (t:Template)
        RETURN count(t) as total_templates
      `);
      
      const totalTemplates = finalResult.records[0].get('total_templates');
      console.log(`📊 Final TKG template count: ${totalTemplates}`);
      
      return {
        success: true,
        removedCount: removedCount,
        duplicateCount: duplicateCount,
        totalTemplates: totalTemplates
      };
      
    } catch (error) {
      console.error('❌ Failed to cleanup TKG duplicates:', error.message);
      return { success: false, error: error.message };
    } finally {
      await session.close();
    }
  }

  /**
   * Check for and prevent duplicate template creation
   */
  async checkTemplateExists(templateId) {
    const session = this.driver.session();
    try {
      const result = await session.run(`
        MATCH (t:Template {id: $templateId})
        RETURN t.id as id, t.title as title, t.category as category
      `, { templateId });
      
      if (result.records.length > 0) {
        const record = result.records[0];
        return {
          exists: true,
          id: record.get('id'),
          title: record.get('title'),
          category: record.get('category')
        };
      }
      
      return { exists: false };
    } catch (error) {
      console.error('❌ Failed to check TKG template existence:', error.message);
      return { exists: false, error: error.message };
    } finally {
      await session.close();
    }
  }

  /**
   * Close TKG driver
   */
  async close() {
    await this.driver.close();
  }
}

module.exports = EnhancedTKGService;
