from typing import Any

from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractBaseUser
from django.db.models import QuerySet
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 (
    generics,
    status,
)
from rest_framework.request import Request
from rest_framework.permissions import (
    AllowAny,
    IsAuthenticated,
)
from rest_framework.response import Response
from rest_framework.views import APIView

from vq_django_auth import (
    serializers,
    usecases,
)
from vq_django_auth.app_settings import api_settings
from vq_django_auth.choices import AuthMethod
from vq_django_auth.throttle import VQAuthThrottle

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


class LoginView(APIView):
    """
    Authentication endpoint that validates user credentials and handles authentication.

    This view handles user authentication with username/email and password.
    The response depends on the configured authentication method:
    - TOKEN: Returns a token that can be used for future authenticated requests
    - SESSION: Creates a Django session and returns 204 No Content

    Accepts:
        POST: with username/email and password fields

    Returns:
        - TOKEN mode: A REST Framework Token on success (200 OK)
        - SESSION mode: Empty response with 204 No Content on success
        - Error details on failure (400 Bad Request)

    Configuration:
        This view uses the LOGIN_SERIALIZER and TOKEN_SERIALIZER from api_settings.
        Authentication method can be configured with AUTH_METHOD setting (SESSION or TOKEN).
    """

    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 POST request to authenticate a user and return token.
        """
        serializer = api_settings.LOGIN_SERIALIZER(data=request.data)
        serializer.is_valid(raise_exception=True)

        user, token = usecases.authenticate_user(
            request=request, **serializer.validated_data
        )
        if api_settings.AUTH_METHOD is AuthMethod.SESSION:
            return Response(status=status.HTTP_204_NO_CONTENT)

        token_serializer = serializers.TokenSerializer(instance=token)
        return Response(token_serializer.data, status=status.HTTP_200_OK)


class LogoutView(APIView):
    """
    Endpoint to log out a user by invalidating their authentication token.

    This view handles user logout by:
    1. Deleting the user's authentication token if token auth is used
    2. Logging out of the Django session if AUTH_METHOD is set to SESSION

    Accepts:
        POST: no required fields

    Returns:
        200 OK with success message on successful logout
    """

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

    def post(self, request: Request, *args: Any, **kwargs: Any) -> Response:
        """
        Handle POST request for logout.
        Execute the logout process by removing the token and ending the session.
        """
        if not (request.auth or api_settings.AUTH_METHOD is AuthMethod.SESSION):
            return Response(
                {
                    "detail": _(
                        "You should be logged in to logout. Check whether the token is passed."
                    )
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        usecases.logout_user(request=request)
        return Response(
            {"detail": _("Successfully logged out.")},
            status=status.HTTP_200_OK,
        )


class UserDetailsView(generics.RetrieveAPIView[AbstractBaseUser]):
    """
    Endpoint for retrieving the authenticated user's details.

    This view allows users to get their own user data.

    Accepts:
        GET: retrieves user details

    Returns:
        User data serialized according to USER_DETAILS_SERIALIZER

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

    serializer_class = api_settings.USER_DETAILS_SERIALIZER
    permission_classes = (IsAuthenticated,)

    def get_object(self) -> AbstractBaseUser:
        """Return the authenticated user."""
        return self.request.user  # type: ignore[return-value]

    def get_queryset(self) -> QuerySet[AbstractBaseUser]:
        """
        Added for compatibility with documentation frameworks.

        This view doesn't use a queryset since it operates on the request user,
        but some documentation tools expect this method to exist.
        """
        return get_user_model().objects.none()
