"""
Django Admin Configuration for Campaign Management System

This module provides comprehensive admin interface configurations for all campaign-related models.
It includes custom admin classes with enhanced functionality, filters, search capabilities,
and proper field organization for efficient campaign management.

Author: Campaign Management Team
Created: 2025
Last Modified: 2025-07-28
"""

from django.contrib import admin
from django.db import models
from django.forms import TextInput, Textarea
from django.utils.html import format_html
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.contrib.admin import SimpleListFilter
from datetime import datetime, timedelta

from .models import (
    Campaigns, 
    Adspots, 
    CampaignTimeIntervals, 
    Campaignairtimelog,
    CampaignPriorityScores,
    AdSpotExclusion,
    Pending,
    DayTime,
    Placement,
    CampaignStatus
)


# ================================
# CUSTOM FILTERS
# ================================

class CampaignStatusFilter(SimpleListFilter):
    """Custom filter for campaign status with improved readability."""
    title = 'Campaign Status'
    parameter_name = 'status'

    def lookups(self, request, model_admin):
        return CampaignStatus.choices

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(status=self.value())
        return queryset


class DateRangeFilter(SimpleListFilter):
    """Filter campaigns by date ranges for better temporal organization."""
    title = 'Campaign Period'
    parameter_name = 'period'

    def lookups(self, request, model_admin):
        return (
            ('current', 'Currently Active'),
            ('upcoming', 'Upcoming (Next 30 days)'),
            ('past', 'Past Campaigns'),
            ('this_month', 'This Month'),
            ('next_month', 'Next Month'),
        )

    def queryset(self, request, queryset):
        today = datetime.now().date()
        if self.value() == 'current':
            return queryset.filter(
                start_day__lte=today.strftime('%Y-%m-%d'),
                end_day__gte=today.strftime('%Y-%m-%d')
            )
        elif self.value() == 'upcoming':
            future_date = (today + timedelta(days=30)).strftime('%Y-%m-%d')
            return queryset.filter(start_day__gt=today.strftime('%Y-%m-%d'), start_day__lte=future_date)
        elif self.value() == 'past':
            return queryset.filter(end_day__lt=today.strftime('%Y-%m-%d'))
        return queryset


# ================================
# INLINE ADMIN CLASSES
# ================================

class AdspotsInline(admin.TabularInline):
    """
    Inline admin for Adspots within Campaign admin.
    
    Allows editing of ad spots directly from the campaign page,
    providing a streamlined workflow for campaign management.
    """
    model = Adspots
    extra = 1
    fields = ['name', 'duration', 'status',]
    readonly_fields = ['id']
    
    def get_queryset(self, request):
        """Optimize queryset to reduce database hits."""
        return super().get_queryset(request).select_related('brand', 'channel')


class CampaignTimeIntervalsInline(admin.TabularInline):
    """
    Inline admin for Campaign Time Intervals.
    
    Manages time slots when advertisements can be displayed,
    crucial for scheduling and pacing control.
    """
    model = CampaignTimeIntervals
    extra = 1
    fields = ['start_time', 'end_time']
    
    class Media:
        css = {
            'all': ('admin/css/widgets.css',)
        }


class CampaignPriorityScoresInline(admin.TabularInline):
    """
    Inline admin for Campaign Priority Scores.
    
    Displays priority scoring history for campaigns,
    helping with campaign optimization and performance tracking.
    """
    model = CampaignPriorityScores
    extra = 0
    readonly_fields = ['created_at', 'updated_at']
    fields = ['priority_score', 'created_at', 'updated_at']
    
    def has_add_permission(self, request, obj=None):
        """Limit adding new priority scores to authorized users."""
        return request.user.has_perm('campaigns.add_campaignpriorityscores')


# ================================
# MAIN ADMIN CLASSES
# ================================

