# -*- coding: utf-8 -*-
"""
Analytics Models
================

Django models for the Adtlas Analytics module.
These models handle storage and management of analytics data, performance metrics,
and reporting information for the Dynamic Ad Insertion (DAI) system.

Models Overview:
- SfrAnalytics: SFR provider analytics data
- BouyguesAnalytics: Bouygues provider analytics data
- Impression: Individual ad impression tracking
- PerformanceMetric: Aggregated performance data
- VastResponse: VAST response tracking
- AnalyticsReport: Generated report metadata
- AdbreakPredict: Ad break prediction data
- RealTimeAdbreak: Real-time ad break information

Key Features:
- Multi-provider analytics support
- Real-time data collection
- Historical data preservation
- Performance optimization
- Data validation and integrity
- Automated cleanup policies

Relationships:
- Links to campaigns, channels, and ad spots
- User activity tracking
- Time-based data organization
- Geographic and demographic segmentation

Author: Adtlas Development Team
Version: 1.0.0
Last Updated: 2024
"""

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from django.contrib.auth import get_user_model
import uuid
from decimal import Decimal

# Get the user model
User = get_user_model()


class BaseAnalyticsModel(models.Model):
    """
    Abstract base model for all analytics models.
    
    Provides common fields and functionality for analytics data:
    - UUID primary key for better performance and security
    - Created and updated timestamps
    - Data validation helpers
    - Common query methods
    
    Attributes:
        id (UUIDField): Primary key using UUID4
        created_at (DateTimeField): Record creation timestamp
        updated_at (DateTimeField): Last update timestamp
        is_active (BooleanField): Soft delete flag
    """
    
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        help_text=_("Unique identifier for this analytics record")
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
        help_text=_("Timestamp when this record was created")
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        db_index=True,
        help_text=_("Timestamp when this record was last updated")
    )
    
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text=_("Whether this record is active (soft delete)")
    )
    
    class Meta:
        abstract = True
        ordering = ['-created_at']
    
    def soft_delete(self):
        """Mark record as inactive instead of deleting."""
        self.is_active = False
        self.save(update_fields=['is_active', 'updated_at'])
    
    def restore(self):
        """Restore a soft-deleted record."""
        self.is_active = True
        self.save(update_fields=['is_active', 'updated_at'])


class SfrAnalytics(BaseAnalyticsModel):
    """
    SFR Analytics data model.
    
    Stores analytics data from SFR provider including audience metrics,
    viewing patterns, and performance indicators.
    
    Attributes:
        channel: Related channel for this analytics data
        campaign: Related campaign (optional)
        date: Date of the analytics data
        hour: Hour of the day (0-23)
        audience_count: Number of viewers
        market_share: Market share percentage
        rating: Audience rating
        reach: Reach percentage
        frequency: Average frequency
        duration: Average viewing duration in seconds
        demographics: JSON field for demographic data
        geographic_data: JSON field for geographic distribution
    """
    
    # Foreign key relationships
    channel = models.ForeignKey(
        'channels.Channel',
        on_delete=models.CASCADE,
        related_name='sfr_analytics',
        help_text=_("Channel associated with this analytics data")
    )
    
    campaign = models.ForeignKey(
        'campaigns.Campaign',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='sfr_analytics',
        help_text=_("Campaign associated with this analytics data")
    )
    
    # Time-based fields
    date = models.DateField(
        db_index=True,
        help_text=_("Date of the analytics data")
    )
    
    hour = models.PositiveSmallIntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(23)],
        help_text=_("Hour of the day (0-23)")
    )
    
    # Audience metrics
    audience_count = models.PositiveIntegerField(
        default=0,
        help_text=_("Number of viewers")
    )
    
    market_share = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00')), MaxValueValidator(Decimal('100.00'))],
        help_text=_("Market share percentage (0-100)")
    )
    
    rating = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("Audience rating")
    )
    
    reach = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00')), MaxValueValidator(Decimal('100.00'))],
        help_text=_("Reach percentage (0-100)")
    )
    
    frequency = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("Average frequency")
    )
    
    duration = models.PositiveIntegerField(
        default=0,
        help_text=_("Average viewing duration in seconds")
    )
    
    # JSON fields for complex data
    demographics = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Demographic breakdown data")
    )
    
    geographic_data = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Geographic distribution data")
    )
    
    class Meta:
        db_table = 'analytics_sfr'
        verbose_name = _("SFR Analytics")
        verbose_name_plural = _("SFR Analytics")
        unique_together = ['channel', 'date', 'hour']
        indexes = [
            models.Index(fields=['date', 'hour']),
            models.Index(fields=['channel', 'date']),
            models.Index(fields=['campaign', 'date']),
        ]
    
    def __str__(self):
        return f"SFR Analytics - {self.channel.name} - {self.date} {self.hour}:00"
    
    @property
    def datetime(self):
        """Get datetime object for this analytics record."""
        return timezone.datetime.combine(self.date, timezone.time(hour=self.hour))
    
    def get_performance_score(self):
        """Calculate overall performance score based on metrics."""
        # Weighted average of key metrics
        score = (
            float(self.rating) * 0.3 +
            float(self.reach) * 0.25 +
            float(self.market_share) * 0.25 +
            min(float(self.frequency), 10) * 0.2  # Cap frequency at 10
        )
        return round(score, 2)


