"""
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
"""
import logging

from django.db.models import Q
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone

logger = logging.getLogger(__name__)

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.
    Also sends email to department heads.
    """
    notification_success = False
    try:
        from apps.notifications.services import send_email, send_sms, get_email_header_html

        recipient_email = appreciation.get_recipient_email()
        recipient_phone = appreciation.get_recipient_phone()

        sender_name = "Anonymous" if appreciation.is_anonymous else (
            appreciation.sender.get_full_name() if appreciation.sender
            else (appreciation.metadata or {}).get("submitted_by_name", "Anonymous")
        )

        html_message = f"""
        <div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
            {get_email_header_html()}
            <div style="padding: 30px;">
                <h2 style="color: #005696; margin-top: 0;">&#127775; Staff Appreciation</h2>
                <div style="background: #eef6fb; border-left: 4px solid #005696; padding: 20px; margin: 20px 0;">
                    <strong>From:</strong> {sender_name}<br>
                    <strong>Hospital:</strong> {appreciation.hospital.name}<br>
                    {f'<strong>Category:</strong> {appreciation.category.name_en}<br>' if appreciation.category else ''}
                    <br>
                    <strong>Message:</strong><br>
                    {appreciation.message_en}
                </div>
                {f'<div style="background: #fffbeb; border-left: 4px solid #f59e0b; padding: 20px; margin: 20px 0;"><strong>Personal Note from {sender_name}:</strong><br>{appreciation.custom_message}</div>' if appreciation.custom_message else ''}
                <p>Congratulations on this recognition! Your dedication is truly appreciated.</p>
            </div>
        </div>
        """

        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}"

        if appreciation.custom_message:
            message_en += f"\n\nPersonal note: {appreciation.custom_message}"
            message_ar += f"\n\nملاحظة شخصية: {appreciation.custom_message}"

        if recipient_email:
            try:
                send_email(
                    email=recipient_email,
                    subject=f"New Appreciation Received - {appreciation.hospital.name}",
                    message=message_en,
                    html_message=html_message,
                    related_object=appreciation,
                )
                notification_success = True
            except Exception as e:
                logger.error(f"Failed to send appreciation email: {e}")

        if recipient_phone:
            try:
                send_sms(
                    phone=recipient_phone,
                    message=message_en,
                    related_object=appreciation,
                )
                notification_success = True
            except Exception as e:
                logger.error(f"Failed to send appreciation SMS: {e}")

        _send_department_head_notification(appreciation, sender_name)
        _send_cc_notifications(appreciation, sender_name, html_message, message_en)
        notification_success = True

    except ImportError as e:
        logger.warning(f"Notification service not available: {e}")
    except Exception as e:
        logger.error(f"Error sending appreciation notification: {e}")

    if notification_success:
        appreciation.notification_sent = True
        appreciation.notification_sent_at = timezone.now()
        appreciation.save(update_fields=['notification_sent', 'notification_sent_at'])


def _send_department_head_notification(appreciation, sender_name):
    from apps.notifications.services import send_email as notify_send_email, get_email_header_html
    from apps.organizations.models import Staff

    if not appreciation.department:
        return

    dept = appreciation.department
    recipient_emails = set()

    if dept.manager and dept.manager.email:
        recipient_emails.add(dept.manager.email)

    heads = Staff.objects.filter(
        department=dept, is_head=True
    ).select_related("user")
    for head in heads:
        if head.user and head.user.email:
            recipient_emails.add(head.user.email)

    if not recipient_emails:
        return

    recipient_name = appreciation.get_recipient_name() if appreciation.recipient else "N/A"

    html = f"""
    <div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
        {get_email_header_html()}
        <div style="padding: 30px;">
            <h2 style="color: #005696; margin-top: 0;">&#127775; New Appreciation in Your Department</h2>
            <div style="background: #ecfdf5; border-left: 4px solid #10b981; padding: 20px; margin: 20px 0;">
                <strong>Staff Member:</strong> {recipient_name}<br>
                <strong>Department:</strong> {dept.name_en or dept.name}<br>
                <strong>From:</strong> {sender_name}<br>
                {f'<strong>Category:</strong> {appreciation.category.name_en}<br>' if appreciation.category else ''}
                <br>
                <strong>Message:</strong><br>
                {appreciation.message_en}
            </div>
            <p>A staff member in your department has received an appreciation. Please acknowledge their good work.</p>
        </div>
    </div>
    """

    plain_message = (
        f"New appreciation for {recipient_name} in {dept.name_en or dept.name}.\n"
        f"From: {sender_name}\n\n"
        f"Message: {appreciation.message_en}"
    )

    for email in recipient_emails:
        try:
            notify_send_email(
                email=email,
                subject=f"New Appreciation for {recipient_name} - {dept.name_en or dept.name}",
                message=plain_message,
                html_message=html,
                related_object=appreciation,
            )
        except Exception as e:
            logger.error(f"Failed to send department head notification: {e}")


def _send_cc_notifications(appreciation, sender_name, html_message, plain_message):
    from apps.notifications.services import send_email as notify_send_email

    cc_list = appreciation.cc_list or []
    if not cc_list:
        return

    recipient_name = appreciation.get_recipient_name() if appreciation.recipient else "N/A"

    for cc_email in cc_list:
        if not cc_email:
            continue
        try:
            notify_send_email(
                email=cc_email.strip(),
                subject=f"CC: Appreciation sent to {recipient_name} - {appreciation.hospital.name}",
                message=plain_message,
                html_message=html_message,
                related_object=appreciation,
            )
        except Exception as e:
            logger.error(f"Failed to send CC notification to {cc_email}: {e}")


def update_appreciation_stats(instance):
    """
    Update appreciation statistics for the recipient.
    
    Creates or updates monthly statistics.
    """
    from django.db.models import F
    
    now = timezone.now()
    year = now.year
    month = now.month
    
    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': {},
        }
    )
    
    AppreciationStats.objects.filter(pk=stats.pk).update(
        received_count=F('received_count') + 1,
        sent_count=F('sent_count') + 1,
    )
    stats.refresh_from_db()
    
    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
        AppreciationStats.objects.filter(pk=stats.pk).update(category_breakdown=category_breakdown)
    
    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,
            )

            # Notify the user
            try:
                from django.contrib.auth import get_user_model
                User = get_user_model()
                user = User.objects.filter(pk=recipient_object_id).first()
                if user and user.email:
                    from apps.notifications.services import NotificationService, get_email_header_html
                    NotificationService.send_email(
                        email=user.email,
                        subject=f"You earned a badge: {badge.name_en}!",
                        message=f"Congratulations! You've earned the \"{badge.name_en}\" badge for receiving {count} appreciations.",
                        html_message=f"""
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
    {get_email_header_html()}
    <div style="padding: 20px;">
        <h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">Congratulations!</h2>
        <p style="margin: 0 0 12px 0;">You've earned the <strong>{badge.name_en}</strong> badge for receiving <strong>{count}</strong> appreciations.</p>
        <p style="margin: 0; color: #6b7280;">Keep up the great work!</p>
    </div>
