"""
Accounts Celery tasks

This module contains tasks for:
- Sending onboarding reminders to provisional users
- Cleaning up expired invitations
- Processing user acknowledgements
"""

import logging
from datetime import timedelta

from celery import shared_task
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.utils import timezone

from apps.notifications.settings_models import HospitalNotificationSettings
from apps.notifications.settings_service import NotificationServiceWithSettings

from .models import UserProvisionalLog
from .services import EmailService

logger = logging.getLogger(__name__)

User = get_user_model()


@shared_task(bind=True, max_retries=3)
def send_onboarding_reminders(self):
    """
    Send reminder emails to provisional users whose invitations are about to expire.

    Sends reminders at:
    - 48 hours before expiry
    - 24 hours before expiry

    Respects hospital notification settings.
    """
    now = timezone.now()

    # Calculate time windows for reminders
    # 24h reminder: expires between 23-25 hours from now
    reminder_24h_start = now + timedelta(hours=23)
    reminder_24h_end = now + timedelta(hours=25)

    # 48h reminder: expires between 47-49 hours from now
    reminder_48h_start = now + timedelta(hours=47)
    reminder_48h_end = now + timedelta(hours=49)

    logger.info(f"Checking for onboarding reminders at {now}")

    # Find users needing 24h reminder
    users_24h = User.objects.filter(
        is_provisional=True,
        acknowledgement_completed=False,
        invitation_expires_at__gte=reminder_24h_start,
        invitation_expires_at__lte=reminder_24h_end,
        invitation_token__isnull=False,
    ).exclude(
        # Exclude users who already received 24h reminder
        provisional_logs__event_type="onboarding_reminder_sent",
        provisional_logs__description__contains="24h",
    )

    # Find users needing 48h reminder
    users_48h = User.objects.filter(
        is_provisional=True,
        acknowledgement_completed=False,
        invitation_expires_at__gte=reminder_48h_start,
        invitation_expires_at__lte=reminder_48h_end,
        invitation_token__isnull=False,
    ).exclude(
        # Exclude users who already received 48h reminder
        provisional_logs__event_type="onboarding_reminder_sent",
        provisional_logs__description__contains="48h",
    )

    sent_count_24h = 0
    sent_count_48h = 0
    skipped_count = 0
    error_count = 0

    # Send 24h reminders
    for user in users_24h:
        try:
            hospital_id = getattr(user, "hospital_id", None)

            if hospital_id:
                # Check notification settings
                settings = HospitalNotificationSettings.get_for_hospital(hospital_id)

                if not settings.notifications_enabled:
                    logger.info(
                        f"Notifications disabled for hospital {hospital_id}, skipping 24h reminder for {user.email}"
                    )
                    skipped_count += 1
                    continue

                if not settings.onboarding_reminder_email:
                    logger.info(f"Onboarding reminders disabled for hospital {hospital_id}, skipping {user.email}")
                    skipped_count += 1
                    continue

            # Send reminder using new notification service
            results = NotificationServiceWithSettings.send_onboarding_reminder(
                user_email=user.email, provisional_user=user
            )

            # Also send via legacy service for backward compatibility
            EmailService.send_reminder_email(user)

            # Log the reminder
            UserProvisionalLog.objects.create(
                user=user,
                event_type="onboarding_reminder_sent",
                description=f"24h reminder sent before invitation expiry",
                metadata={
                    "hours_before_expiry": 24,
                    "expires_at": user.invitation_expires_at.isoformat(),
                    "channels": [r[0] for r in results] if results else ["email"],
                },
            )

            sent_count_24h += 1
            logger.info(f"Sent 24h onboarding reminder to {user.email}")

        except Exception as e:
            logger.error(f"Failed to send 24h reminder to {user.email}: {str(e)}")
            error_count += 1

    # Send 48h reminders
    for user in users_48h:
        try:
            hospital_id = getattr(user, "hospital_id", None)

            if hospital_id:
                # Check notification settings
                settings = HospitalNotificationSettings.get_for_hospital(hospital_id)

                if not settings.notifications_enabled:
                    logger.info(
                        f"Notifications disabled for hospital {hospital_id}, skipping 48h reminder for {user.email}"
                    )
                    skipped_count += 1
                    continue

                if not settings.onboarding_reminder_email:
                    logger.info(f"Onboarding reminders disabled for hospital {hospital_id}, skipping {user.email}")
                    skipped_count += 1
                    continue

            # Send reminder using new notification service
            results = NotificationServiceWithSettings.send_onboarding_reminder(
                user_email=user.email, provisional_user=user
            )

            # Also send via legacy service for backward compatibility
            EmailService.send_reminder_email(user)

            # Log the reminder
            UserProvisionalLog.objects.create(
                user=user,
                event_type="onboarding_reminder_sent",
                description=f"48h reminder sent before invitation expiry",
                metadata={
                    "hours_before_expiry": 48,
                    "expires_at": user.invitation_expires_at.isoformat(),
                    "channels": [r[0] for r in results] if results else ["email"],
                },
            )

            sent_count_48h += 1
            logger.info(f"Sent 48h onboarding reminder to {user.email}")

        except Exception as e:
            logger.error(f"Failed to send 48h reminder to {user.email}: {str(e)}")
            error_count += 1

    summary = {
        "24h_reminders_sent": sent_count_24h,
        "48h_reminders_sent": sent_count_48h,
        "total_reminders_sent": sent_count_24h + sent_count_48h,
        "skipped_due_to_settings": skipped_count,
        "errors": error_count,
    }

    logger.info(f"Onboarding reminder task completed: {summary}")
    return summary