class BouyguesAnalytics(BaseAnalyticsModel):
    """
    Bouygues Analytics data model.
    
    Stores analytics data from Bouygues provider with similar structure
    to SFR but potentially different metrics and data points.
    
    Attributes:
        channel: Related channel for this analytics data
        campaign: Related campaign (optional)
        date: Date of the analytics data
        hour: Hour of the day (0-23)
        viewers: Number of viewers
        share: Market share percentage
        rating_value: Audience rating value
        reach_percentage: Reach percentage
        avg_frequency: Average frequency
        watch_time: Average watch time in minutes
        device_data: JSON field for device breakdown
        age_groups: JSON field for age group data
    """
    
    # Foreign key relationships
    channel = models.ForeignKey(
        'channels.Channel',
        on_delete=models.CASCADE,
        related_name='bouygues_analytics',
        help_text=_("Channel associated with this analytics data")
    )
    
    campaign = models.ForeignKey(
        'campaigns.Campaign',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='bouygues_analytics',
        help_text=_("Campaign associated with this analytics data")
    )
    
    # Time-based fields
    date = models.DateField(
        db_index=True,
        help_text=_("Date of the analytics data")
    )
    
    hour = models.PositiveSmallIntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(23)],
        help_text=_("Hour of the day (0-23)")
    )
    
    # Audience metrics
    viewers = models.PositiveIntegerField(
        default=0,
        help_text=_("Number of viewers")
    )
    
    share = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00')), MaxValueValidator(Decimal('100.00'))],
        help_text=_("Market share percentage (0-100)")
    )
    
    rating_value = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("Audience rating value")
    )
    
    reach_percentage = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00')), MaxValueValidator(Decimal('100.00'))],
        help_text=_("Reach percentage (0-100)")
    )
    
    avg_frequency = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("Average frequency")
    )
    
    watch_time = models.PositiveIntegerField(
        default=0,
        help_text=_("Average watch time in minutes")
    )
    
    # JSON fields for complex data
    device_data = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Device breakdown data")
    )
    
    age_groups = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Age group distribution data")
    )
    
    class Meta:
        db_table = 'analytics_bouygues'
        verbose_name = _("Bouygues Analytics")
        verbose_name_plural = _("Bouygues Analytics")
        unique_together = ['channel', 'date', 'hour']
        indexes = [
            models.Index(fields=['date', 'hour']),
            models.Index(fields=['channel', 'date']),
            models.Index(fields=['campaign', 'date']),
        ]
    
    def __str__(self):
        return f"Bouygues Analytics - {self.channel.name} - {self.date} {self.hour}:00"