</div>
""",
                    )
            except Exception as e:
                logger.error(f"Failed to send badge award notification: {e}")


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()
    elif criteria_type == 'diverse_senders':
        return Appreciation.objects.filter(
            recipient_content_type=content_type,
            recipient_object_id=object_id,
            status=AppreciationStatus.SENT,
        ).values('sender').distinct().count()
    elif criteria_type == 'streak_weeks':
        return check_appreciation_streak_count(content_type, object_id)
    return 0


def check_appreciation_streak_count(content_type, object_id):
    """Count consecutive weeks of appreciation ending at current week."""
    from datetime import timedelta

    now = timezone.now()
    streak = 0

    current_week_start = now - timedelta(days=now.weekday())
    current_week_start = current_week_start.replace(hour=0, minute=0, second=0, microsecond=0)
    if Appreciation.objects.filter(
        recipient_content_type=content_type,
        recipient_object_id=object_id,
        status=AppreciationStatus.SENT,
        sent_at__gte=current_week_start,
        sent_at__lte=now,
    ).exists():
        streak += 1

    for i in range(1, 52):
        week_start = current_week_start - timedelta(weeks=i)
        week_end = current_week_start - timedelta(weeks=i - 1)
        if 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():
            streak += 1
        else:
            break

    return streak


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.
    Includes the current partial week.
    """
    from datetime import timedelta
    
    now = timezone.now()
    current_week_start = now - timedelta(days=now.weekday())
    current_week_start = current_week_start.replace(hour=0, minute=0, second=0, microsecond=0)
    
    streak = 0
    
    if Appreciation.objects.filter(
        recipient_content_type=content_type,
        recipient_object_id=object_id,
        status=AppreciationStatus.SENT,
        sent_at__gte=current_week_start,
        sent_at__lte=now,
    ).exists():
        streak += 1

    for i in range(1, required_weeks):
        week_start = current_week_start - timedelta(weeks=i)
        week_end = current_week_start - timedelta(weeks=i - 1)
        
        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:
            streak += 1
        else:
            break
    
    return streak >= required_weeks
