# -*- coding: utf-8 -*-
"""
Advertisers App Views

This module contains all view classes for the Advertisers application.
It provides comprehensive API endpoints for managing advertisers, agencies,
brands, and their relationships using Django REST Framework.
"""

from decimal import Decimal
from django.db.models import Q, Count, Sum, Avg
from django.utils.translation import gettext_lazy as _
from django.shortcuts import get_object_or_404
from django.core.exceptions import ValidationError
from django.http import Http404

from rest_framework import viewsets, status, permissions
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as filters

from apps.core.permissions import IsOwnerOrReadOnly, IsAdminOrReadOnly
from apps.core.pagination import StandardResultsSetPagination
from apps.core.mixins import AuditMixin, CacheMixin

from .models import (
    BrandCategory,
    Agency,
    Advertiser,
    Brand,
    AdvertiserContact,
    AgencyUser,
    AdvertiserBilling
)
from .serializers import (
    BrandCategorySerializer,
    BrandCategoryDetailSerializer,
    AgencySerializer,
    AgencyDetailSerializer,
    AdvertiserSerializer,
    AdvertiserDetailSerializer,
    BrandSerializer,
    BrandDetailSerializer,
    AdvertiserContactSerializer,
    AgencyUserSerializer,
    AdvertiserBillingSerializer
)
from .filters import (
    BrandCategoryFilter,
    AgencyFilter,
    AdvertiserFilter,
    BrandFilter,
    AdvertiserContactFilter,
    AgencyUserFilter
)


class BrandCategoryViewSet(AuditMixin, CacheMixin, viewsets.ModelViewSet):
    """
    ViewSet for managing brand categories.
    
    Provides CRUD operations for brand categories with hierarchical support,
    filtering, searching, and statistical information.
    """
    
    queryset = BrandCategory.objects.all()
    serializer_class = BrandCategorySerializer
    permission_classes = [permissions.IsAuthenticated, IsAdminOrReadOnly]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = BrandCategoryFilter
    search_fields = ['name', 'description']
    ordering_fields = ['name', 'order', 'created_at']
    ordering = ['order', 'name']
    pagination_class = StandardResultsSetPagination
    
    def get_serializer_class(self):
        """Return appropriate serializer based on action."""
        if self.action == 'retrieve':
            return BrandCategoryDetailSerializer
        return BrandCategorySerializer
    
    def get_queryset(self):
        """Optimize queryset with related data."""
        queryset = super().get_queryset()
        return queryset.select_related('parent').prefetch_related(
            'subcategories', 'brands'
        ).annotate(
            brands_count=Count('brands', filter=Q(brands__is_active=True))
        )
    
    @action(detail=True, methods=['get'])
    def subcategories(self, request, pk=None):
        """
        Get all subcategories for a specific category.
        """
        category = self.get_object()
        subcategories = category.subcategories.filter(is_active=True)
        serializer = self.get_serializer(subcategories, many=True)
        return Response(serializer.data)
    
    @action(detail=True, methods=['get'])
    def brands(self, request, pk=None):
        """
        Get all brands in a specific category.
        """
        category = self.get_object()
        brands = category.brands.filter(is_active=True)
        
        # Apply pagination
        page = self.paginate_queryset(brands)
        if page is not None:
            serializer = BrandSerializer(page, many=True, context={'request': request})
            return self.get_paginated_response(serializer.data)
        
        serializer = BrandSerializer(brands, many=True, context={'request': request})
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def hierarchy(self, request):
        """
        Get the complete category hierarchy.
        """
        root_categories = self.get_queryset().filter(parent__isnull=True)
        serializer = BrandCategoryDetailSerializer(
            root_categories, many=True, context={'request': request}
        )
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def statistics(self, request):
        """
        Get category statistics.
        """
        queryset = self.get_queryset()
        stats = {
            'total_categories': queryset.count(),
            'active_categories': queryset.filter(is_active=True).count(),
            'root_categories': queryset.filter(parent__isnull=True).count(),
            'categories_with_brands': queryset.filter(brands_count__gt=0).count(),
            'total_brands': sum(cat.brands_count for cat in queryset)
        }
        return Response(stats)


