"""
Appreciation signals - Signal handlers for appreciation events

This module handles:
- Sending notifications when appreciations are sent
- Updating statistics when appreciations are created
- Checking and awarding badges
"""
from django.db.models import Q
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone

from apps.appreciation.models import (
    Appreciation,
    AppreciationBadge,
    AppreciationStats,
    AppreciationStatus,
    UserBadge,
)


@receiver(post_save, sender=Appreciation)
def handle_appreciation_sent(sender, instance, created, **kwargs):
    """
    Handle appreciation sent events.
    
    This signal triggers:
    1. Send notification to recipient
    2. Update statistics
    3. Check for badge awards
    """
    # Only process when appreciation is sent (status changes to 'sent')
    if instance.status == AppreciationStatus.SENT and not instance.notification_sent:
        # Send notification
        send_appreciation_notification(instance)
        
        # Update statistics
        update_appreciation_stats(instance)
        
        # Check for badge awards
        check_and_award_badges(instance)


def send_appreciation_notification(appreciation):
    """
    Send notification to recipient when appreciation is sent.
    
    Uses the notification system to send email/SMS/WhatsApp.
    """
    try:
        from apps.notifications.services import send_email, send_sms
        
        # Get recipient details
        recipient_email = appreciation.get_recipient_email()
        recipient_phone = appreciation.get_recipient_phone()
        
        # Get sender name
        sender_name = "Anonymous" if appreciation.is_anonymous else appreciation.sender.get_full_name()
        
        # Build message
        message_en = f"You've received an appreciation from {sender_name}!"
        message_ar = f"لقد تلقيت تكريماً من {sender_name}!"
        
        if appreciation.category:
            message_en += f"\n\nCategory: {appreciation.category.name_en}"
            message_ar += f"\n\nالفئة: {appreciation.category.name_ar}"
        
        message_en += f"\n\nMessage: {appreciation.message_en}"
        message_ar += f"\n\nالرسالة: {appreciation.message_ar}"
        
        # Send email if available
        if recipient_email:
            try:
                send_email(
                    email=recipient_email,
                    subject=f"New Appreciation Received - {appreciation.hospital.name}",
                    message=message_en,
                    related_object=appreciation,
                )
            except Exception as e:
                # Log error but don't fail
                print(f"Failed to send appreciation email: {e}")
        
        # Send SMS if available
        if recipient_phone:
            try:
                send_sms(
                    phone=recipient_phone,
                    message=message_en,
                    related_object=appreciation,
                )
            except Exception as e:
                # Log error but don't fail
                print(f"Failed to send appreciation SMS: {e}")
    
    except ImportError as e:
        # Notification service not available - skip notification
        print(f"Notification service not available: {e}")
    except Exception as e:
        # Any other error - log but don't fail
        print(f"Error sending appreciation notification: {e}")
    
    # Mark notification as sent (even if notification failed)
    appreciation.notification_sent = True
    appreciation.notification_sent_at = timezone.now()
    appreciation.save(update_fields=['notification_sent', 'notification_sent_at'])


def update_appreciation_stats(instance):
    """
    Update appreciation statistics for the recipient.
    
    Creates or updates monthly statistics.
    """
    # Get current year and month
    now = timezone.now()
    year = now.year
    month = now.month
    
    # Get or create stats record
    stats, created = AppreciationStats.objects.get_or_create(
        recipient_content_type=instance.recipient_content_type,
        recipient_object_id=instance.recipient_object_id,
        year=year,
        month=month,
        defaults={
            'hospital': instance.hospital,
            'department': instance.department,
            'received_count': 0,
            'sent_count': 0,
            'acknowledged_count': 0,
            'category_breakdown': {},
        }
    )
    
    # Update received count
    stats.received_count += 1
    
    # Update category breakdown
    if instance.category:
        category_breakdown = stats.category_breakdown or {}
        category_id_str = str(instance.category.id)
        category_breakdown[category_id_str] = category_breakdown.get(category_id_str, 0) + 1
        stats.category_breakdown = category_breakdown
    
    # Save stats
    stats.save()
    
    # Recalculate rankings
    recalculate_rankings(instance.hospital, year, month, instance.department)


def recalculate_rankings(hospital, year, month, department=None):
    """
    Recalculate rankings for a given period.
    
    Updates hospital_rank and department_rank for all recipients.
    """
    # Get all stats for the period
    if department:
        stats_queryset = AppreciationStats.objects.filter(
            hospital=hospital,
            department=department,
            year=year,
            month=month,
        )
    else:
        stats_queryset = AppreciationStats.objects.filter(
            hospital=hospital,
            year=year,
            month=month,
        )
    
    # Order by received count
    stats_queryset = stats_queryset.order_by('-received_count')
    
    # Update hospital rank
    for rank, stat in enumerate(stats_queryset, start=1):
        stat.hospital_rank = rank
        stat.save(update_fields=['hospital_rank'])
    
    # Update department rank if department is specified
    if department:
        dept_stats = stats_queryset.filter(department=department)
        for rank, stat in enumerate(dept_stats, start=1):
            stat.department_rank = rank
            stat.save(update_fields=['department_rank'])


