"""
Accounts services - Onboarding, email notifications, and other account services
"""

import logging
import secrets
from datetime import timedelta

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils import timezone

from django.db import models as db_models

from .models import (
    AcknowledgementChecklistItem,
    AcknowledgementContent,
    UserAcknowledgement,
    UserProvisionalLog,
)


class OnboardingService:
    """Service for managing user onboarding and acknowledgements"""

    logger = logging.getLogger(__name__)

    @staticmethod
    def create_provisional_user(user_data):
        """
        Create a provisional user with invitation token

        Args:
            user_data: Dict with user fields (email, first_name, last_name, etc.)

        Returns:
            User instance with is_provisional=True
        """
        from django.contrib.auth import get_user_model

        User = get_user_model()

        # Create user with unusable password
        user_data["is_provisional"] = True
        user_data["is_active"] = True  # Active but needs onboarding
        user_data["invitation_token"] = OnboardingService.generate_token()
        user_data["invitation_expires_at"] = timezone.now() + timedelta(days=7)

        user = User.objects.create(**user_data)
        user.set_unusable_password()
        user.save()

        # Log creation (only store simple data, not objects)
        log_metadata = {
            "email": user.email,
            "first_name": user.first_name,
            "last_name": user.last_name,
            "hospital_id": str(user.hospital_id) if user.hospital_id else None,
            "department_id": str(user.department_id) if user.department_id else None,
        }
        UserProvisionalLog.objects.create(
            user=user, event_type="created", description=f"Provisional user created", metadata=log_metadata
        )

        return user

    @staticmethod
    def generate_token():
        """Generate a secure invitation token"""
        return secrets.token_urlsafe(32)

    @staticmethod
    def validate_token(token):
        """
        Validate invitation token

        Args:
            token: Invitation token string

        Returns:
            User instance if valid, None otherwise
        """
        from django.contrib.auth import get_user_model

        User = get_user_model()

        try:
            user = User.objects.get(invitation_token=token, is_provisional=True)

            # Check if expired
            if user.invitation_expires_at and user.invitation_expires_at < timezone.now():
                # Log expiration
                UserProvisionalLog.objects.create(
                    user=user, event_type="invitation_expired", description="Invitation token expired"
                )
                return None

            return user
        except User.DoesNotExist:
            return None

    @staticmethod
    def get_wizard_content(user):
        return AcknowledgementContent.objects.filter(is_active=True).order_by("category", "order")

    @staticmethod
    def get_checklist_items(user):
        return AcknowledgementChecklistItem.objects.filter(is_active=True).order_by("category", "order", "code")

    @staticmethod
    def get_user_acknowledgements(user):
        """
        Get all acknowledgements for a user

        Args:
            user: User instance

        Returns:
            QuerySet of UserAcknowledgement
        """
        return UserAcknowledgement.objects.filter(user=user)

    @staticmethod
    def get_user_progress_percentage(user):
        """
        Calculate user's onboarding progress percentage

        Args:
            user: User instance

        Returns:
            Float percentage (0-100)
        """
        # Get all required checklist items for user
        required_items = OnboardingService.get_checklist_items(user).filter(is_required=True)

        if not required_items.exists():
            return 100.0

        # Count acknowledged items
        acknowledged_count = UserAcknowledgement.objects.filter(
            user=user, checklist_item__in=required_items, is_acknowledged=True
        ).count()

        total_count = required_items.count()

        return (acknowledged_count / total_count) * 100

    @staticmethod
    def save_wizard_step(user, step_id):
        """
        Save completed wizard step

        Args:
            user: User instance
            step_id: Step identifier
        """
        if step_id not in user.wizard_completed_steps:
            user.wizard_completed_steps.append(step_id)
            user.current_wizard_step = max(user.current_wizard_step, step_id)
            user.save(update_fields=["wizard_completed_steps", "current_wizard_step"])

            # Log step completion
            UserProvisionalLog.objects.create(
                user=user,
                event_type="step_completed",
                description=f"Completed wizard step {step_id}",
                metadata={"step_id": step_id},
            )

    @staticmethod
    def acknowledge_item(user, checklist_item, signature=None, request=None):
        """
        Acknowledge a checklist item

        Args:
            user: User instance
            checklist_item: AcknowledgementChecklistItem instance
            signature: Optional signature data
            request: Optional request object for IP/user agent

        Returns:
            UserAcknowledgement instance
        """
        from .pdf_service import AcknowledgementPDFService
        from django.core.files.uploadedfile import SimpleUploadedFile
        import uuid

        # Get or create acknowledgement
        acknowledgement, created = UserAcknowledgement.objects.get_or_create(
            user=user,
            checklist_item=checklist_item,
            defaults={
                "is_acknowledged": True,
                "signature": signature or "",
                "signature_ip": OnboardingService._get_client_ip(request) if request else None,
                "signature_user_agent": request.META.get("HTTP_USER_AGENT", "") if request else "",
            },
        )

        if created:
            # Generate PDF
            try:
                language = user.language or "en"
                pdf_data = AcknowledgementPDFService.generate_pdf(user, acknowledgement, language)

                # Create filename
                filename = (
                    f"acknowledgement_{user.employee_id or user.id}_{checklist_item.code}_{uuid.uuid4().hex[:8]}.pdf"
                )

                # Save PDF
                acknowledgement.pdf_file.save(
                    filename, SimpleUploadedFile(filename, pdf_data, content_type="application/pdf"), save=True
                )
            except Exception as e:
                print(f"Error generating PDF for acknowledgement: {e}")

            # Log acknowledgement
            UserProvisionalLog.objects.create(
                user=user,
                event_type="step_completed",
                description=f"Acknowledged item: {checklist_item.code}",
                metadata={"checklist_item_id": str(checklist_item.id)},
            )

        return acknowledgement

    @staticmethod
    def complete_wizard(user, username, password, signature_data, request=None):
        """
        Complete wizard and activate user account

        Args:
            user: User instance
            username: Desired username
            password: Desired password
            signature_data: Final signature data
            request: Optional request object

        Returns:
            Boolean indicating success
        """
        from django.contrib.auth import get_user_model

        User = get_user_model()

        # Check if username is available
        if User.objects.filter(username=username).exists():
            return False

        # Check if all required items are acknowledged
        required_items = OnboardingService.get_checklist_items(user).filter(is_required=True)
        acknowledged_items = UserAcknowledgement.objects.filter(
            user=user, checklist_item__in=required_items, is_acknowledged=True
        )

        if acknowledged_items.count() != required_items.count():
            return False

        # Activate user
        user.is_provisional = False
        user.acknowledgement_completed = True
        user.acknowledgement_completed_at = timezone.now()
        user.username = username
        user.set_password(password)
        user.invitation_token = None
        user.invitation_expires_at = None
        user.save(
            update_fields=[
                "is_provisional",
                "acknowledgement_completed",
                "acknowledgement_completed_at",
                "username",
                "invitation_token",
                "invitation_expires_at",
            ]
        )

        # Log activation
        UserProvisionalLog.objects.create(
            user=user,
            event_type="user_activated",
            description="User account activated after completing onboarding",
            ip_address=OnboardingService._get_client_ip(request) if request else None,
            user_agent=request.META.get("HTTP_USER_AGENT", "") if request else "",
        )

        return True

    @staticmethod
    def _get_client_ip(request):
        """Get client IP address from request"""
        x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
        if x_forwarded_for:
            ip = x_forwarded_for.split(",")[0]
        else:
            ip = request.META.get("REMOTE_ADDR")
        return ip

    @staticmethod
    def get_pending_onboarding_users():
        """
        Get count of pending onboarding users

        Returns:
            Integer count
        """
        from django.contrib.auth import get_user_model

        User = get_user_model()

        return User.objects.filter(is_provisional=True, acknowledgement_completed=False).count()