class AgencyViewSet(AuditMixin, CacheMixin, viewsets.ModelViewSet):
    """
    ViewSet for managing advertising agencies.
    
    Provides CRUD operations for agencies with user management,
    advertiser relationships, and comprehensive statistics.
    """
    
    queryset = Agency.objects.all()
    serializer_class = AgencySerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = AgencyFilter
    search_fields = ['name', 'code', 'email', 'city']
    ordering_fields = ['name', 'code', 'created_at', 'commission_rate']
    ordering = ['name']
    pagination_class = StandardResultsSetPagination
    
    def get_serializer_class(self):
        """Return appropriate serializer based on action."""
        if self.action == 'retrieve':
            return AgencyDetailSerializer
        return AgencySerializer
    
    def get_queryset(self):
        """Optimize queryset with related data and annotations."""
        queryset = super().get_queryset()
        return queryset.select_related('created_by').prefetch_related(
            'advertisers', 'users'
        ).annotate(
            advertisers_count=Count('advertisers', filter=Q(advertisers__is_active=True)),
            users_count=Count('users', filter=Q(users__is_active=True))
        )
    
    def perform_create(self, serializer):
        """Set the created_by field when creating an agency."""
        serializer.save(created_by=self.request.user)
    
    @action(detail=True, methods=['get'])
    def advertisers(self, request, pk=None):
        """
        Get all advertisers managed by this agency.
        """
        agency = self.get_object()
        advertisers = agency.advertisers.filter(is_active=True)
        
        # Apply pagination
        page = self.paginate_queryset(advertisers)
        if page is not None:
            serializer = AdvertiserSerializer(page, many=True, context={'request': request})
            return self.get_paginated_response(serializer.data)
        
        serializer = AdvertiserSerializer(advertisers, many=True, context={'request': request})
        return Response(serializer.data)
    
    @action(detail=True, methods=['get'])
    def users(self, request, pk=None):
        """
        Get all users associated with this agency.
        """
        agency = self.get_object()
        agency_users = agency.users.filter(is_active=True)
        
        # Apply pagination
        page = self.paginate_queryset(agency_users)
        if page is not None:
            serializer = AgencyUserSerializer(page, many=True, context={'request': request})
            return self.get_paginated_response(serializer.data)
        
        serializer = AgencyUserSerializer(agency_users, many=True, context={'request': request})
        return Response(serializer.data)
    
    @action(detail=True, methods=['post'])
    def add_user(self, request, pk=None):
        """
        Add a user to this agency.
        """
        agency = self.get_object()
        serializer = AgencyUserSerializer(data=request.data, context={'request': request})
        
        if serializer.is_valid():
            serializer.save(agency=agency)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=True, methods=['get'])
    def statistics(self, request, pk=None):
        """
        Get comprehensive statistics for this agency.
        """
        agency = self.get_object()
        
        # Get advertiser statistics
        advertisers = agency.advertisers.filter(is_active=True)
        total_advertisers = advertisers.count()
        
        # Get brand statistics
        total_brands = sum(adv.total_brands for adv in advertisers)
        
        # Get campaign statistics (if campaigns app exists)
        try:
            from apps.campaigns.models import Campaign
            active_campaigns = Campaign.objects.filter(
                advertiser__agency=agency,
                status='active'
            ).count()
            total_budget = Campaign.objects.filter(
                advertiser__agency=agency
            ).aggregate(total=Sum('budget'))['total'] or Decimal('0.00')
        except ImportError:
            active_campaigns = 0
            total_budget = Decimal('0.00')
        
        stats = {
            'total_advertisers': total_advertisers,
            'total_brands': total_brands,
            'total_users': agency.users.filter(is_active=True).count(),
            'active_campaigns': active_campaigns,
            'total_budget': total_budget,
            'commission_rate': agency.commission_rate,
            'estimated_commission': total_budget * (agency.commission_rate / 100)
        }
        
        return Response(stats)
    
    @action(detail=False, methods=['get'])
    def top_agencies(self, request):
        """
        Get top agencies by various metrics.
        """
        queryset = self.get_queryset()
        
        # Top by advertisers
        top_by_advertisers = queryset.order_by('-advertisers_count')[:5]
        
        # Top by commission rate
        top_by_commission = queryset.order_by('-commission_rate')[:5]
        
        data = {
            'top_by_advertisers': AgencySerializer(
                top_by_advertisers, many=True, context={'request': request}
            ).data,
            'top_by_commission': AgencySerializer(
                top_by_commission, many=True, context={'request': request}
            ).data
        }
        
        return Response(data)