class Impression(BaseAnalyticsModel):
    """
    Individual ad impression tracking model.
    
    Records each individual ad impression with detailed tracking information
    for performance analysis and billing purposes.
    
    Attributes:
        campaign: Related campaign
        adspot: Related ad spot
        channel: Channel where impression occurred
        user_agent: User agent string
        ip_address: Client IP address (anonymized)
        timestamp: Exact timestamp of impression
        duration: How long the ad was viewed
        completion_rate: Percentage of ad completed
        click_through: Whether user clicked through
        vast_response: Related VAST response
        geographic_info: Geographic information
        device_info: Device information
    """
    
    # Foreign key relationships
    campaign = models.ForeignKey(
        'campaigns.Campaign',
        on_delete=models.CASCADE,
        related_name='impressions',
        help_text=_("Campaign associated with this impression")
    )
    
    adspot = models.ForeignKey(
        'adspots.AdSpot',
        on_delete=models.CASCADE,
        related_name='impressions',
        help_text=_("Ad spot associated with this impression")
    )
    
    channel = models.ForeignKey(
        'channels.Channel',
        on_delete=models.CASCADE,
        related_name='impressions',
        help_text=_("Channel where impression occurred")
    )
    
    # Tracking information
    user_agent = models.TextField(
        blank=True,
        help_text=_("User agent string (anonymized)")
    )
    
    ip_address = models.GenericIPAddressField(
        null=True,
        blank=True,
        help_text=_("Client IP address (anonymized for privacy)")
    )
    
    timestamp = models.DateTimeField(
        default=timezone.now,
        db_index=True,
        help_text=_("Exact timestamp of impression")
    )
    
    # Performance metrics
    duration = models.PositiveIntegerField(
        default=0,
        help_text=_("How long the ad was viewed in seconds")
    )
    
    completion_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00')), MaxValueValidator(Decimal('100.00'))],
        help_text=_("Percentage of ad completed (0-100)")
    )
    
    click_through = models.BooleanField(
        default=False,
        help_text=_("Whether user clicked through the ad")
    )
    
    # Optional VAST response reference
    vast_response = models.ForeignKey(
        'VastResponse',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='impressions',
        help_text=_("Related VAST response")
    )
    
    # Additional data
    geographic_info = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Geographic information (anonymized)")
    )
    
    device_info = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Device and browser information")
    )
    
    class Meta:
        db_table = 'analytics_impressions'
        verbose_name = _("Impression")
        verbose_name_plural = _("Impressions")
        indexes = [
            models.Index(fields=['timestamp']),
            models.Index(fields=['campaign', 'timestamp']),
            models.Index(fields=['channel', 'timestamp']),
            models.Index(fields=['adspot', 'timestamp']),
        ]
    
    def __str__(self):
        return f"Impression - {self.campaign.name} - {self.timestamp}"
    
    @property
    def is_completed(self):
        """Check if impression was completed (>75% viewed)."""
        return self.completion_rate >= Decimal('75.00')
    
    @property
    def is_viewable(self):
        """Check if impression meets viewability standards (>50% for 2+ seconds)."""
        return self.completion_rate >= Decimal('50.00') and self.duration >= 2