def check_and_award_badges(instance):
    """
    Check if recipient qualifies for any badges and award them.
    
    Checks all active badge criteria and awards badges if criteria are met.
    """
    from django.contrib.contenttypes.models import ContentType
    
    # Get recipient
    recipient_content_type = instance.recipient_content_type
    recipient_object_id = instance.recipient_object_id
    
    # Get all active badges for the hospital
    badges = AppreciationBadge.objects.filter(
        Q(hospital=instance.hospital) | Q(hospital__isnull=True),
        is_active=True,
    )
    
    for badge in badges:
        # Check if badge already earned
        if UserBadge.objects.filter(
            recipient_content_type=recipient_content_type,
            recipient_object_id=recipient_object_id,
            badge=badge,
        ).exists():
            continue  # Already earned
        
        # Check badge criteria
        qualifies = check_badge_criteria(
            badge,
            recipient_content_type,
            recipient_object_id,
            instance.hospital,
        )
        
        if qualifies:
            # Calculate current count
            count = get_appreciation_count(
                recipient_content_type,
                recipient_object_id,
                badge.criteria_type
            )
            
            # Award badge
            UserBadge.objects.create(
                recipient_content_type=recipient_content_type,
                recipient_object_id=recipient_object_id,
                badge=badge,
                appreciation_count=count,
            )


def check_badge_criteria(badge, content_type, object_id, hospital):
    """
    Check if recipient meets badge criteria.
    
    Returns True if criteria is met, False otherwise.
    """
    criteria_type = badge.criteria_type
    criteria_value = badge.criteria_value
    
    if criteria_type == 'received_count':
        # Check total appreciation count
        count = Appreciation.objects.filter(
            recipient_content_type=content_type,
            recipient_object_id=object_id,
            status=AppreciationStatus.SENT,
        ).count()
        return count >= criteria_value
    
    elif criteria_type == 'received_month':
        # Check appreciation count in current month
        now = timezone.now()
        count = Appreciation.objects.filter(
            recipient_content_type=content_type,
            recipient_object_id=object_id,
            status=AppreciationStatus.SENT,
            sent_at__year=now.year,
            sent_at__month=now.month,
        ).count()
        return count >= criteria_value
    
    elif criteria_type == 'streak_weeks':
        # Check consecutive weeks with appreciation
        return check_appreciation_streak(
            content_type,
            object_id,
            criteria_value
        )
    
    elif criteria_type == 'diverse_senders':
        # Check appreciation from different senders
        sender_count = Appreciation.objects.filter(
            recipient_content_type=content_type,
            recipient_object_id=object_id,
            status=AppreciationStatus.SENT,
        ).values('sender').distinct().count()
        return sender_count >= criteria_value
    
    return False


def get_appreciation_count(content_type, object_id, criteria_type):
    """
    Get appreciation count based on criteria type.
    """
    if criteria_type == 'received_count':
        return Appreciation.objects.filter(
            recipient_content_type=content_type,
            recipient_object_id=object_id,
            status=AppreciationStatus.SENT,
        ).count()
    elif criteria_type == 'received_month':
        now = timezone.now()
        return Appreciation.objects.filter(
            recipient_content_type=content_type,
            recipient_object_id=object_id,
            status=AppreciationStatus.SENT,
            sent_at__year=now.year,
            sent_at__month=now.month,
        ).count()
    return 0


def check_appreciation_streak(content_type, object_id, required_weeks):
    """
    Check if recipient has appreciation streak for required weeks.
    
    Returns True if streak meets or exceeds required_weeks.
    """
    from datetime import timedelta
    
    now = timezone.now()
    current_week = 0
    
    # Check week by week going backwards
    for i in range(required_weeks):
        week_start = now - timedelta(weeks=i+1)
        week_end = now - timedelta(weeks=i)
        
        # Check if there's any appreciation in this week
        has_appreciation = Appreciation.objects.filter(
            recipient_content_type=content_type,
            recipient_object_id=object_id,
            status=AppreciationStatus.SENT,
            sent_at__gte=week_start,
            sent_at__lt=week_end,
        ).exists()
        
        if has_appreciation:
            current_week += 1
        else:
            break
    
    return current_week >= required_weeks