@admin.register(Campaigns)
class CampaignsAdmin(admin.ModelAdmin):
    """
    Comprehensive admin interface for Campaign management.
    
    Features:
    - Advanced filtering and search capabilities
    - Organized fieldsets for better UX
    - Inline editing of related models
    - Custom actions for bulk operations
    - Performance optimizations
    """
    
    # Display configuration
    list_display = [
        'name', 'get_brand_name', 'get_agency_name',
        'start_date', 'end_date', 'budget', 'cpm', 'get_delivery_status', 'exclusivity'
    ]
    
    list_filter = [
        CampaignStatusFilter, DateRangeFilter, 'exclusivity', 
        'pacing', 'is_vast', 'media_type', 'agency', 'brand'
    ]
    
    search_fields = [
        'name', 'product', 'category', 'country', 
        'brand__name', 'agency__name'
    ]
    
    # Field organization
    fieldsets = (
        ('Basic Information', {
            'fields': ('name', 'brand', 'agency', 'campaign_manager'),
            'classes': ('wide',)
        }),
        ('Campaign Details', {
            'fields': (
                'product', 'category', 'country', 
                'media_type', 'general_rotation'
            ),
            'classes': ('wide',)
        }),
        ('Scheduling & Budget', {
            'fields': (
                ('start_date', 'end_date'), 
                ('budget', 'cpm'), 
                ('volume', 'delivery'),
                'broadcasts_day'
            ),
            'classes': ('wide',)
        }),
        ('Settings & Preferences', {
            'fields': (
                ('pacing', 'status', 'exclusivity'), 
                ('is_vast', 'vast_data'),
                'program_category', 'shows_preference', 'position_preference'
            ),
            'classes': ('collapse',)
        }),
    )
    
    # Inline models
    inlines = [CampaignTimeIntervalsInline, AdspotsInline, CampaignPriorityScoresInline]
    
    # Performance optimizations
    list_select_related = ['brand', 'agency', 'campaign_manager']
    list_per_page = 25
    
    # Custom methods for list display
    def get_brand_name(self, obj):
        """Display brand name with link to brand admin."""
        if obj.brand:
            url = reverse('admin:agencies_brand_change', args=[obj.brand.id])
            return format_html('<a href="{}">{}</a>', url, obj.brand.name)
        return '-'
    get_brand_name.short_description = 'Brand'
    get_brand_name.admin_order_field = 'brand__name'
    
    def get_agency_name(self, obj):
        """Display agency name with link to agency admin."""
        if obj.agency:
            url = reverse('admin:agencies_agency_change', args=[obj.agency.id])
            return format_html('<a href="{}">{}</a>', url, obj.agency.name)
        return '-'
    get_agency_name.short_description = 'Agency'
    get_agency_name.admin_order_field = 'agency__name'
    
    def get_delivery_status(self, obj):
        """Visual indicator for delivery progress."""
        if obj.volume and obj.delivery:
            percentage = (obj.delivery / obj.volume) * 100
            color = 'green' if percentage >= 80 else 'orange' if percentage >= 50 else 'red'
            percentage_str = f"{percentage:.1f}"
            return format_html('<span style="color: {};">{}%</span>', color, percentage_str)
        return '-'
    get_delivery_status.short_description = 'Delivery %'
    


@admin.register(Adspots)
class AdspotsAdmin(admin.ModelAdmin):
    """
    Admin interface for Advertisement Spots management.
    
    Provides comprehensive tools for managing individual ad creatives,
    their file paths, durations, and associated campaigns.
    """
    
    list_display = [
        'name', 'get_campaign_name', 'get_brand_name', 
        'duration', 'status', 'get_file_info' 
    ]
    
    list_filter = [
        'status', 'campaign__status', 'brand', 'channel'
    ]
    
    search_fields = [
        'name', 'url_from_vast', 'campaign__name', 'brand__name'
    ]
    
    fieldsets = (
        ('Basic Information', {
            'fields': ('name', 'campaign', 'brand', 'channel'),
        }),
        ('File Information', {
            'fields': (
                'original_file', 'encoded_file',
                'duration', 'status',
            ),
        }),
        ('VAST Integration', {
            'fields': ('url_from_vast',),
            'classes': ('collapse',)
        }), 
    )
     
    list_select_related = ['campaign', 'brand', 'channel']
    
    def get_campaign_name(self, obj):
        """Display campaign name with status indicator."""
        if obj.campaign:
            status_color = {
                'Prebooked': 'blue',
                'Booked': 'green',
                'In_progress': 'orange',
                'Terminated': 'red',
                'Canceled': 'gray'
            }.get(obj.campaign.status, 'black')
            
            return format_html(
                '<span style="color: {};">{}</span>',
                status_color, obj.campaign.name
            )
        return '-'
    get_campaign_name.short_description = 'Campaign'
    get_campaign_name.admin_order_field = 'campaign__name'
    
    def get_brand_name(self, obj):
        """Display brand name."""
        return obj.brand.name if obj.brand else '-'
    get_brand_name.short_description = 'Brand'
    get_brand_name.admin_order_field = 'brand__name'
    
    def get_file_info(self, obj):
        """Display file information with visual indicators."""
        info = [] 
        if obj.original_file:
            info.append(f"📄 Original {obj.original_file}")
        if obj.encoded_file:
            info.append(f"🔄 Encoded {obj.encoded_file}")
        return mark_safe("<br>".join(info)) if info else '-'
    get_file_info.short_description = 'Files'


