from drf_spectacular.utils import extend_schema
from rest_framework import generics, permissions, status
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response
from rest_framework_simplejwt.exceptions import TokenError
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

from .serializers import (
    DetailSerializer, EmailChangeSerializer, EmailSerializer, LoginSerializer, LogoutSerializer,
    PasswordChangeSerializer, PasswordResetSerializer, ProfileSerializer, RefreshSerializer,
    RegistrationSerializer, TokenConfirmationSerializer,
)
from .services import AccountService
from .throttles import AccountEmailThrottle, AccountIPThrottle

GENERIC_EMAIL_RESPONSE = {"detail": "If the account is eligible, an email will be sent."}


class PublicAccountView(generics.GenericAPIView):
    permission_classes = [permissions.AllowAny]
    authentication_classes = []
    throttle_classes = [AccountIPThrottle, AccountEmailThrottle]
    throttle_scope = "account_public"


class RegisterView(PublicAccountView):
    serializer_class = RegistrationSerializer

    @extend_schema(responses={201: DetailSerializer})
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({"detail": "Account created. Check your email to verify it."}, status=status.HTTP_201_CREATED)


class LoginView(TokenObtainPairView):
    serializer_class = LoginSerializer
    throttle_classes = [AccountIPThrottle, AccountEmailThrottle]
    throttle_scope = "account_login"


class RefreshView(TokenRefreshView):
    serializer_class = RefreshSerializer
    throttle_classes = [AccountIPThrottle]
    throttle_scope = "account_refresh"


class ProfileView(generics.RetrieveUpdateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    serializer_class = ProfileSerializer
    http_method_names = ["get", "patch", "head", "options"]

    def get_object(self):
        return self.request.user


class VerifyEmailView(PublicAccountView):
    serializer_class = TokenConfirmationSerializer

    @extend_schema(responses=DetailSerializer)
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        AccountService.verify_email(**serializer.validated_data)
        return Response({"detail": "Email verified. You can now sign in."})


class ResendVerificationView(PublicAccountView):
    serializer_class = EmailSerializer
    purpose = "verify-email"

    @extend_schema(responses=DetailSerializer)
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        AccountService.request_email(purpose=self.purpose, **serializer.validated_data)
        return Response(GENERIC_EMAIL_RESPONSE)


class PasswordResetRequestView(ResendVerificationView):
    purpose = "reset-password"


class PasswordResetConfirmView(PublicAccountView):
    serializer_class = PasswordResetSerializer

    @extend_schema(responses=DetailSerializer)
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        AccountService.reset_password(**serializer.validated_data)
        return Response({"detail": "Password reset. Sign in again on all devices."})


class PrivateAccountView(generics.GenericAPIView):
    permission_classes = [permissions.IsAuthenticated]
    throttle_classes = [AccountIPThrottle]
    throttle_scope = "account_security"


class PasswordChangeView(PrivateAccountView):
    serializer_class = PasswordChangeSerializer

    @extend_schema(responses=DetailSerializer)
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        AccountService.change_password(user=request.user, **serializer.validated_data)
        return Response({"detail": "Password changed. Sign in again on all devices."})


class LogoutView(PrivateAccountView):
    serializer_class = LogoutSerializer

    @extend_schema(responses={204: None})
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            token = RefreshToken(serializer.validated_data["refresh"])
            if token.get("user_id") != str(request.user.pk):
                raise ValidationError({"refresh": ["Token does not belong to this account."]})
            token.blacklist()
            if token.get('session_id'):
                from .security_models import AccountSession
                from django.utils import timezone
                AccountSession.objects.filter(pk=token['session_id'],user=request.user,revoked_at__isnull=True).update(revoked_at=timezone.now())
        except TokenError as exc:
            raise ValidationError({"refresh": ["Invalid, expired or revoked token."]}) from exc
        return Response(status=status.HTTP_204_NO_CONTENT)


class LogoutAllView(PrivateAccountView):
    @extend_schema(request=None, responses={204: None})
    def post(self, request):
        AccountService.logout_all(user=request.user)
        return Response(status=status.HTTP_204_NO_CONTENT)


class EmailChangeRequestView(PrivateAccountView):
    serializer_class = EmailChangeSerializer

    @extend_schema(responses=DetailSerializer)
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        AccountService.request_email_change(user=request.user, **serializer.validated_data)
        return Response({"detail": "Check the new email address to confirm the change."})


class EmailChangeConfirmView(PublicAccountView):
    serializer_class = TokenConfirmationSerializer

    @extend_schema(responses=DetailSerializer)
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        AccountService.confirm_email_change(**serializer.validated_data)
        return Response({"detail": "Email changed. Sign in again on all devices."})