class AdvertiserViewSet(AuditMixin, CacheMixin, viewsets.ModelViewSet):
    """
    ViewSet for managing advertisers.
    
    Provides CRUD operations for advertisers with agency relationships,
    brand management, contact information, and billing details.
    """
    
    queryset = Advertiser.objects.all()
    serializer_class = AdvertiserSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = AdvertiserFilter
    search_fields = ['name', 'code', 'email', 'industry']
    ordering_fields = ['name', 'code', 'created_at', 'credit_limit']
    ordering = ['name']
    pagination_class = StandardResultsSetPagination
    
    def get_serializer_class(self):
        """Return appropriate serializer based on action."""
        if self.action == 'retrieve':
            return AdvertiserDetailSerializer
        return AdvertiserSerializer
    
    def get_queryset(self):
        """Optimize queryset with related data and annotations."""
        queryset = super().get_queryset()
        return queryset.select_related('agency', 'created_by').prefetch_related(
            'brands', 'contacts'
        ).annotate(
            brands_count=Count('brands', filter=Q(brands__is_active=True)),
            contacts_count=Count('contacts', filter=Q(contacts__is_active=True))
        )
    
    def perform_create(self, serializer):
        """Set the created_by field when creating an advertiser."""
        serializer.save(created_by=self.request.user)
    
    @action(detail=True, methods=['get'])
    def brands(self, request, pk=None):
        """
        Get all brands for this advertiser.
        """
        advertiser = self.get_object()
        brands = advertiser.brands.filter(is_active=True)
        
        # Apply pagination
        page = self.paginate_queryset(brands)
        if page is not None:
            serializer = BrandSerializer(page, many=True, context={'request': request})
            return self.get_paginated_response(serializer.data)
        
        serializer = BrandSerializer(brands, many=True, context={'request': request})
        return Response(serializer.data)
    
    @action(detail=True, methods=['get'])
    def contacts(self, request, pk=None):
        """
        Get all contacts for this advertiser.
        """
        advertiser = self.get_object()
        contacts = advertiser.contacts.filter(is_active=True)
        
        serializer = AdvertiserContactSerializer(
            contacts, many=True, context={'request': request}
        )
        return Response(serializer.data)
    
    @action(detail=True, methods=['post'])
    def add_contact(self, request, pk=None):
        """
        Add a contact to this advertiser.
        """
        advertiser = self.get_object()
        serializer = AdvertiserContactSerializer(
            data=request.data, context={'request': request}
        )
        
        if serializer.is_valid():
            serializer.save(advertiser=advertiser)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=True, methods=['get'])
    def billing(self, request, pk=None):
        """
        Get billing information for this advertiser.
        """
        advertiser = self.get_object()
        try:
            billing = advertiser.billing_info
            serializer = AdvertiserBillingSerializer(
                billing, context={'request': request}
            )
            return Response(serializer.data)
        except AdvertiserBilling.DoesNotExist:
            return Response(
                {'detail': _('Billing information not found.')},
                status=status.HTTP_404_NOT_FOUND
            )
    
    @action(detail=True, methods=['post', 'put'])
    def update_billing(self, request, pk=None):
        """
        Create or update billing information for this advertiser.
        """
        advertiser = self.get_object()
        
        try:
            billing = advertiser.billing_info
            serializer = AdvertiserBillingSerializer(
                billing, data=request.data, context={'request': request}
            )
        except AdvertiserBilling.DoesNotExist:
            serializer = AdvertiserBillingSerializer(
                data=request.data, context={'request': request}
            )
        
        if serializer.is_valid():
            serializer.save(advertiser=advertiser)
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=True, methods=['get'])
    def statistics(self, request, pk=None):
        """
        Get comprehensive statistics for this advertiser.
        """
        advertiser = self.get_object()
        
        # Get campaign statistics (if campaigns app exists)
        try:
            from apps.campaigns.models import Campaign
            campaigns = Campaign.objects.filter(advertiser=advertiser)
            active_campaigns = campaigns.filter(status='active').count()
            total_campaigns = campaigns.count()
            total_spend = campaigns.aggregate(total=Sum('budget'))['total'] or Decimal('0.00')
        except ImportError:
            active_campaigns = 0
            total_campaigns = 0
            total_spend = Decimal('0.00')
        
        stats = {
            'total_brands': advertiser.brands.filter(is_active=True).count(),
            'total_contacts': advertiser.contacts.filter(is_active=True).count(),
            'total_campaigns': total_campaigns,
            'active_campaigns': active_campaigns,
            'total_spend': total_spend,
            'credit_limit': advertiser.credit_limit,
            'advertiser_type': advertiser.advertiser_type,
            'is_agency_managed': advertiser.is_agency_managed
        }
        
        # Add billing information if available
        try:
            billing = advertiser.billing_info
            stats.update({
                'current_balance': billing.current_balance,
                'available_credit': billing.available_credit,
                'is_over_limit': billing.is_over_limit
            })
        except AdvertiserBilling.DoesNotExist:
            pass
        
        return Response(stats)
    
    @action(detail=False, methods=['get'])
    def by_type(self, request):
        """
        Get advertisers grouped by type.
        """
        queryset = self.get_queryset()
        
        data = {}
        for type_code, type_name in Advertiser.ADVERTISER_TYPES:
            advertisers = queryset.filter(advertiser_type=type_code, is_active=True)
            data[type_code] = {
                'name': type_name,
                'count': advertisers.count(),
                'advertisers': AdvertiserSerializer(
                    advertisers[:10], many=True, context={'request': request}
                ).data
            }
        
        return Response(data)