@admin.register(CampaignTimeIntervals)
class CampaignTimeIntervalsAdmin(admin.ModelAdmin):
    """
    Admin interface for Campaign Time Intervals.
    
    Manages time slots for ad spot availability, critical for
    campaign scheduling and pacing control.
    """
    
    list_display = ['get_campaign_name', 'start_time', 'end_time', 'get_duration']
    list_filter = ['campaign__status', 'start_time', 'end_time']
    search_fields = ['campaign__name']
    
    fieldsets = (
        ('Time Interval Configuration', {
            'fields': ('campaign', 'start_time', 'end_time'),
            'description': 'Define time slots when this campaign\'s ads can be displayed.'
        }),
    )
    
    list_select_related = ['campaign']
    
    def get_campaign_name(self, obj):
        """Display campaign name."""
        return obj.campaign.name if obj.campaign else '-'
    get_campaign_name.short_description = 'Campaign'
    get_campaign_name.admin_order_field = 'campaign__name'
    
    def get_duration(self, obj):
        """Calculate and display interval duration."""
        if obj.start_time and obj.end_time:
            start = datetime.combine(datetime.today(), obj.start_time)
            end = datetime.combine(datetime.today(), obj.end_time)
            if end < start:  # Next day
                end += timedelta(days=1)
            duration = end - start
            hours, remainder = divmod(duration.seconds, 3600)
            minutes, _ = divmod(remainder, 60)
            return "{}h {}m".format(hours, minutes)
        return '-'
    get_duration.short_description = 'Duration'


@admin.register(Campaignairtimelog)
class CampaignairtimelogAdmin(admin.ModelAdmin):
    """
    Admin interface for Campaign Airtime Logs.
    
    Tracks actual broadcast times and insertion status for
    campaign performance monitoring and reporting.
    """
    
    list_display = [
        'get_campaign_name', 'get_spot_name', 'get_adbreak_info',
        'airtime_start', 'airtime_end', 'get_insertion_status'
    ]
    
    list_filter = [
        'insertion_status', 'airtime_start', 'campaign__status',
        'ad_break__channel'
    ]
    
    search_fields = [
        'campaign__name', 'spot__adspot_name', 
        'ad_break__channel__name'
    ]
    
    date_hierarchy = 'airtime_start'
    list_select_related = ['campaign', 'spot', 'ad_break']
    
    fieldsets = (
        ('Broadcast Information', {
            'fields': ('campaign', 'spot', 'ad_break'),
        }),
        ('Timing', {
            'fields': ('airtime_start', 'airtime_end'),
        }),
        ('Status', {
            'fields': ('insertion_status',),
        })
    )
    
    def get_campaign_name(self, obj):
        return obj.campaign.name if obj.campaign else '-'
    get_campaign_name.short_description = 'Campaign'
    
    def get_spot_name(self, obj):
        return obj.spot.adspot_name if obj.spot else '-'
    get_spot_name.short_description = 'Ad Spot'
    
    def get_adbreak_info(self, obj):
        if obj.ad_break:
            return "{}".format(obj.ad_break.channel.name if obj.ad_break.channel else 'Unknown')
        return '-'
    get_adbreak_info.short_description = 'Channel'
    
    def get_insertion_status(self, obj):
        """Visual status indicator."""
        if obj.insertion_status is None:
            return mark_safe('<span style="color: gray;">⏳ Pending</span>')
        elif obj.insertion_status:
            return mark_safe('<span style="color: green;">✅ Success</span>')
        else:
            return mark_safe('<span style="color: red;">❌ Failed</span>')
    get_insertion_status.short_description = 'Status'


