"""
User Deletion Service

This service handles user account deletion while preserving submissions
and maintaining data integrity across the platform.
"""

import logging
import secrets
import string
from datetime import datetime
from typing import Any

from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.mail import send_mail
from django.db import transaction
from django.template.loader import render_to_string
from django.utils import timezone

from apps.core.utils import capture_and_log

logger = logging.getLogger(__name__)

User = get_user_model()


class UserDeletionRequest:
    """Model for tracking user deletion requests."""

    def __init__(self, user: User, verification_code: str, expires_at: datetime):
        self.user = user
        self.verification_code = verification_code
        self.expires_at = expires_at
        self.created_at = timezone.now()


class UserDeletionService:
    """Service for handling user account deletion with submission preservation."""

    # In-memory storage for deletion requests (in production, use Redis or database)
    _deletion_requests = {}

    @staticmethod
    def can_delete_user(user: User) -> tuple[bool, str | None]:
        """
        Check if a user can be safely deleted.

        Args:
            user: User to check for deletion eligibility

        Returns:
            Tuple[bool, Optional[str]]: (can_delete, reason_if_not)
        """
        # Prevent deletion of superusers
        if user.is_superuser:
            return False, "Cannot delete superuser accounts"

        # Prevent deletion of staff/admin users
        if user.is_staff:
            return False, "Cannot delete staff accounts"

        # Check for system users (if you have any special system accounts)
        if hasattr(user, "role") and user.role in ["ADMIN", "MODERATOR"]:
            return False, "Cannot delete admin or moderator accounts"

        return True, None

    @staticmethod
    def request_user_deletion(user: User) -> UserDeletionRequest:
        """
        Create a deletion request for a user and send verification email.

        Args:
            user: User requesting deletion

        Returns:
            UserDeletionRequest: The deletion request object

        Raises:
            ValueError: If user cannot be deleted
        """
        # Check if user can be deleted
        can_delete, reason = UserDeletionService.can_delete_user(user)
        if not can_delete:
            raise ValueError(reason)

        # Generate verification code
        verification_code = "".join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(8))

        # Set expiration (24 hours from now)
        expires_at = timezone.now() + timezone.timedelta(hours=24)

        # Create deletion request
        deletion_request = UserDeletionRequest(user, verification_code, expires_at)

        # Store request (in production, use Redis or database)
        UserDeletionService._deletion_requests[verification_code] = deletion_request

        # Send verification email
        UserDeletionService._send_deletion_verification_email(user, verification_code, expires_at)

        return deletion_request

    @staticmethod
    def verify_and_delete_user(verification_code: str) -> dict[str, Any]:
        """
        Verify deletion code and delete user account.

        Args:
            verification_code: Verification code from email

        Returns:
            Dict[str, Any]: Deletion result information

        Raises:
            ValueError: If verification code is invalid or expired
        """
        # Find deletion request
        deletion_request = UserDeletionService._deletion_requests.get(verification_code)
        if not deletion_request:
            raise ValueError("Invalid verification code")

        # Check if expired
        if timezone.now() > deletion_request.expires_at:
            # Clean up expired request
            del UserDeletionService._deletion_requests[verification_code]
            raise ValueError("Verification code has expired")

        user = deletion_request.user

        # Perform deletion
        result = UserDeletionService.delete_user_preserve_submissions(user)

        # Clean up deletion request
        del UserDeletionService._deletion_requests[verification_code]

        # Add verification info to result
        result["deletion_request"] = {
            "verification_code": verification_code,
            "created_at": deletion_request.created_at,
            "verified_at": timezone.now(),
        }

        return result

    @staticmethod
    def cancel_deletion_request(user: User) -> bool:
        """
        Cancel a pending deletion request for a user.

        Args:
            user: User whose deletion request to cancel

        Returns:
            bool: True if request was found and cancelled, False if no request found
        """
        # Find and remove any deletion requests for this user
        to_remove = []
        for code, request in UserDeletionService._deletion_requests.items():
            if request.user.id == user.id:
                to_remove.append(code)

        for code in to_remove:
            del UserDeletionService._deletion_requests[code]

        return len(to_remove) > 0

    @staticmethod
    @transaction.atomic
    def delete_user_preserve_submissions(user: User) -> dict[str, Any]:
        """
        Delete a user account while preserving all their submissions.

        Args:
            user: User to delete

        Returns:
            Dict[str, Any]: Information about the deletion and preserved submissions
        """
        # Get or create the "deleted_user" placeholder
        deleted_user_placeholder, created = User.objects.get_or_create(
            username="deleted_user",
            defaults={
                "email": "deleted@thrillwiki.com",
                "first_name": "Deleted",
                "last_name": "User",
                "is_active": False,
            },
        )

        # Count submissions before transfer
        submission_counts = UserDeletionService._count_user_submissions(user)

        # Transfer submissions to placeholder user
        UserDeletionService._transfer_user_submissions(user, deleted_user_placeholder)

        # Store user info before deletion
        deleted_user_info = {
            "username": user.username,
            "user_id": getattr(user, "user_id", user.id),
            "email": user.email,
            "date_joined": user.date_joined,
        }

        # Delete the user account
        user.delete()

        return {
            "deleted_user": deleted_user_info,
            "preserved_submissions": submission_counts,
            "transferred_to": {
                "username": deleted_user_placeholder.username,
                "user_id": getattr(deleted_user_placeholder, "user_id", deleted_user_placeholder.id),
            },
        }

    @staticmethod
    def _count_user_submissions(user: User) -> dict[str, int]:
        """Count all submissions for a user."""
        counts = {}

        # Count different types of submissions
        # Note: These are placeholder counts - adjust based on your actual models
        counts["park_reviews"] = getattr(user, "park_reviews", user.__class__.objects.none()).count()
        counts["ride_reviews"] = getattr(user, "ride_reviews", user.__class__.objects.none()).count()
        counts["uploaded_park_photos"] = getattr(user, "uploaded_park_photos", user.__class__.objects.none()).count()
        counts["uploaded_ride_photos"] = getattr(user, "uploaded_ride_photos", user.__class__.objects.none()).count()
        counts["top_lists"] = getattr(user, "top_lists", user.__class__.objects.none()).count()
        counts["edit_submissions"] = getattr(user, "edit_submissions", user.__class__.objects.none()).count()
        counts["photo_submissions"] = getattr(user, "photo_submissions", user.__class__.objects.none()).count()

        return counts

    @staticmethod
    def _transfer_user_submissions(user: User, placeholder_user: User) -> None:
        """Transfer all user submissions to placeholder user."""

        # Transfer different types of submissions
        # Note: Adjust these based on your actual model relationships

        # Park reviews
        if hasattr(user, "park_reviews"):
            user.park_reviews.all().update(user=placeholder_user)

        # Ride reviews
        if hasattr(user, "ride_reviews"):
            user.ride_reviews.all().update(user=placeholder_user)

        # Uploaded photos
        if hasattr(user, "uploaded_park_photos"):
            user.uploaded_park_photos.all().update(user=placeholder_user)

        if hasattr(user, "uploaded_ride_photos"):
            user.uploaded_ride_photos.all().update(user=placeholder_user)

        # Top lists
        if hasattr(user, "top_lists"):
            user.top_lists.all().update(user=placeholder_user)

        # Edit submissions
        if hasattr(user, "edit_submissions"):
            user.edit_submissions.all().update(user=placeholder_user)

        # Photo submissions
        if hasattr(user, "photo_submissions"):
            user.photo_submissions.all().update(user=placeholder_user)

    @staticmethod
    def _send_deletion_verification_email(user: User, verification_code: str, expires_at: timezone.datetime) -> None:
        """Send verification email for account deletion."""
        try:
            context = {
                "user": user,
                "verification_code": verification_code,
                "expires_at": expires_at,
                "site_name": "ThrillWiki",
                "site_url": getattr(settings, "SITE_URL", "https://thrillwiki.com"),
            }

            subject = "ThrillWiki: Confirm Account Deletion"
            html_message = render_to_string("emails/account_deletion_verification.html", context)
            plain_message = render_to_string("emails/account_deletion_verification.txt", context)

            send_mail(
                subject=subject,
                message=plain_message,
                html_message=html_message,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[user.email],
                fail_silently=False,
            )

            logger.info(f"Deletion verification email sent to {user.email}")

        except Exception as e:
            capture_and_log(e, f'Send deletion verification email to {user.email}', source='service')
            raise