class BrandViewSet(AuditMixin, CacheMixin, viewsets.ModelViewSet):
    """
    ViewSet for managing brands.
    
    Provides CRUD operations for brands with category relationships,
    advertiser associations, and campaign statistics.
    """
    
    queryset = Brand.objects.all()
    serializer_class = BrandSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = BrandFilter
    search_fields = ['name', 'code', 'description']
    ordering_fields = ['name', 'code', 'created_at']
    ordering = ['name']
    pagination_class = StandardResultsSetPagination
    
    def get_serializer_class(self):
        """Return appropriate serializer based on action."""
        if self.action == 'retrieve':
            return BrandDetailSerializer
        return BrandSerializer
    
    def get_queryset(self):
        """Optimize queryset with related data."""
        queryset = super().get_queryset()
        return queryset.select_related('advertiser', 'category')
    
    @action(detail=True, methods=['get'])
    def campaigns(self, request, pk=None):
        """
        Get all campaigns for this brand.
        """
        brand = self.get_object()
        
        try:
            from apps.campaigns.models import Campaign
            from apps.campaigns.serializers import CampaignSerializer
            
            campaigns = Campaign.objects.filter(brand=brand)
            
            # Apply pagination
            page = self.paginate_queryset(campaigns)
            if page is not None:
                serializer = CampaignSerializer(page, many=True, context={'request': request})
                return self.get_paginated_response(serializer.data)
            
            serializer = CampaignSerializer(campaigns, many=True, context={'request': request})
            return Response(serializer.data)
        except ImportError:
            return Response(
                {'detail': _('Campaigns module not available.')},
                status=status.HTTP_404_NOT_FOUND
            )
    
    @action(detail=True, methods=['get'])
    def statistics(self, request, pk=None):
        """
        Get statistics for this brand.
        """
        brand = self.get_object()
        
        # Get campaign statistics (if campaigns app exists)
        try:
            from apps.campaigns.models import Campaign
            campaigns = Campaign.objects.filter(brand=brand)
            active_campaigns = campaigns.filter(status='active').count()
            total_campaigns = campaigns.count()
            total_budget = campaigns.aggregate(total=Sum('budget'))['total'] or Decimal('0.00')
        except ImportError:
            active_campaigns = 0
            total_campaigns = 0
            total_budget = Decimal('0.00')
        
        stats = {
            'total_campaigns': total_campaigns,
            'active_campaigns': active_campaigns,
            'total_budget': total_budget,
            'category': brand.category.name if brand.category else None,
            'advertiser': brand.advertiser.name,
            'is_agency_managed': brand.advertiser.is_agency_managed
        }
        
        return Response(stats)
    
    @action(detail=False, methods=['get'])
    def by_category(self, request):
        """
        Get brands grouped by category.
        """
        queryset = self.get_queryset().filter(is_active=True)
        
        # Get brands with categories
        categorized_brands = queryset.exclude(category__isnull=True)
        categories_data = {}
        
        for brand in categorized_brands:
            category_name = brand.category.name
            if category_name not in categories_data:
                categories_data[category_name] = {
                    'category_id': brand.category.id,
                    'brands': []
                }
            categories_data[category_name]['brands'].append(
                BrandSerializer(brand, context={'request': request}).data
            )
        
        # Get uncategorized brands
        uncategorized_brands = queryset.filter(category__isnull=True)
        
        data = {
            'categorized': categories_data,
            'uncategorized': BrandSerializer(
                uncategorized_brands, many=True, context={'request': request}
            ).data
        }
        
        return Response(data)