@admin.register(CampaignPriorityScores)
class CampaignPriorityScoresAdmin(admin.ModelAdmin):
    """
    Admin interface for Campaign Priority Scores.
    
    Manages priority scoring system for campaign optimization
    and automated decision making.
    """
    
    list_display = [
        'get_campaign_name', 'priority_score', 'created_at', 'updated_at'
    ]
    
    list_filter = ['created_at', 'updated_at', 'campaign__status']
    search_fields = ['campaign__name']
    readonly_fields = ['created_at', 'updated_at']
    date_hierarchy = 'created_at'
    
    fieldsets = (
        ('Priority Score Information', {
            'fields': ('campaign', 'priority_score'),
        }),
        ('Timestamps', {
            'fields': ('created_at', 'updated_at'),
            'classes': ('collapse',)
        })
    )
    
    def get_campaign_name(self, obj):
        return obj.campaign.name if obj.campaign else '-'
    get_campaign_name.short_description = 'Campaign'
    get_campaign_name.admin_order_field = 'campaign__name'


@admin.register(AdSpotExclusion)
class AdSpotExclusionAdmin(admin.ModelAdmin):
    """
    Admin interface for Ad Spot Exclusions.
    
    Manages competitive exclusions and scheduling conflicts
    between different advertisement spots.
    """
    
    list_display = ['get_spot_1', 'get_spot_2', 'get_brands', 'get_campaigns']
    search_fields = [
        'ad_spot_1__adspot_name', 'ad_spot_2__adspot_name',
        'ad_spot_1__id_brand__name', 'ad_spot_2__id_brand__name'
    ]
    
    fieldsets = (
        ('Exclusion Configuration', {
            'fields': ('ad_spot_1', 'ad_spot_2'),
            'description': 'Define which ad spots cannot be scheduled together.'
        }),
    )
    
    def get_spot_1(self, obj):
        return obj.ad_spot_1.adspot_name if obj.ad_spot_1 else '-'
    get_spot_1.short_description = 'Ad Spot 1'
    
    def get_spot_2(self, obj):
        return obj.ad_spot_2.adspot_name if obj.ad_spot_2 else '-'
    get_spot_2.short_description = 'Ad Spot 2'
    
    def get_brands(self, obj):
        brands = []
        if obj.ad_spot_1 and obj.ad_spot_1.id_brand:
            brands.append(obj.ad_spot_1.id_brand.name)
        if obj.ad_spot_2 and obj.ad_spot_2.id_brand:
            brands.append(obj.ad_spot_2.id_brand.name)
        return ' ↔ '.join(set(brands)) if brands else '-'
    get_brands.short_description = 'Brands'
    
    def get_campaigns(self, obj):
        campaigns = []
        if obj.ad_spot_1 and obj.ad_spot_1.id_campaign:
            campaigns.append(obj.ad_spot_1.id_campaign.name)
        if obj.ad_spot_2 and obj.ad_spot_2.id_campaign:
            campaigns.append(obj.ad_spot_2.id_campaign.name)
        return ' ↔ '.join(set(campaigns)) if campaigns else '-'
    get_campaigns.short_description = 'Campaigns'


@admin.register(Pending)
class PendingAdmin(admin.ModelAdmin):
    """
    Admin interface for Pending advertisements.
    
    Manages advertisements waiting for approval or processing,
    with quick action capabilities for workflow management.
    """
    
    list_display = ['creative_id', 'get_url_display', 'duration', 'id_pending']
    search_fields = ['creative_id', 'url']
    
    fieldsets = (
        ('Pending Advertisement', {
            'fields': ('creative_id', 'url', 'duration'),
        }),
    )
    
    def get_url_display(self, obj):
        """Display URL with link if valid."""
        if obj.url:
            if obj.url.startswith(('http://', 'https://')):
                return format_html('<a href="{}" target="_blank">🔗 View</a>', obj.url)
            return obj.url[:50] + '...' if len(obj.url) > 50 else obj.url
        return '-'
    get_url_display.short_description = 'URL'


