import logging
from typing import Any

from django.contrib.auth.tokens import default_token_generator
from django.http import HttpResponseBase
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views.decorators.debug import sensitive_post_parameters
from rest_framework import status
from rest_framework.request import Request
from rest_framework.views import APIView
from rest_framework.permissions import (
    AllowAny,
    IsAuthenticated,
)
from rest_framework.response import Response

from vq_django_auth import (
    serializers,
    usecases,
)
from vq_django_auth.throttle import VQAuthThrottle

logger = logging.getLogger("vq_django_auth.views")


sensitive_post_parameters_m = method_decorator(
    sensitive_post_parameters(
        "password",
        "old_password",
        "new_password1",
        "new_password2",
    ),
)


class PasswordResetView(APIView):
    """
    Endpoint for initiating a password reset process via email.

    This view sends a password reset email to the user with a link or token
    that can be used to reset their password.

    Accepts:
        POST with email field

    Returns:
        200 OK with success message if email was sent (or would have been sent)

    Notes:
        For security reasons, this endpoint returns 200 OK even if the email
        doesn't exist in the system. This prevents user enumeration attacks.
    """

    permission_classes = (AllowAny,)
    throttle_classes = [
        VQAuthThrottle,
    ]

    def post(self, request: Request, *args: Any, **kwargs: Any) -> Response:
        """
        Handle password reset request by validating email and sending reset link.
        """
        serializer = serializers.PasswordResetSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user_email = serializer.validated_data["email"]
        user = usecases.find_user_by_email(user_email)
        if user is None:
            logger.info(
                "Password reset requested for non-existent or inactive user: %s",
                user_email,
            )
        else:
            usecases.initiate_password_reset(
                request=request,
                user_email=user_email,
                user=user,
                token=default_token_generator.make_token(user),
            )

        # Return the success message with OK HTTP status
        return Response(
            {
                "detail": _(
                    "Password reset e-mail has been sent if the e-mail was found."
                )
            },
            status=status.HTTP_200_OK,
        )


class PasswordResetConfirmView(APIView):
    """
    Endpoint for completing the password reset process.

    This view validates the token and user ID from the reset link,
    then sets the new password if validation is successful.

    Accepts:
        POST with uid, token, new_password1, and new_password2 fields

    Returns:
        200 OK with success message if password was successfully reset
        400 BAD REQUEST with error details if validation fails
    """

    permission_classes = (AllowAny,)
    throttle_classes = [
        VQAuthThrottle,
    ]

    @sensitive_post_parameters_m
    def dispatch(self, *args: Any, **kwargs: Any) -> HttpResponseBase:
        return super().dispatch(*args, **kwargs)

    def post(self, request: Request, *args: Any, **kwargs: Any) -> Response:
        """
        Handle password reset confirmation by validating the token and setting the new password.
        """
        serializer = serializers.PasswordResetConfirmSerializer(
            data=request.data, context={"request": request}
        )
        serializer.is_valid(raise_exception=True)
        _user, token = usecases.confirm_password_reset(
            request=request,
            data=serializer.validated_data,
        )
        data = {"detail": _("Password has been reset with the new password.")}
        if token:
            token_data = serializers.TokenSerializer(instance=token)
            data.update(token_data.data)

        return Response(data)


class PasswordChangeView(APIView):
    """
    Endpoint for authenticated users to change their password.

    This view allows users to change their password while authenticated.

    Accepts:
        POST with new_password1, new_password2, and optionally old_password fields

    Returns:
        200 OK with success message if password was successfully changed
        400 BAD REQUEST with error details if validation fails

    Configuration:
        This view uses PASSWORD_CHANGE_SERIALIZER from api_settings.
    """

    permission_classes = (IsAuthenticated,)
    throttle_classes = [
        VQAuthThrottle,
    ]

    @sensitive_post_parameters_m
    def dispatch(self, *args: Any, **kwargs: Any) -> HttpResponseBase:
        return super().dispatch(*args, **kwargs)

    def post(self, request: Request, *args: Any, **kwargs: Any) -> Response:
        """
        Handle password change request by validating passwords and updating the user.
        """
        serializer = serializers.PasswordChangeSerializer(
            data=request.data, context={"request": request}
        )
        serializer.is_valid(raise_exception=True)
        _user, token = usecases.change_user_password(
            request=request, data=serializer.validated_data
        )
        data = {"detail": _("New password has been saved.")}
        if token:
            token_data = serializers.TokenSerializer(instance=token)
            data.update(token_data.data)

        return Response(data)