class PasswordResetTokenService:
    """Service for one-time admin password reset links."""

    @staticmethod
    def create_reset_token(user):
        if user.is_provisional:
            raise ValueError("Provisional users should use the onboarding invitation flow.")

        user.invitation_token = secrets.token_urlsafe(32)
        user.invitation_expires_at = timezone.now() + timedelta(hours=24)
        user.set_unusable_password()
        user.save(update_fields=["invitation_token", "invitation_expires_at", "password"])
        return user.invitation_token

    @staticmethod
    def validate_reset_token(token):
        from django.contrib.auth import get_user_model

        User = get_user_model()
        try:
            return User.objects.get(
                invitation_token=token,
                invitation_expires_at__gte=timezone.now(),
                is_provisional=False,
            )
        except User.DoesNotExist:
            return None

    @staticmethod
    def clear_reset_token(user):
        user.invitation_token = None
        user.invitation_expires_at = None
        user.save(update_fields=["invitation_token", "invitation_expires_at"])

    @staticmethod
    def build_reset_url(base_url, token):
        return f"{base_url.rstrip('/')}/accounts/password/reset/{token}/"


class EmailService:
    """Service for sending onboarding-related emails"""

    @staticmethod
    def send_invitation_email(user, request=None):
        """
        Send invitation email to provisional user

        Args:
            user: User instance
            request: Optional request object for building URLs

        Returns:
            Boolean indicating success
        """
        # Build activation URL
        base_url = getattr(settings, "BASE_URL", "http://localhost:8000")
        activation_url = f"{base_url}/accounts/onboarding/activate/{user.invitation_token}/"
        days_remaining = (
            max((user.invitation_expires_at - timezone.now()).days, 0) if user.invitation_expires_at else 0
        )

        # Render email content
        context = {
            "user": user,
            "activation_url": activation_url,
            "expires_at": user.invitation_expires_at,
            "days_remaining": days_remaining,
        }

        subject = render_to_string("accounts/onboarding/invitation_subject.txt", context).strip()
        message_html = render_to_string("accounts/onboarding/invitation_email.html", context)
        message_text = render_to_string("accounts/onboarding/invitation_email.txt", context)

        # Send email
        try:
            send_mail(
                subject=subject,
                message=message_text,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[user.email],
                html_message=message_html,
                fail_silently=False,
            )

            OnboardingService.logger.info(
                "Invitation email sent | to=%s | subject=%s | activation_url=%s",
                user.email,
                subject,
                activation_url,
            )

            # Log invitation sent
            UserProvisionalLog.objects.create(
                user=user,
                event_type="invitation_sent",
                description="Invitation email sent",
                metadata={"email": user.email},
            )

            return True
        except Exception as e:
            print(f"Error sending invitation email: {e}")
            return False

    @staticmethod
    def send_reminder_email(user, request=None):
        """
        Send reminder email to pending user

        Args:
            user: User instance
            request: Optional request object

        Returns:
            Boolean indicating success
        """
        # Build activation URL
        base_url = getattr(settings, "BASE_URL", "http://localhost:8000")
        activation_url = f"{base_url}/accounts/onboarding/activate/{user.invitation_token}/"
        days_remaining = (
            max((user.invitation_expires_at - timezone.now()).days, 0) if user.invitation_expires_at else 0
        )

        # Render email content
        context = {
            "user": user,
            "activation_url": activation_url,
            "expires_at": user.invitation_expires_at,
            "days_remaining": days_remaining,
        }

        subject = render_to_string("accounts/onboarding/reminder_subject.txt", context).strip()
        message_html = render_to_string("accounts/onboarding/reminder_email.html", context)
        message_text = render_to_string("accounts/onboarding/reminder_email.txt", context)

        # Send email
        try:
            send_mail(
                subject=subject,
                message=message_text,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[user.email],
                html_message=message_html,
                fail_silently=False,
            )

            # Log reminder sent
            UserProvisionalLog.objects.create(
                user=user,
                event_type="invitation_resent",
                description="Reminder email sent",
                metadata={"email": user.email},
            )

            return True
        except Exception as e:
            print(f"Error sending reminder email: {e}")
            return False

    @staticmethod
    def send_completion_notification(user, admin_users, request=None):
        """
        Send notification to admins about user completion

        Args:
            user: User instance who completed onboarding
            admin_users: QuerySet of admin users to notify
            request: Optional request object

        Returns:
            Boolean indicating success
        """
        base_url = getattr(settings, "BASE_URL", "http://localhost:8000")
        user_detail_url = f"{base_url}/accounts/onboarding/provisional/{user.id}/progress/"

        role_display = ", ".join(user.get_role_names()) if hasattr(user, "get_role_names") else ""
        completed_at = user.acknowledgement_completed_at or timezone.now()

        # Render email content
        context = {
            "user": user,
            "user_detail_url": user_detail_url,
            "role_display": role_display,
            "completed_at": completed_at,
        }

        subject = render_to_string("accounts/onboarding/completion_subject.txt", context).strip()
        message_html = render_to_string("accounts/onboarding/completion_email.html", context)
        message_text = render_to_string("accounts/onboarding/completion_email.txt", context)

        # Get admin email list
        admin_emails = [admin.email for admin in admin_users if admin.email]

        if not admin_emails:
            return False

        # Send email
        try:
            send_mail(
                subject=subject,
                message=message_text,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=admin_emails,
                html_message=message_html,
                fail_silently=False,
            )

            return True
        except Exception as e:
            print(f"Error sending completion notification: {e}")
            return False


