import uuid
import time
from datetime import datetime
from django.http import HttpResponse, JsonResponse
from django.views.generic import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.template import Template, Context
from django.conf import settings
from django.urls import reverse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from apps.vast.models import VastRequest, VastResponse, VastTracking, VastTemplate, AdDecisionEngine
from apps.campaigns.models import Campaign, AdSpot
from apps.channels.models import Channel
from apps.playlists.models import Playlist


@method_decorator(csrf_exempt, name='dispatch')
class VastAdServerView(View):
    """Main VAST ad server endpoint"""
    
    def get(self, request):
        start_time = time.time()
        
        # Generate unique request ID
        request_id = str(uuid.uuid4())
        
        # Extract request parameters
        channel_id = request.GET.get('channel_id')
        playlist_id = request.GET.get('playlist_id')
        width = request.GET.get('w', 1920)
        height = request.GET.get('h', 1080)
        duration = request.GET.get('duration', 30)
        
        # Get client information
        user_agent = request.META.get('HTTP_USER_AGENT', '')
        ip_address = self._get_client_ip(request)
        referrer = request.META.get('HTTP_REFERER', '')
        
        try:
            # Create VAST request record
            vast_request = VastRequest.objects.create(
                request_id=request_id,
                channel_id=channel_id if channel_id else None,
                playlist_id=playlist_id if playlist_id else None,
                user_agent=user_agent,
                ip_address=ip_address,
                referrer=referrer,
                width=int(width),
                height=int(height),
                duration=int(duration)
            )
            
            # Select ad using decision engine
            selected_adspot = self._select_ad(vast_request)
            
            if not selected_adspot:
                # No ad available - return empty VAST
                vast_request.response_status = 'no_ad'
                vast_request.save()
                return self._generate_empty_vast()
            
            # Generate VAST XML response
            vast_xml = self._generate_vast_xml(vast_request, selected_adspot)
            
            # Create response record
            VastResponse.objects.create(
                request=vast_request,
                vast_xml=vast_xml,
                adspot=selected_adspot,
                campaign=selected_adspot.campaign,
                impression_url=self._build_tracking_url(request, vast_request.request_id, 'impression'),
                click_url=self._build_tracking_url(request, vast_request.request_id, 'click'),
                start_url=self._build_tracking_url(request, vast_request.request_id, 'start'),
                firstquartile_url=self._build_tracking_url(request, vast_request.request_id, 'firstQuartile'),
                midpoint_url=self._build_tracking_url(request, vast_request.request_id, 'midpoint'),
                thirdquartile_url=self._build_tracking_url(request, vast_request.request_id, 'thirdQuartile'),
                complete_url=self._build_tracking_url(request, vast_request.request_id, 'complete'),
            )
            
            # Update request with response details
            vast_request.selected_adspot = selected_adspot
            vast_request.response_status = 'success'
            vast_request.response_time_ms = int((time.time() - start_time) * 1000)
            vast_request.save()
            
            return HttpResponse(vast_xml, content_type='application/xml')
            
        except Exception as e:
            # Log error and return empty VAST
            if 'vast_request' in locals():
                vast_request.response_status = 'error'
                vast_request.save()
            
            return self._generate_empty_vast()
    
    def _get_client_ip(self, request):
        """Extract client IP address"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip
    
    def _select_ad(self, vast_request):
        """Select appropriate ad based on decision engines"""
        # Get active decision engines
        decision_engines = AdDecisionEngine.objects.filter(is_active=True).order_by('-priority')
        
        for engine in decision_engines:
            adspot = self._apply_decision_engine(engine, vast_request)
            if adspot:
                return adspot
        
        # Fallback to simple selection
        return self._simple_ad_selection(vast_request)
    
    def _apply_decision_engine(self, engine, vast_request):
        """Apply specific decision engine logic"""
        if engine.decision_type == 'round_robin':
            return self._round_robin_selection(engine, vast_request)
        elif engine.decision_type == 'weighted':
            return self._weighted_selection(engine, vast_request)
        elif engine.decision_type == 'priority':
            return self._priority_selection(engine, vast_request)
        # Add more decision types as needed
        return None
    
    def _simple_ad_selection(self, vast_request):
        """Simple ad selection fallback"""
        # Get active campaigns
        active_campaigns = Campaign.objects.filter(status='active')
        
        if vast_request.channel:
            # Filter by channel if specified
            active_campaigns = active_campaigns.filter(
                adspots__channel=vast_request.channel
            ).distinct()
        
        # Get available adspots
        adspots = AdSpot.objects.filter(
            campaign__in=active_campaigns,
            status='active'
        )
        
        if adspots.exists():
            return adspots.first()
        
        return None
    
    def _round_robin_selection(self, engine, vast_request):
        """Round robin ad selection"""
        # Implementation for round robin logic
        campaigns = engine.campaigns.filter(status='active')
        if campaigns.exists():
            adspots = AdSpot.objects.filter(
                campaign__in=campaigns,
                status='active'
            )
            if adspots.exists():
                return adspots.first()
        return None
    
    def _weighted_selection(self, engine, vast_request):
        """Weighted random ad selection"""
        # Implementation for weighted selection
        # This would use the configuration to determine weights
        return self._simple_ad_selection(vast_request)
    
    def _priority_selection(self, engine, vast_request):
        """Priority-based ad selection"""
        # Implementation for priority-based selection
        campaigns = engine.campaigns.filter(status='active').order_by('-priority')
        if campaigns.exists():
            adspots = AdSpot.objects.filter(
                campaign__in=campaigns,
                status='active'
            )
            if adspots.exists():
                return adspots.first()
        return None
    
    def _generate_vast_xml(self, vast_request, adspot):
        """Generate VAST XML for the selected ad"""
        # Get VAST template
        template = VastTemplate.objects.filter(
            ad_type='linear',
            is_active=True
        ).first()
        
        if not template:
            return self._generate_default_vast_xml(vast_request, adspot)
        
        # Prepare template context
        context = {
            'request_id': vast_request.request_id,
            'adspot': adspot,
            'campaign': adspot.campaign,
            'media_url': adspot.encoded_filepath or adspot.original_filepath,
            'click_through_url': adspot.url_from_vast or '#',
            'duration': adspot.duration,
            'width': vast_request.width,
            'height': vast_request.height,
            'impression_url': self._build_tracking_url(None, vast_request.request_id, 'impression'),
            'start_url': self._build_tracking_url(None, vast_request.request_id, 'start'),
            'firstquartile_url': self._build_tracking_url(None, vast_request.request_id, 'firstQuartile'),
            'midpoint_url': self._build_tracking_url(None, vast_request.request_id, 'midpoint'),
            'thirdquartile_url': self._build_tracking_url(None, vast_request.request_id, 'thirdQuartile'),
            'complete_url': self._build_tracking_url(None, vast_request.request_id, 'complete'),
            'click_url': self._build_tracking_url(None, vast_request.request_id, 'click'),
        }
        
        # Render template
        django_template = Template(template.template_xml)
        return django_template.render(Context(context))
    
    def _generate_default_vast_xml(self, vast_request, adspot):
        """Generate default VAST XML when no template is available"""
        vast_xml = f'''
<?xml version="1.0" encoding="UTF-8"?>
<VAST version="3.0">
    <Ad id="{adspot.id}">
        <InLine>
            <AdSystem>Adtlas</AdSystem>
            <AdTitle><![CDATA[{adspot.adspot_name}]]></AdTitle>
            <Impression><![CDATA[{self._build_tracking_url(None, vast_request.request_id, 'impression')}]]></Impression>
            <Creatives>
                <Creative>
                    <Linear>
                        <Duration>{adspot.duration}</Duration>
                        <TrackingEvents>
                            <Tracking event="start"><![CDATA[{self._build_tracking_url(None, vast_request.request_id, 'start')}]]></Tracking>
                            <Tracking event="firstQuartile"><![CDATA[{self._build_tracking_url(None, vast_request.request_id, 'firstQuartile')}]]></Tracking>
                            <Tracking event="midpoint"><![CDATA[{self._build_tracking_url(None, vast_request.request_id, 'midpoint')}]]></Tracking>
                            <Tracking event="thirdQuartile"><![CDATA[{self._build_tracking_url(None, vast_request.request_id, 'thirdQuartile')}]]></Tracking>
                            <Tracking event="complete"><![CDATA[{self._build_tracking_url(None, vast_request.request_id, 'complete')}]]></Tracking>
                        </TrackingEvents>
                        <VideoClicks>
                            <ClickThrough><![CDATA[{adspot.url_from_vast or '#'}]]></ClickThrough>
                            <ClickTracking><![CDATA[{self._build_tracking_url(None, vast_request.request_id, 'click')}]]></ClickTracking>
                        </VideoClicks>
                        <MediaFiles>
                            <MediaFile delivery="progressive" type="video/mp4" width="{vast_request.width}" height="{vast_request.height}">
                                <![CDATA[{adspot.encoded_filepath or adspot.original_filepath}]]>
                            </MediaFile>
                        </MediaFiles>
                    </Linear>
                </Creative>
            </Creatives>
        </InLine>
    </Ad>
</VAST>
        '''
        return vast_xml.strip()
    
    def _generate_empty_vast(self):
        """Generate empty VAST response when no ad is available"""
        empty_vast = '''
<?xml version="1.0" encoding="UTF-8"?>
<VAST version="3.0">
</VAST>
        '''
        return HttpResponse(empty_vast.strip(), content_type='application/xml')
    
    def _build_tracking_url(self, request, request_id, event_type):
        """Build tracking URL for VAST events"""
        base_url = getattr(settings, 'VAST_TRACKING_BASE_URL', 'http://localhost:8000')
        return f"{base_url}/vast/track/{request_id}/{event_type}/"


@method_decorator(csrf_exempt, name='dispatch')
class VastTrackingView(View):
    """Handle VAST tracking events"""
    
    def get(self, request, request_id, event_type):
        return self._handle_tracking(request, request_id, event_type)
    
    def post(self, request, request_id, event_type):
        return self._handle_tracking(request, request_id, event_type)
    
    def _handle_tracking(self, request, request_id, event_type):
        try:
            # Find the VAST request
            vast_request = VastRequest.objects.get(request_id=request_id)
            
            # Extract additional parameters
            video_position = request.GET.get('position')
            error_code = request.GET.get('errorcode')
            error_message = request.GET.get('errormessage')
            
            # Create tracking event
            VastTracking.objects.create(
                request=vast_request,
                event_type=event_type,
                user_agent=request.META.get('HTTP_USER_AGENT', ''),
                ip_address=self._get_client_ip(request),
                video_position=int(video_position) if video_position else None,
                error_code=error_code,
                error_message=error_message,
                metadata={
                    'query_params': dict(request.GET),
                    'timestamp': datetime.now().isoformat(),
                }
            )
            
            # Return 1x1 pixel image for tracking
            response = HttpResponse(
                b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x04\x01\x00\x3B',
                content_type='image/gif'
            )
            response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
            response['Pragma'] = 'no-cache'
            response['Expires'] = '0'
            return response
            
        except VastRequest.DoesNotExist:
            return HttpResponse(status=404)
        except Exception as e:
            return HttpResponse(status=500)
    
    def _get_client_ip(self, request):
        """Extract client IP address"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip


