from rest_framework import serializers
from .models import Clusters, Keywords, ContentIdeas
from django.db.models import Count, Sum, Avg


class ClusterSerializer(serializers.ModelSerializer):
    """Serializer for Clusters model with dynamically calculated keywords_count, volume, and difficulty"""
    keywords_count = serializers.SerializerMethodField()
    volume = serializers.SerializerMethodField()
    difficulty = serializers.SerializerMethodField()
    sector_name = serializers.SerializerMethodField()
    ideas_count = serializers.SerializerMethodField()
    content_count = serializers.SerializerMethodField()
    site_id = serializers.IntegerField(write_only=True, required=False)
    sector_id = serializers.IntegerField(write_only=True, required=False)
    
    class Meta:
        model = Clusters
        fields = [
            'id',
            'name',
            'description',
            'keywords_count',
            'volume',
            'difficulty',
            'mapped_pages',
            'ideas_count',
            'content_count',
            'status',
            'sector_name',
            'site_id',
            'sector_id',
            'created_at',
        ]
        read_only_fields = ['id', 'created_at']
    
    def get_sector_name(self, obj):
        """Get sector name from Sector model"""
        if obj.sector_id:
            try:
                from igny8_core.auth.models import Sector
                sector = Sector.objects.get(id=obj.sector_id)
                return sector.name
            except Sector.DoesNotExist:
                return None
        return None
    
    def get_keywords_count(self, obj):
        """Calculate actual count of keywords associated with this cluster"""
        # Get cached data if available (set by viewset)
        if hasattr(obj, '_keywords_count'):
            return obj._keywords_count
        
        # Fallback: calculate on the fly (single query per cluster)
        return Keywords.objects.filter(cluster_id=obj.id).count()
    
    def get_volume(self, obj):
        """Calculate total volume of keywords associated with this cluster"""
        # Get cached data if available (set by viewset)
        if hasattr(obj, '_volume'):
            return obj._volume
        
        # Fallback: calculate on the fly (single query per cluster)
        # Since volume is a property, we need to use COALESCE to check override first
        from django.db.models import Sum, Case, When, F, IntegerField
        result = Keywords.objects.filter(cluster_id=obj.id).aggregate(
            total=Sum(
                Case(
                    When(volume_override__isnull=False, then=F('volume_override')),
                    default=F('seed_keyword__volume'),
                    output_field=IntegerField()
                )
            )
        )
        return result['total'] or 0
    
    def get_difficulty(self, obj):
        """Calculate average difficulty of keywords associated with this cluster"""
        # Get cached data if available (set by viewset)
        if hasattr(obj, '_difficulty'):
            return obj._difficulty
        
        # Fallback: calculate on the fly (single query per cluster)
        # Since difficulty is a property, we need to use COALESCE to check override first
        from django.db.models import Avg, Case, When, F, IntegerField
        result = Keywords.objects.filter(cluster_id=obj.id).aggregate(
            avg_difficulty=Avg(
                Case(
                    When(difficulty_override__isnull=False, then=F('difficulty_override')),
                    default=F('seed_keyword__difficulty'),
                    output_field=IntegerField()
                )
            )
        )
        return round(result['avg_difficulty'] or 0, 1)  # Round to 1 decimal place

    def get_ideas_count(self, obj):
        """Count content ideas linked to this cluster"""
        if hasattr(obj, '_ideas_count'):
            return obj._ideas_count

        return ContentIdeas.objects.filter(keyword_cluster_id=obj.id).count()

    def get_content_count(self, obj):
        """Count generated content items linked to this cluster via tasks"""
        if hasattr(obj, '_content_count'):
            return obj._content_count

        from igny8_core.modules.writer.models import Content

        return Content.objects.filter(task__cluster_id=obj.id).count()
    
    @classmethod
    def prefetch_keyword_stats(cls, clusters):
        """
        Optimize keyword count, volume, and difficulty calculation by fetching all stats in bulk
        This prevents N+1 queries by doing a single aggregate query
        """
        if not clusters:
            return clusters
        
        # Get all cluster IDs
        cluster_ids = [cluster.id for cluster in clusters]
        
        # Aggregate keyword counts, volumes, and difficulties for all clusters in one query
        from django.db.models import Count, Sum, Avg, Case, When, F, IntegerField
        keyword_stats = (
            Keywords.objects
            .filter(cluster_id__in=cluster_ids)
            .values('cluster_id')
            .annotate(
                count=Count('id'),
                total_volume=Sum(
                    Case(
                        When(volume_override__isnull=False, then=F('volume_override')),
                        default=F('seed_keyword__volume'),
                        output_field=IntegerField()
                    )
                ),
                avg_difficulty=Avg(
                    Case(
                        When(difficulty_override__isnull=False, then=F('difficulty_override')),
                        default=F('seed_keyword__difficulty'),
                        output_field=IntegerField()
                    )
                )
            )
        )
        
        # Create a dictionary mapping cluster_id to stats
        stats_dict = {
            stat['cluster_id']: {
                'count': stat['count'],
                'volume': stat['total_volume'] or 0,
                'difficulty': round(stat['avg_difficulty'] or 0, 1)  # Round to 1 decimal place
            }
            for stat in keyword_stats
        }
        
        # Prefetch idea counts
        idea_counts = (
            ContentIdeas.objects
            .filter(keyword_cluster_id__in=cluster_ids)
            .values('keyword_cluster_id')
            .annotate(count=Count('id'))
        )
        idea_stats = {item['keyword_cluster_id']: item['count'] for item in idea_counts}

        # Prefetch content counts (through writer.Tasks -> Content)
        from igny8_core.modules.writer.models import Content

        content_counts = (
            Content.objects
            .filter(task__cluster_id__in=cluster_ids)
            .values('task__cluster_id')
            .annotate(count=Count('id'))
        )
        content_stats = {item['task__cluster_id']: item['count'] for item in content_counts}

        # Attach stats to each cluster object
        for cluster in clusters:
            cluster_stats = stats_dict.get(cluster.id, {'count': 0, 'volume': 0, 'difficulty': 0})
            cluster._keywords_count = cluster_stats['count']
            cluster._volume = cluster_stats['volume']
            cluster._difficulty = cluster_stats['difficulty']
            cluster._ideas_count = idea_stats.get(cluster.id, 0)
            cluster._content_count = content_stats.get(cluster.id, 0)
        
        return clusters