@admin.register(DayTime)
class DayTimeAdmin(admin.ModelAdmin):
    """
    Admin interface for Day Time configurations.
    
    Manages time slot definitions used across the system
    for scheduling and time-based filtering.
    """
    
    list_display = ['name', 'start', 'end', 'get_duration_display']
    search_fields = ['name']
    
    fieldsets = (
        ('Time Slot Configuration', {
            'fields': ('name', 'start', 'end'),
        }),
    )
    
    def get_duration_display(self, obj):
        """Calculate and display time slot duration."""
        try:
            start_time = datetime.strptime(obj.start, '%H:%M').time()
            end_time = datetime.strptime(obj.end, '%H:%M').time()
            
            start_dt = datetime.combine(datetime.today(), start_time)
            end_dt = datetime.combine(datetime.today(), end_time)
            
            if end_dt < start_dt:  # Next day
                end_dt += timedelta(days=1)
            
            duration = end_dt - start_dt
            hours, remainder = divmod(duration.seconds, 3600)
            minutes, _ = divmod(remainder, 60)
            return "{}h {}m".format(hours, minutes)
        except (ValueError, AttributeError):
            return 'Invalid format'
    get_duration_display.short_description = 'Duration'


@admin.register(Placement)
class PlacementAdmin(admin.ModelAdmin):
    """
    Admin interface for Advertisement Placements.
    
    Manages the relationship between campaigns, time slots,
    and channels for optimal ad placement strategies.
    """
    
    list_display = [
        'get_campaign_name', 'get_channel_name', 'get_time_slot', 'id_placement'
    ]
    
    list_filter = ['id_time', 'id_channel', 'id_campaign__status']
    search_fields = [
        'id_campaign__name', 'id_channel__name', 'id_time__name'
    ]
    
    fieldsets = (
        ('Placement Configuration', {
            'fields': ('id_campaign', 'id_channel', 'id_time'),
            'description': 'Configure where and when campaign ads will be placed.'
        }),
    )
    
    list_select_related = ['id_campaign', 'id_channel', 'id_time']
    
    def get_campaign_name(self, obj):
        return obj.id_campaign.name if obj.id_campaign else '-'
    get_campaign_name.short_description = 'Campaign'
    
    def get_channel_name(self, obj):
        return obj.id_channel.name if obj.id_channel else '-'
    get_channel_name.short_description = 'Channel'
    
    def get_time_slot(self, obj):
        if obj.id_time:
            return "{} ({}-{})".format(obj.id_time.name, obj.id_time.start, obj.id_time.end)
        return '-'
    get_time_slot.short_description = 'Time Slot'


# ================================
# ADMIN SITE CUSTOMIZATION
# ================================

# Customize admin site headers and titles
admin.site.site_header = "Campaign Management System"
admin.site.site_title = "CMS Admin"
admin.site.index_title = "Campaign Management Dashboard"

# Add custom CSS for better visual presentation
class CampaignAdminSite(admin.AdminSite):
    """Custom admin site with enhanced styling and functionality."""
    
    def get_app_list(self, request):
        """
        Return a sorted list of all the installed apps that have been
        registered in this site.
        """
        app_dict = self._build_app_dict(request)
        
        # Sort models within each app
        for app in app_dict.values():
            app['models'].sort(key=lambda x: x['name'])
        
        # Define custom app order
        app_order = ['campaigns', 'agencies', 'channels', 'accounts']
        
        # Sort apps according to custom order
        app_list = []
        for app_name in app_order:
            if app_name in app_dict:
                app_list.append(app_dict[app_name])
        
        # Add remaining apps
        for app_name, app in app_dict.items():
            if app_name not in app_order:
                app_list.append(app)
        
        return app_list


# Register the custom admin site if needed
# campaign_admin_site = CampaignAdminSite(name='campaign_admin')

# Note: To use the custom admin site, you would need to register all models with it:
# campaign_admin_site.register(Campaigns, CampaignsAdmin)
# etc.