class VastResponse(BaseAnalyticsModel):
    """
    VAST response tracking model.
    
    Stores information about VAST (Video Ad Serving Template) responses
    for tracking ad delivery and performance.
    
    Attributes:
        campaign: Related campaign
        adspot: Related ad spot
        vast_url: VAST XML URL
        response_time: Response time in milliseconds
        status_code: HTTP status code
        error_message: Error message if any
        xml_content: VAST XML content (truncated)
        tracking_urls: JSON field with tracking URLs
        creative_info: Information about the creative
        served_at: When the VAST was served
    """
    
    # Foreign key relationships
    campaign = models.ForeignKey(
        'campaigns.Campaign',
        on_delete=models.CASCADE,
        related_name='vast_responses',
        help_text=_("Campaign associated with this VAST response")
    )
    
    adspot = models.ForeignKey(
        'adspots.AdSpot',
        on_delete=models.CASCADE,
        related_name='vast_responses',
        help_text=_("Ad spot associated with this VAST response")
    )
    
    # VAST information
    vast_url = models.URLField(
        help_text=_("VAST XML URL")
    )
    
    response_time = models.PositiveIntegerField(
        default=0,
        help_text=_("Response time in milliseconds")
    )
    
    status_code = models.PositiveSmallIntegerField(
        default=200,
        help_text=_("HTTP status code")
    )
    
    error_message = models.TextField(
        blank=True,
        help_text=_("Error message if any")
    )
    
    xml_content = models.TextField(
        blank=True,
        help_text=_("VAST XML content (truncated for storage)")
    )
    
    # Tracking data
    tracking_urls = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Tracking URLs from VAST response")
    )
    
    creative_info = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Information about the creative")
    )
    
    served_at = models.DateTimeField(
        default=timezone.now,
        db_index=True,
        help_text=_("When the VAST was served")
    )
    
    class Meta:
        db_table = 'analytics_vast_responses'
        verbose_name = _("VAST Response")
        verbose_name_plural = _("VAST Responses")
        indexes = [
            models.Index(fields=['served_at']),
            models.Index(fields=['campaign', 'served_at']),
            models.Index(fields=['status_code']),
        ]
    
    def __str__(self):
        return f"VAST Response - {self.campaign.name} - {self.served_at}"
    
    @property
    def is_successful(self):
        """Check if VAST response was successful."""
        return 200 <= self.status_code < 300
    
    @property
    def is_fast_response(self):
        """Check if response time was acceptable (<500ms)."""
        return self.response_time < 500


class PerformanceMetric(BaseAnalyticsModel):
    """
    Aggregated performance metrics model.
    
    Stores pre-calculated performance metrics for faster reporting
    and dashboard display.
    
    Attributes:
        campaign: Related campaign
        channel: Related channel (optional)
        date: Date of metrics
        metric_type: Type of metric (daily, weekly, monthly)
        impressions_count: Total impressions
        clicks_count: Total clicks
        completion_rate: Average completion rate
        ctr: Click-through rate
        cpm: Cost per mille
        revenue: Total revenue
        cost: Total cost
        roi: Return on investment
    """
    
    METRIC_TYPES = [
        ('daily', _('Daily')),
        ('weekly', _('Weekly')),
        ('monthly', _('Monthly')),
        ('quarterly', _('Quarterly')),
        ('yearly', _('Yearly')),
    ]
    
    # Foreign key relationships
    campaign = models.ForeignKey(
        'campaigns.Campaign',
        on_delete=models.CASCADE,
        related_name='performance_metrics',
        help_text=_("Campaign associated with these metrics")
    )
    
    channel = models.ForeignKey(
        'channels.Channel',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='performance_metrics',
        help_text=_("Channel associated with these metrics (optional)")
    )
    
    # Time and type
    date = models.DateField(
        db_index=True,
        help_text=_("Date of metrics")
    )
    
    metric_type = models.CharField(
        max_length=20,
        choices=METRIC_TYPES,
        default='daily',
        help_text=_("Type of metric aggregation")
    )
    
    # Performance metrics
    impressions_count = models.PositiveIntegerField(
        default=0,
        help_text=_("Total impressions")
    )
    
    clicks_count = models.PositiveIntegerField(
        default=0,
        help_text=_("Total clicks")
    )
    
    completion_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00')), MaxValueValidator(Decimal('100.00'))],
        help_text=_("Average completion rate (0-100)")
    )
    
    ctr = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("Click-through rate percentage")
    )
    
    cpm = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("Cost per mille (CPM)")
    )
    
    revenue = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("Total revenue")
    )
    
    cost = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        help_text=_("Total cost")
    )
    
    roi = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        default=Decimal('0.00'),
        help_text=_("Return on investment percentage")
    )
    
    class Meta:
        db_table = 'analytics_performance_metrics'
        verbose_name = _("Performance Metric")
        verbose_name_plural = _("Performance Metrics")
        unique_together = ['campaign', 'channel', 'date', 'metric_type']
        indexes = [
            models.Index(fields=['date', 'metric_type']),
            models.Index(fields=['campaign', 'date']),
        ]
    
    def __str__(self):
        channel_name = self.channel.name if self.channel else "All Channels"
        return f"Performance - {self.campaign.name} - {channel_name} - {self.date}"
    
    def calculate_ctr(self):
        """Calculate and update CTR based on impressions and clicks."""
        if self.impressions_count > 0:
            self.ctr = Decimal(str((self.clicks_count / self.impressions_count) * 100))
        else:
            self.ctr = Decimal('0.00')
    
    def calculate_roi(self):
        """Calculate and update ROI based on revenue and cost."""
        if self.cost > 0:
            self.roi = Decimal(str(((self.revenue - self.cost) / self.cost) * 100))
        else:
            self.roi = Decimal('0.00')


