# ================================================================================================
# POSTGRESQL TO NEO4J MIGRATION SERVICE
# Migrates existing PostgreSQL data to Neo4j with price-based relationships
# ================================================================================================

import os
import sys
from datetime import datetime
from typing import Dict, Any, Optional, List, Tuple
from neo4j import GraphDatabase
import psycopg2
from psycopg2.extras import RealDictCursor
from loguru import logger

class PostgresToNeo4jMigration:
    def __init__(self, 
                 postgres_config: Dict[str, Any],
                 neo4j_config: Dict[str, Any],
                 namespace: str = "TSS"):
        """
        Initialize migration service with PostgreSQL and Neo4j configurations
        """
        self.postgres_config = postgres_config
        self.neo4j_config = neo4j_config
        self.postgres_conn = None
        self.neo4j_driver = None
        self.namespace = namespace
        
    def get_namespaced_label(self, base_label: str) -> str:
        """Get namespaced label for nodes"""
        return f"{base_label}:{self.namespace}"
    
    def get_namespaced_relationship(self, base_relationship: str) -> str:
        """Get namespaced relationship type"""
        return f"{base_relationship}_{self.namespace}"
        
    def connect_postgres(self):
        """Connect to PostgreSQL database"""
        try:
            self.postgres_conn = psycopg2.connect(**self.postgres_config)
            logger.info("✅ Connected to PostgreSQL successfully")
            return True
        except Exception as e:
            logger.error(f"❌ PostgreSQL connection failed: {e}")
            return False
    
    def connect_neo4j(self):
        """Connect to Neo4j database"""
        try:
            self.neo4j_driver = GraphDatabase.driver(
                self.neo4j_config["uri"],
                auth=(self.neo4j_config["user"], self.neo4j_config["password"])
            )
            self.neo4j_driver.verify_connectivity()
            logger.info("✅ Connected to Neo4j successfully")
            return True
        except Exception as e:
            logger.error(f"❌ Neo4j connection failed: {e}")
            return False
    
    def close_connections(self):
        """Close all database connections"""
        if self.postgres_conn:
            self.postgres_conn.close()
        if self.neo4j_driver:
            self.neo4j_driver.close()
    
    def clear_conflicting_nodes(self):
        """Clear nodes that might cause constraint conflicts"""
        logger.info("🧹 Clearing potentially conflicting nodes...")
        
        # Remove any PriceTier nodes that don't have namespace labels
        self.run_neo4j_query(f"""
            MATCH (n:PriceTier) 
            WHERE NOT '{self.namespace}' IN labels(n) 
            AND NOT 'TM' IN labels(n)
            DETACH DELETE n
        """)
        
        # Remove any TechStack nodes that don't have namespace labels
        self.run_neo4j_query(f"""
            MATCH (n:TechStack) 
            WHERE NOT '{self.namespace}' IN labels(n) 
            AND NOT 'TM' IN labels(n)
            DETACH DELETE n
        """)
        
        # Remove any Domain nodes that don't have namespace labels
        self.run_neo4j_query(f"""
            MATCH (n:Domain) 
            WHERE NOT '{self.namespace}' IN labels(n) 
            AND NOT 'TM' IN labels(n)
            DETACH DELETE n
        """)
        
        logger.info("✅ Conflicting nodes cleared")
    
    def run_postgres_query(self, query: str, params: Optional[Dict] = None):
        """Execute PostgreSQL query and return results"""
        with self.postgres_conn.cursor(cursor_factory=RealDictCursor) as cursor:
            cursor.execute(query, params or {})
            return cursor.fetchall()
    
    def run_neo4j_query(self, query: str, params: Optional[Dict] = None):
        """Execute Neo4j query"""
        with self.neo4j_driver.session() as session:
            result = session.run(query, params or {})
            return [record.data() for record in result]
    
    def migrate_price_tiers(self):
        """Migrate price tiers from PostgreSQL to Neo4j"""
        logger.info("🔄 Migrating price tiers...")
        
        # Get price tiers from PostgreSQL
        price_tiers = self.run_postgres_query("""
            SELECT id, tier_name, min_price_usd, max_price_usd, 
                   target_audience, typical_project_scale, description
            FROM price_tiers
            ORDER BY min_price_usd
        """)
        
        # Create price tier nodes in Neo4j
        for tier in price_tiers:
            # Convert decimal values to float
            tier_data = dict(tier)
            tier_data['min_price_usd'] = float(tier_data['min_price_usd'])
            tier_data['max_price_usd'] = float(tier_data['max_price_usd'])
            
            query = f"""
            CREATE (p:{self.get_namespaced_label('PriceTier')} {{
                id: $id,
                tier_name: $tier_name,
                min_price_usd: $min_price_usd,
                max_price_usd: $max_price_usd,
                target_audience: $target_audience,
                typical_project_scale: $typical_project_scale,
                description: $description,
                migrated_at: datetime()
            }})
            """
            self.run_neo4j_query(query, tier_data)
        
        logger.info(f"✅ Migrated {len(price_tiers)} price tiers")
        return len(price_tiers)
    
    def migrate_technologies(self):
        """Migrate all technology categories from PostgreSQL to Neo4j"""
        logger.info("🔄 Migrating technologies...")
        
        technology_tables = [
            ("frontend_technologies", "frontend"),
            ("backend_technologies", "backend"),
            ("database_technologies", "database"),
            ("cloud_technologies", "cloud"),
            ("testing_technologies", "testing"),
            ("mobile_technologies", "mobile"),
            ("devops_technologies", "devops"),
            ("ai_ml_technologies", "ai_ml")
        ]
        
        total_technologies = 0
        
        for table_name, category in technology_tables:
            logger.info(f"  📊 Migrating {category} technologies...")
            
            # Get technologies from PostgreSQL
            technologies = self.run_postgres_query(f"""
                SELECT * FROM {table_name}
                ORDER BY name
            """)
            
            # Create or update technology nodes in Neo4j
            for tech in technologies:
                # Convert PostgreSQL row to Neo4j properties
                properties = dict(tech)
                properties['category'] = category
                properties['migrated_at'] = datetime.now().isoformat()
                
                # Convert decimal values to float
                for key, value in properties.items():
                    if hasattr(value, '__class__') and 'Decimal' in str(value.__class__):
                        properties[key] = float(value)
                
                # Use MERGE to create or update existing technology nodes
                # This will work with existing TM technology nodes
                query = f"""
                MERGE (t:Technology {{name: $name}})
                ON CREATE SET t += {{
                    {', '.join([f'{k}: ${k}' for k in properties.keys() if k != 'name'])}
                }}
                ON MATCH SET t += {{
                    {', '.join([f'{k}: ${k}' for k in properties.keys() if k != 'name'])}
                }}
                SET t:{self.get_namespaced_label('Technology')}
                """
                self.run_neo4j_query(query, properties)
            
            logger.info(f"    ✅ Migrated {len(technologies)} {category} technologies")
            total_technologies += len(technologies)
        
        logger.info(f"✅ Total technologies migrated: {total_technologies}")
        return total_technologies
    
    def migrate_tech_pricing(self):
        """Migrate technology pricing data"""
        logger.info("🔄 Migrating technology pricing...")
        
        # Get tech pricing from PostgreSQL
        pricing_data = self.run_postgres_query("""
            SELECT tp.*, pt.tier_name as price_tier_name
            FROM tech_pricing tp
            JOIN price_tiers pt ON tp.price_tier_id = pt.id
            ORDER BY tp.tech_name
        """)
        
        # Update technologies with pricing data
        for pricing in pricing_data:
            # Convert decimal values to float
            pricing_dict = dict(pricing)
            for key, value in pricing_dict.items():
                if hasattr(value, '__class__') and 'Decimal' in str(value.__class__):
                    pricing_dict[key] = float(value)
            
            # Update technology with pricing
            query = f"""
            MATCH (t:{self.get_namespaced_label('Technology')} {{name: $tech_name}})
            SET t.monthly_cost_usd = $monthly_operational_cost_usd,
                t.setup_cost_usd = $development_cost_usd,
                t.license_cost_usd = $license_cost_usd,
                t.training_cost_usd = $training_cost_usd,
                t.total_cost_of_ownership_score = $total_cost_of_ownership_score,
                t.price_performance_ratio = $price_performance_ratio,
                t.price_tier_name = $price_tier_name,
                t.min_cpu_cores = $min_cpu_cores,
                t.min_ram_gb = $min_ram_gb,
                t.min_storage_gb = $min_storage_gb
            """
            self.run_neo4j_query(query, pricing_dict)
        
        logger.info(f"✅ Updated {len(pricing_data)} technologies with pricing data")
        return len(pricing_data)
    
    def migrate_price_based_stacks(self):
        """Migrate complete tech stacks from price_based_stacks table"""
        logger.info("🔄 Migrating price-based tech stacks...")
        
        # Get price-based stacks from PostgreSQL
        stacks = self.run_postgres_query("""
            SELECT pbs.*, pt.tier_name as price_tier_name
            FROM price_based_stacks pbs
            JOIN price_tiers pt ON pbs.price_tier_id = pt.id
            ORDER BY pbs.total_monthly_cost_usd
        """)
        
        # Create tech stack nodes in Neo4j
        for stack in stacks:
            # Convert decimal values to float
            stack_dict = dict(stack)
            for key, value in stack_dict.items():
                if hasattr(value, '__class__') and 'Decimal' in str(value.__class__):
                    stack_dict[key] = float(value)
            
            # Create or update the tech stack node
            query = f"""
            MERGE (s:TechStack {{name: $stack_name}})
            ON CREATE SET s += {{
                monthly_cost: $total_monthly_cost_usd,
                setup_cost: $total_setup_cost_usd,
                team_size_range: $team_size_range,
                development_time_months: $development_time_months,
                satisfaction_score: $user_satisfaction_score,
                success_rate: $success_rate_percentage,
                price_tier: $price_tier_name,
                maintenance_complexity: $maintenance_complexity,
                scalability_ceiling: $scalability_ceiling,
                recommended_domains: $recommended_domains,
                description: $description,
                pros: $pros,
                cons: $cons,
                frontend_tech: $frontend_tech,
                backend_tech: $backend_tech,
                database_tech: $database_tech,
                cloud_tech: $cloud_tech,
                testing_tech: $testing_tech,
                mobile_tech: $mobile_tech,
                devops_tech: $devops_tech,
                ai_ml_tech: $ai_ml_tech,
                migrated_at: datetime()
            }}
            ON MATCH SET s += {{
                monthly_cost: $total_monthly_cost_usd,
                setup_cost: $total_setup_cost_usd,
                team_size_range: $team_size_range,
                development_time_months: $development_time_months,
                satisfaction_score: $user_satisfaction_score,
                success_rate: $success_rate_percentage,
                price_tier: $price_tier_name,
                maintenance_complexity: $maintenance_complexity,
                scalability_ceiling: $scalability_ceiling,
                recommended_domains: $recommended_domains,
                description: $description,
                pros: $pros,
                cons: $cons,
                frontend_tech: $frontend_tech,
                backend_tech: $backend_tech,
                database_tech: $database_tech,
                cloud_tech: $cloud_tech,
                testing_tech: $testing_tech,
                mobile_tech: $mobile_tech,
                devops_tech: $devops_tech,
                ai_ml_tech: $ai_ml_tech,
                migrated_at: datetime()
            }}
            SET s:{self.get_namespaced_label('TechStack')}
            """
            self.run_neo4j_query(query, stack_dict)
        
        logger.info(f"✅ Migrated {len(stacks)} price-based tech stacks")
        return len(stacks)
    
    def migrate_stack_recommendations(self):
        """Migrate domain-specific stack recommendations"""
        logger.info("🔄 Migrating stack recommendations...")
        
        # Get stack recommendations from PostgreSQL
        # Handle case where price_tier_id might be NULL
        recommendations = self.run_postgres_query("""
            SELECT sr.*, 
                   COALESCE(pt.tier_name, 'Not Specified') as price_tier_name, 
                   pbs.stack_name,
                   pbs.price_tier_id as stack_price_tier_id
            FROM stack_recommendations sr
            LEFT JOIN price_tiers pt ON sr.price_tier_id = pt.id
            JOIN price_based_stacks pbs ON sr.recommended_stack_id = pbs.id
            ORDER BY sr.business_domain, sr.confidence_score DESC
        """)
        
        # Create domain nodes and recommendations
        for rec in recommendations:
            # Convert arrays to lists
            rec_dict = dict(rec)
            for key, value in rec_dict.items():
                if hasattr(value, '__class__') and 'list' in str(value.__class__):
                    rec_dict[key] = list(value)
            
            # Create domain node
            domain_query = f"""
            MERGE (d:{self.get_namespaced_label('Domain')} {{name: $business_domain}})
            SET d.project_scale = $project_scale,
                d.team_experience_level = $team_experience_level
            """
            self.run_neo4j_query(domain_query, rec_dict)
            
            # Get the actual price tier for the stack
            stack_tier_query = f"""
            MATCH (s:{self.get_namespaced_label('TechStack')} {{name: $stack_name}})-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')}]->(pt:{self.get_namespaced_label('PriceTier')})
            RETURN pt.tier_name as actual_tier_name
            """
            tier_result = self.run_neo4j_query(stack_tier_query, {"stack_name": rec_dict["stack_name"]})
            actual_tier = tier_result[0]["actual_tier_name"] if tier_result else rec_dict["price_tier_name"]
            
            # Create recommendation relationship
            rec_query = f"""
            MATCH (d:{self.get_namespaced_label('Domain')} {{name: $business_domain}})
            MATCH (s:{self.get_namespaced_label('TechStack')} {{name: $stack_name}})
            CREATE (d)-[:{self.get_namespaced_relationship('RECOMMENDS')} {{
                confidence_score: $confidence_score,
                recommendation_reasons: $recommendation_reasons,
                potential_risks: $potential_risks,
                alternative_stacks: $alternative_stacks,
                price_tier: $actual_tier
            }}]->(s)
            """
            rec_dict["actual_tier"] = actual_tier
            self.run_neo4j_query(rec_query, rec_dict)
        
        logger.info(f"✅ Migrated {len(recommendations)} stack recommendations")
        return len(recommendations)
    
    def migrate_tools(self):
        """Migrate tools with pricing from PostgreSQL to Neo4j"""
        logger.info("🔄 Migrating tools with pricing...")
        
        # Get tools with pricing from PostgreSQL
        tools = self.run_postgres_query("""
            SELECT t.*, pt.tier_name as price_tier_name
            FROM tools t
            LEFT JOIN price_tiers pt ON t.price_tier_id = pt.id
            ORDER BY t.name
        """)
        
        # Create tool nodes in Neo4j
        for tool in tools:
            properties = dict(tool)
            properties['migrated_at'] = datetime.now().isoformat()
            
            # Convert decimal values to float
            for key, value in properties.items():
                if hasattr(value, '__class__') and 'Decimal' in str(value.__class__):
                    properties[key] = float(value)
            
            # Create or update the tool node (use MERGE to handle duplicates)
            query = f"""
            MERGE (tool:Tool {{name: $name}})
            ON CREATE SET tool += {{
                {', '.join([f'{k}: ${k}' for k in properties.keys() if k != 'name'])}
            }}
            ON MATCH SET tool += {{
                {', '.join([f'{k}: ${k}' for k in properties.keys() if k != 'name'])}
            }}
            SET tool:{self.get_namespaced_label('Tool')}
            """
            self.run_neo4j_query(query, properties)
        
        logger.info(f"✅ Migrated {len(tools)} tools")
        return len(tools)
    
    def create_price_relationships(self):
        """Create price-based relationships between technologies/tools and price tiers"""
        logger.info("🔗 Creating price-based relationships...")
        
        # Create relationships for technologies
        technology_categories = ["frontend", "backend", "database", "cloud", "testing", "mobile", "devops", "ai_ml"]
        
        for category in technology_categories:
            logger.info(f"  📊 Creating price relationships for {category} technologies...")
            
            # Get technologies and their price tiers
            query = f"""
            MATCH (t:{self.get_namespaced_label('Technology')} {{category: '{category}'}})
            MATCH (p:{self.get_namespaced_label('PriceTier')})
            WHERE t.monthly_cost_usd >= p.min_price_usd 
            AND t.monthly_cost_usd <= p.max_price_usd
            CREATE (t)-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')} {{
                fit_score: CASE 
                    WHEN t.monthly_cost_usd = 0.0 THEN 100.0
                    ELSE 100.0 - ((t.monthly_cost_usd - p.min_price_usd) / (p.max_price_usd - p.min_price_usd) * 20.0)
                END,
                cost_efficiency: t.total_cost_of_ownership_score,
                price_performance: t.price_performance_ratio
            }}]->(p)
            RETURN count(*) as relationships_created
            """
            
            result = self.run_neo4j_query(query)
            if result:
                logger.info(f"    ✅ Created {result[0]['relationships_created']} price relationships for {category}")
        
        # Create relationships for tools
        logger.info("  📊 Creating price relationships for tools...")
        query = f"""
        MATCH (tool:{self.get_namespaced_label('Tool')})
        MATCH (p:{self.get_namespaced_label('PriceTier')})
        WHERE tool.monthly_cost_usd >= p.min_price_usd 
        AND tool.monthly_cost_usd <= p.max_price_usd
        CREATE (tool)-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')} {{
            fit_score: CASE 
                WHEN tool.monthly_cost_usd = 0.0 THEN 100.0
                ELSE 100.0 - ((tool.monthly_cost_usd - p.min_price_usd) / (p.max_price_usd - p.min_price_usd) * 20.0)
            END,
            cost_efficiency: tool.total_cost_of_ownership_score,
            price_performance: tool.price_performance_ratio
        }}]->(p)
        RETURN count(*) as relationships_created
        """
        
        result = self.run_neo4j_query(query)
        if result:
            logger.info(f"    ✅ Created {result[0]['relationships_created']} price relationships for tools")
    
    def create_technology_compatibility_relationships(self):
        """Create compatibility relationships between technologies"""
        logger.info("🔗 Creating technology compatibility relationships...")
        
        query = f"""
        MATCH (t1:{self.get_namespaced_label('Technology')}), (t2:{self.get_namespaced_label('Technology')})
        WHERE t1.name <> t2.name
        AND (
            // Same category, different technologies
            (t1.category = t2.category AND t1.name <> t2.name) OR
            // Frontend-Backend compatibility
            (t1.category = "frontend" AND t2.category = "backend") OR
            (t1.category = "backend" AND t2.category = "frontend") OR
            // Backend-Database compatibility
            (t1.category = "backend" AND t2.category = "database") OR
            (t1.category = "database" AND t2.category = "backend") OR
            // Cloud compatibility with all
            (t1.category = "cloud" AND t2.category IN ["frontend", "backend", "database"]) OR
            (t2.category = "cloud" AND t1.category IN ["frontend", "backend", "database"])
        )
        MERGE (t1)-[r:{self.get_namespaced_relationship('COMPATIBLE_WITH')} {{
            compatibility_score: CASE 
                WHEN t1.category = t2.category THEN 0.8
                WHEN (t1.category = "frontend" AND t2.category = "backend") THEN 0.9
                WHEN (t1.category = "backend" AND t2.category = "database") THEN 0.9
                WHEN (t1.category = "cloud" AND t2.category IN ["frontend", "backend", "database"]) THEN 0.85
                ELSE 0.7
            END,
            integration_effort: CASE 
                WHEN t1.category = t2.category THEN "Low"
                WHEN (t1.category = "frontend" AND t2.category = "backend") THEN "Medium"
                WHEN (t1.category = "backend" AND t2.category = "database") THEN "Low"
                WHEN (t1.category = "cloud" AND t2.category IN ["frontend", "backend", "database"]) THEN "Low"
                ELSE "High"
            END,
            reason: "Auto-generated compatibility relationship",
            created_at: datetime()
        }}]->(t2)
        RETURN count(r) as relationships_created
        """
        
        result = self.run_neo4j_query(query)
        if result:
            logger.info(f"✅ Created {result[0]['relationships_created']} compatibility relationships")
    
    def create_tech_stack_relationships(self):
        """Create relationships between tech stacks and their technologies"""
        logger.info("🔗 Creating tech stack relationships...")
        
        # Create relationships for each technology type separately
        tech_relationships = [
            ("frontend_tech", self.get_namespaced_relationship("USES_FRONTEND"), "frontend"),
            ("backend_tech", self.get_namespaced_relationship("USES_BACKEND"), "backend"),
            ("database_tech", self.get_namespaced_relationship("USES_DATABASE"), "database"),
            ("cloud_tech", self.get_namespaced_relationship("USES_CLOUD"), "cloud"),
            ("testing_tech", self.get_namespaced_relationship("USES_TESTING"), "testing"),
            ("mobile_tech", self.get_namespaced_relationship("USES_MOBILE"), "mobile"),
            ("devops_tech", self.get_namespaced_relationship("USES_DEVOPS"), "devops"),
            ("ai_ml_tech", self.get_namespaced_relationship("USES_AI_ML"), "ai_ml")
        ]
        
        total_relationships = 0
        
        for tech_field, relationship_type, category in tech_relationships:
            # For testing technologies, also check frontend category since some testing tools are categorized as frontend
            if category == "testing":
                query = f"""
                MATCH (s:{self.get_namespaced_label('TechStack')})
                WHERE s.{tech_field} IS NOT NULL
                MATCH (t:{self.get_namespaced_label('Technology')} {{name: s.{tech_field}}})
                WHERE t.category = '{category}' OR (t.category = 'frontend' AND s.{tech_field} IN ['Jest', 'Cypress', 'Playwright', 'Selenium', 'Vitest', 'Testing Library'])
                MERGE (s)-[:{relationship_type} {{role: '{category}', importance: 'critical'}}]->(t)
                RETURN count(s) as relationships_created
                """
            else:
                query = f"""
                MATCH (s:{self.get_namespaced_label('TechStack')})
                WHERE s.{tech_field} IS NOT NULL
                MATCH (t:{self.get_namespaced_label('Technology')} {{name: s.{tech_field}, category: '{category}'}})
                MERGE (s)-[:{relationship_type} {{role: '{category}', importance: 'critical'}}]->(t)
                RETURN count(s) as relationships_created
                """
            
            result = self.run_neo4j_query(query)
            if result:
                count = result[0]['relationships_created']
                total_relationships += count
                logger.info(f"    ✅ Created {count} {relationship_type} relationships")
        
        logger.info(f"✅ Created {total_relationships} total tech stack relationships")
        
        # Create price tier relationships for tech stacks
        price_tier_query = f"""
        MATCH (s:{self.get_namespaced_label('TechStack')})
        MATCH (p:{self.get_namespaced_label('PriceTier')} {{tier_name: s.price_tier}})
        MERGE (s)-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')} {{fit_score: 100.0}}]->(p)
        RETURN count(s) as relationships_created
        """
        
        result = self.run_neo4j_query(price_tier_query)
        if result:
            logger.info(f"✅ Created price tier relationships for {result[0]['relationships_created']} tech stacks")
    
    def create_optimal_tech_stacks(self, max_stacks_per_tier: int = 5):
        """Create optimal tech stacks based on price tiers and compatibility"""
        logger.info("🏗️ Creating optimal tech stacks...")
        
        # Get price tiers
        price_tiers = self.run_neo4j_query(f"MATCH (p:{self.get_namespaced_label('PriceTier')}) RETURN p ORDER BY p.min_price_usd")
        
        total_stacks = 0
        
        for tier in price_tiers:
            tier_name = tier['p']['tier_name']
            min_price = tier['p']['min_price_usd']
            max_price = tier['p']['max_price_usd']
            
            logger.info(f"  📊 Creating stacks for {tier_name} (${min_price}-${max_price})...")
            
            # Find optimal combinations within this price tier
            query = f"""
            MATCH (frontend:{self.get_namespaced_label('Technology')} {{category: "frontend"}})-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')}]->(p:{self.get_namespaced_label('PriceTier')} {{tier_name: $tier_name}})
            MATCH (backend:{self.get_namespaced_label('Technology')} {{category: "backend"}})-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')}]->(p)
            MATCH (database:{self.get_namespaced_label('Technology')} {{category: "database"}})-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')}]->(p)
            MATCH (cloud:{self.get_namespaced_label('Technology')} {{category: "cloud"}})-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')}]->(p)
            
            WITH frontend, backend, database, cloud, p,
                 (frontend.monthly_cost_usd + backend.monthly_cost_usd + 
                  database.monthly_cost_usd + cloud.monthly_cost_usd) as total_cost,
                 (frontend.total_cost_of_ownership_score + backend.total_cost_of_ownership_score + 
                  database.total_cost_of_ownership_score + cloud.total_cost_of_ownership_score) as total_score
            
            WHERE total_cost >= p.min_price_usd AND total_cost <= p.max_price_usd
            
            WITH frontend, backend, database, cloud, total_cost, total_score,
                 (total_score / 4.0) as avg_score,
                 (100.0 - ((total_cost - p.min_price_usd) / (p.max_price_usd - p.min_price_usd) * 20.0)) as budget_efficiency
            
            ORDER BY avg_score DESC, budget_efficiency DESC, total_cost ASC
            LIMIT $max_stacks
            
            CREATE (s:{self.get_namespaced_label('TechStack')} {{
                name: "Optimal " + $tier_name + " Stack - $" + toString(round(total_cost)) + "/month",
                monthly_cost: total_cost,
                setup_cost: total_cost * 0.5,
                team_size_range: CASE 
                    WHEN $tier_name = "Micro Budget" THEN "1-2"
                    WHEN $tier_name = "Startup Budget" THEN "2-4"
                    WHEN $tier_name = "Small Business" THEN "3-6"
                    WHEN $tier_name = "Growth Stage" THEN "5-10"
                    ELSE "8-15"
                END,
                development_time_months: CASE 
                    WHEN $tier_name = "Micro Budget" THEN 1
                    WHEN $tier_name = "Startup Budget" THEN 2
                    WHEN $tier_name = "Small Business" THEN 3
                    WHEN $tier_name = "Growth Stage" THEN 4
                    ELSE 6
                END,
                satisfaction_score: toInteger(avg_score),
                success_rate: toInteger(avg_score * 0.9),
                price_tier: $tier_name,
                budget_efficiency: budget_efficiency,
                created_at: datetime()
            }})
            
            CREATE (s)-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')} {{fit_score: budget_efficiency}}]->(p)
            CREATE (s)-[:{self.get_namespaced_relationship('USES_FRONTEND')} {{role: "frontend", importance: "critical"}}]->(frontend)
            CREATE (s)-[:{self.get_namespaced_relationship('USES_BACKEND')} {{role: "backend", importance: "critical"}}]->(backend)
            CREATE (s)-[:{self.get_namespaced_relationship('USES_DATABASE')} {{role: "database", importance: "critical"}}]->(database)
            CREATE (s)-[:{self.get_namespaced_relationship('USES_CLOUD')} {{role: "cloud", importance: "critical"}}]->(cloud)
            
            RETURN count(s) as stacks_created
            """
            
            result = self.run_neo4j_query(query, {
                "tier_name": tier_name,
                "max_stacks": max_stacks_per_tier
            })
            
            if result and result[0]['stacks_created'] > 0:
                stacks_created = result[0]['stacks_created']
                logger.info(f"    ✅ Created {stacks_created} optimal stacks for {tier_name}")
                total_stacks += stacks_created
        
        logger.info(f"✅ Total tech stacks created: {total_stacks}")
        return total_stacks
    
    def validate_migration(self):
        """Validate the migration results"""
        logger.info("🔍 Validating migration...")
        
        # Count nodes
        node_counts = self.run_neo4j_query("""
            MATCH (n)
            RETURN labels(n)[0] as label, count(n) as count
            ORDER BY count DESC
        """)
        
        logger.info("📊 Node counts:")
        for item in node_counts:
            logger.info(f"  {item['label']}: {item['count']}")
        
        # Count relationships
        rel_counts = self.run_neo4j_query("""
            MATCH ()-[r]->()
            RETURN type(r) as type, count(r) as count
            ORDER BY count DESC
        """)
        
        logger.info("🔗 Relationship counts:")
        for item in rel_counts:
            logger.info(f"  {item['type']}: {item['count']}")
        
        # Validate tech stacks
        stack_validation = self.run_neo4j_query(f"""
            MATCH (s:{self.get_namespaced_label('TechStack')})
            RETURN s.name,
                   exists((s)-[:{self.get_namespaced_relationship('BELONGS_TO_TIER')}]->()) as has_price_tier,
                   exists((s)-[:{self.get_namespaced_relationship('USES_FRONTEND')}]->()) as has_frontend,
                   exists((s)-[:{self.get_namespaced_relationship('USES_BACKEND')}]->()) as has_backend,
                   exists((s)-[:{self.get_namespaced_relationship('USES_DATABASE')}]->()) as has_database,
                   exists((s)-[:{self.get_namespaced_relationship('USES_CLOUD')}]->()) as has_cloud
        """)
        
        complete_stacks = [s for s in stack_validation if all([
            s['has_price_tier'], s['has_frontend'], s['has_backend'], 
            s['has_database'], s['has_cloud']
        ])]
        
        logger.info(f"✅ Complete tech stacks: {len(complete_stacks)}/{len(stack_validation)}")
        
        return {
            "node_counts": node_counts,
            "relationship_counts": rel_counts,
            "complete_stacks": len(complete_stacks),
            "total_stacks": len(stack_validation)
        }
    
    def run_full_migration(self):
        """Run the complete migration process"""
        logger.info("🚀 Starting PostgreSQL to Neo4j migration...")
        
        try:
            # Connect to databases
            if not self.connect_postgres():
                return False
            if not self.connect_neo4j():
                return False
            
            # Clear Neo4j TSS namespace data only (preserve TM data)
            logger.info(f"🧹 Clearing Neo4j {self.namespace} namespace data...")
            
            # First, remove any existing TSS namespaced data
            logger.info("🧹 Removing existing TSS namespaced data...")
            self.run_neo4j_query(f"MATCH (n) WHERE '{self.namespace}' IN labels(n) DETACH DELETE n")
            
            # Clear potentially conflicting nodes
            self.clear_conflicting_nodes()
            
            logger.info("✅ Cleanup completed - TSS and conflicting nodes removed")
            
            # Run migrations
            price_tiers_count = self.migrate_price_tiers()
            technologies_count = self.migrate_technologies()
            tech_pricing_count = self.migrate_tech_pricing()
            price_based_stacks_count = self.migrate_price_based_stacks()
            stack_recommendations_count = self.migrate_stack_recommendations()
            tools_count = self.migrate_tools()
            
            # Create relationships
            self.create_price_relationships()
            self.create_technology_compatibility_relationships()
            self.create_tech_stack_relationships()
            
            # Create optimal tech stacks (only if no existing stacks)
            if price_based_stacks_count == 0:
                stacks_count = self.create_optimal_tech_stacks()
            else:
                stacks_count = price_based_stacks_count
            
            # Validate migration
            validation = self.validate_migration()
            
            logger.info("🎉 Migration completed successfully!")
            logger.info(f"📊 Summary:")
            logger.info(f"  Price tiers: {price_tiers_count}")
            logger.info(f"  Technologies: {technologies_count}")
            logger.info(f"  Tech pricing: {tech_pricing_count}")
            logger.info(f"  Price-based stacks: {price_based_stacks_count}")
            logger.info(f"  Stack recommendations: {stack_recommendations_count}")
            logger.info(f"  Tools: {tools_count}")
            logger.info(f"  Total tech stacks: {stacks_count}")
            logger.info(f"  Complete stacks: {validation['complete_stacks']}/{validation['total_stacks']}")
            
            return True
            
        except Exception as e:
            logger.error(f"❌ Migration failed: {e}")
            return False
        finally:
            self.close_connections()

# ================================================================================================
# MAIN EXECUTION
# ================================================================================================

if __name__ == "__main__":
    # Configuration
    postgres_config = {
        "host": os.getenv("POSTGRES_HOST", "localhost"),
        "port": int(os.getenv("POSTGRES_PORT", "5432")),
        "user": os.getenv("POSTGRES_USER", "pipeline_admin"),
        "password": os.getenv("POSTGRES_PASSWORD", "secure_pipeline_2024"),
        "database": os.getenv("POSTGRES_DB", "dev_pipeline")
    }
    
    neo4j_config = {
        "uri": os.getenv("NEO4J_URI", "bolt://localhost:7687"),
        "user": os.getenv("NEO4J_USER", "neo4j"),
        "password": os.getenv("NEO4J_PASSWORD", "password")
    }
    
    # Run migration
    migration = PostgresToNeo4jMigration(postgres_config, neo4j_config)
    success = migration.run_full_migration()
    
    if success:
        logger.info("✅ Migration completed successfully!")
        sys.exit(0)
    else:
        logger.error("❌ Migration failed!")
        sys.exit(1)