class AdvertiserContactViewSet(AuditMixin, viewsets.ModelViewSet):
    """
    ViewSet for managing advertiser contacts.
    
    Provides CRUD operations for advertiser contact information
    with filtering and search capabilities.
    """
    
    queryset = AdvertiserContact.objects.all()
    serializer_class = AdvertiserContactSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = AdvertiserContactFilter
    search_fields = ['first_name', 'last_name', 'email']
    ordering_fields = ['last_name', 'first_name', 'contact_type', 'created_at']
    ordering = ['advertiser', 'contact_type', 'last_name']
    pagination_class = StandardResultsSetPagination
    
    def get_queryset(self):
        """Optimize queryset with related data."""
        queryset = super().get_queryset()
        return queryset.select_related('advertiser')


class AgencyUserViewSet(AuditMixin, viewsets.ModelViewSet):
    """
    ViewSet for managing agency users.
    
    Provides CRUD operations for agency user relationships
    with role-based permissions and access control.
    """
    
    queryset = AgencyUser.objects.all()
    serializer_class = AgencyUserSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = AgencyUserFilter
    search_fields = ['user__first_name', 'user__last_name', 'user__email']
    ordering_fields = ['user__last_name', 'role', 'joined_at']
    ordering = ['agency', 'role', 'user__last_name']
    pagination_class = StandardResultsSetPagination
    
    def get_queryset(self):
        """Optimize queryset with related data."""
        queryset = super().get_queryset()
        return queryset.select_related('user', 'agency')
    
    @action(detail=False, methods=['get'])
    def my_agencies(self, request):
        """
        Get agencies associated with the current user.
        """
        user_agencies = self.get_queryset().filter(
            user=request.user, is_active=True
        )
        
        serializer = self.get_serializer(user_agencies, many=True)
        return Response(serializer.data)


class AdvertiserBillingViewSet(AuditMixin, viewsets.ModelViewSet):
    """
    ViewSet for managing advertiser billing information.
    
    Provides CRUD operations for billing details with
    financial tracking and credit management.
    """
    
    queryset = AdvertiserBilling.objects.all()
    serializer_class = AdvertiserBillingSerializer
    permission_classes = [permissions.IsAuthenticated, IsAdminOrReadOnly]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ['advertiser__name', 'billing_name', 'tax_id']
    ordering_fields = ['advertiser__name', 'credit_limit', 'current_balance']
    ordering = ['advertiser__name']
    pagination_class = StandardResultsSetPagination
    
    def get_queryset(self):
        """Optimize queryset with related data."""
        queryset = super().get_queryset()
        return queryset.select_related('advertiser')
    
    @action(detail=False, methods=['get'])
    def over_limit(self, request):
        """
        Get advertisers who are over their credit limit.
        """
        over_limit_billing = self.get_queryset().filter(
            current_balance__gt=models.F('credit_limit')
        )
        
        serializer = self.get_serializer(over_limit_billing, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def low_credit(self, request):
        """
        Get advertisers with low available credit (less than 10% of limit).
        """
        from django.db.models import F
        
        low_credit_billing = self.get_queryset().filter(
            current_balance__gt=F('credit_limit') * 0.9
        )
        
        serializer = self.get_serializer(low_credit_billing, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def financial_summary(self, request):
        """
        Get financial summary across all advertisers.
        """
        queryset = self.get_queryset().filter(is_active=True)
        
        summary = queryset.aggregate(
            total_credit_limit=Sum('credit_limit'),
            total_current_balance=Sum('current_balance'),
            average_credit_limit=Avg('credit_limit'),
            average_balance=Avg('current_balance')
        )
        
        # Calculate additional metrics
        summary['total_available_credit'] = (
            summary['total_credit_limit'] - summary['total_current_balance']
        )
        summary['credit_utilization_rate'] = (
            (summary['total_current_balance'] / summary['total_credit_limit'] * 100)
            if summary['total_credit_limit'] > 0 else 0
        )
        summary['over_limit_count'] = queryset.filter(
            current_balance__gt=F('credit_limit')
        ).count()
        
        return Response(summary)