class AnalyticsReport(BaseAnalyticsModel):
    """
    Generated analytics report metadata model.
    
    Stores information about generated reports for tracking and caching purposes.
    
    Attributes:
        name: Report name
        report_type: Type of report
        parameters: Report parameters (JSON)
        file_path: Path to generated report file
        file_size: Size of report file
        generated_by: User who generated the report
        generated_at: When report was generated
        expires_at: When report expires
        download_count: Number of times downloaded
        is_public: Whether report is publicly accessible
    """
    
    REPORT_TYPES = [
        ('campaign_performance', _('Campaign Performance')),
        ('channel_analytics', _('Channel Analytics')),
        ('impression_summary', _('Impression Summary')),
        ('vast_performance', _('VAST Performance')),
        ('revenue_report', _('Revenue Report')),
        ('custom', _('Custom Report')),
    ]
    
    # Report metadata
    name = models.CharField(
        max_length=255,
        help_text=_("Report name")
    )
    
    report_type = models.CharField(
        max_length=50,
        choices=REPORT_TYPES,
        help_text=_("Type of report")
    )
    
    parameters = models.JSONField(
        default=dict,
        help_text=_("Report parameters and filters")
    )
    
    # File information
    file_path = models.CharField(
        max_length=500,
        blank=True,
        help_text=_("Path to generated report file")
    )
    
    file_size = models.PositiveIntegerField(
        default=0,
        help_text=_("Size of report file in bytes")
    )
    
    # Generation information
    generated_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        related_name='generated_reports',
        help_text=_("User who generated the report")
    )
    
    generated_at = models.DateTimeField(
        default=timezone.now,
        db_index=True,
        help_text=_("When report was generated")
    )
    
    expires_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text=_("When report expires")
    )
    
    # Usage tracking
    download_count = models.PositiveIntegerField(
        default=0,
        help_text=_("Number of times downloaded")
    )
    
    is_public = models.BooleanField(
        default=False,
        help_text=_("Whether report is publicly accessible")
    )
    
    class Meta:
        db_table = 'analytics_reports'
        verbose_name = _("Analytics Report")
        verbose_name_plural = _("Analytics Reports")
        indexes = [
            models.Index(fields=['generated_at']),
            models.Index(fields=['report_type']),
            models.Index(fields=['generated_by']),
        ]
    
    def __str__(self):
        return f"Report - {self.name} - {self.generated_at}"
    
    @property
    def is_expired(self):
        """Check if report has expired."""
        if self.expires_at:
            return timezone.now() > self.expires_at
        return False
    
    def increment_download_count(self):
        """Increment download counter."""
        self.download_count += 1
        self.save(update_fields=['download_count'])