@shared_task(bind=True, max_retries=3)
def cleanup_expired_invitations(self):
    """
    Clean up expired provisional user invitations.

    Marks users with expired invitations and logs the expiration.
    Optionally sends a final notification about expiration.
    """
    now = timezone.now()

    # Find expired invitations
    expired_users = User.objects.filter(
        is_provisional=True,
        acknowledgement_completed=False,
        invitation_expires_at__lt=now,
        invitation_token__isnull=False,
    ).exclude(
        # Exclude already logged as expired
        provisional_logs__event_type="invitation_expired"
    )

    expired_count = 0
    error_count = 0

    for user in expired_users:
        try:
            # Log the expiration
            UserProvisionalLog.objects.create(
                user=user,
                event_type="invitation_expired",
                description=f"Invitation token expired for {user.email}",
                metadata={
                    "expired_at": now.isoformat(),
                    "original_expiry": user.invitation_expires_at.isoformat() if user.invitation_expires_at else None,
                },
            )

            # Invalidate the token
            user.invitation_token = None
            user.save(update_fields=["invitation_token"])

            expired_count += 1
            logger.info(f"Marked invitation as expired for {user.email}")

        except Exception as e:
            logger.error(f"Failed to process expired invitation for {user.email}: {str(e)}")
            error_count += 1

    summary = {"expired_invitations": expired_count, "errors": error_count}

    logger.info(f"Expired invitation cleanup completed: {summary}")
    return summary


@shared_task(bind=True, max_retries=3)
def send_final_expiration_notice(self, user_id):
    """
    Send a final notification to user that their invitation has expired.

    Args:
        user_id: UUID of the provisional user
    """
    try:
        user = User.objects.get(id=user_id, is_provisional=True)

        # Check notification settings
        hospital_id = getattr(user, "hospital_id", None)

        if hospital_id:
            settings = HospitalNotificationSettings.get_for_hospital(hospital_id)

            if not settings.notifications_enabled or not settings.onboarding_reminder_email:
                logger.info(f"Notifications disabled, skipping expiration notice for {user.email}")
                return {"status": "skipped", "reason": "notifications_disabled"}

        # Send expiration email
        from django.core.mail import send_mail
        from django.conf import settings as django_settings
        from django.template.loader import render_to_string

        subject = "Your PX360 Invitation Has Expired"
        html_message = render_to_string("emails/invitation_expired.html", {"user": user})
        message = f"""
Dear {user.first_name},

Your invitation to join PX360 has expired. 

Please contact your administrator to request a new invitation.

Best regards,
PX360 Team
"""

        send_mail(
            subject=subject,
            message=message,
            from_email=django_settings.DEFAULT_FROM_EMAIL,
            recipient_list=[user.email],
            fail_silently=True,
            html_message=html_message,
        )

        logger.info(f"Sent expiration notice to {user.email}")
        return {"status": "sent", "user_email": user.email}

    except User.DoesNotExist:
        logger.warning(f"User {user_id} not found for expiration notice")
        return {"status": "error", "reason": "user_not_found"}
    except Exception as e:
        logger.error(f"Failed to send expiration notice: {str(e)}")
        return {"status": "error", "reason": str(e)}