class StaffActivityService:
    """Service for logging staff activity across the application."""

    logger = logging.getLogger(__name__)

    @staticmethod
    def log(
        user,
        activity_type,
        description="",
        content_object=None,
        module="",
        action="",
        request=None,
        metadata=None,
    ):
        from .models import StaffActivityLog

        kwargs = {
            "user": user,
            "activity_type": activity_type,
            "description": description,
            "module": module,
            "action": action,
            "metadata": metadata or {},
        }

        if content_object is not None:
            kwargs["content_type"] = ContentType.objects.get_for_model(
                content_object.__class__
            )
            kwargs["object_id"] = content_object.pk

        if request is not None:
            kwargs["ip_address"] = StaffActivityService._get_client_ip(request)
            kwargs["user_agent"] = request.META.get("HTTP_USER_AGENT", "")[:500]

        try:
            StaffActivityLog.objects.create(**kwargs)
        except Exception as e:
            StaffActivityService.logger.error(f"Failed to log staff activity: {e}")

    @staticmethod
    def log_from_request(request, activity_type, description="", **kwargs):
        user = getattr(request, "user", None)
        if user and user.is_authenticated:
            StaffActivityService.log(
                user=user,
                activity_type=activity_type,
                description=description,
                request=request,
                **kwargs,
            )

    @staticmethod
    def _get_client_ip(request):
        x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
        if x_forwarded_for:
            return x_forwarded_for.split(",")[0].strip()
        return request.META.get("REMOTE_ADDR")