class VastAnalyticsAPIView(APIView):
    """API for VAST analytics and reporting"""
    
    def get(self, request):
        # Get query parameters
        start_date = request.GET.get('start_date')
        end_date = request.GET.get('end_date')
        campaign_id = request.GET.get('campaign_id')
        channel_id = request.GET.get('channel_id')
        
        # Build base queryset
        queryset = VastRequest.objects.all()
        
        if start_date:
            queryset = queryset.filter(created_at__date__gte=start_date)
        if end_date:
            queryset = queryset.filter(created_at__date__lte=end_date)
        if campaign_id:
            queryset = queryset.filter(selected_adspot__campaign_id=campaign_id)
        if channel_id:
            queryset = queryset.filter(channel_id=channel_id)
        
        # Calculate metrics
        total_requests = queryset.count()
        successful_requests = queryset.filter(response_status='success').count()
        
        # Get tracking events
        tracking_queryset = VastTracking.objects.filter(request__in=queryset)
        
        impressions = tracking_queryset.filter(event_type='impression').count()
        clicks = tracking_queryset.filter(event_type='click').count()
        completions = tracking_queryset.filter(event_type='complete').count()
        
        # Calculate rates
        fill_rate = (successful_requests / total_requests * 100) if total_requests > 0 else 0
        ctr = (clicks / impressions * 100) if impressions > 0 else 0
        completion_rate = (completions / impressions * 100) if impressions > 0 else 0
        
        return Response({
            'total_requests': total_requests,
            'successful_requests': successful_requests,
            'impressions': impressions,
            'clicks': clicks,
            'completions': completions,
            'fill_rate': round(fill_rate, 2),
            'ctr': round(ctr, 2),
            'completion_rate': round(completion_rate, 2),
        })