# apps/auth/views.py

from django.utils.decorators import method_decorator
from rest_framework import status
from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.views import TokenRefreshView as _JWTRefreshView
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.exceptions import TokenError

from drf_spectacular.utils import (
    extend_schema,
    extend_schema_view,
    OpenApiParameter,
    OpenApiExample,
    OpenApiResponse,
    inline_serializer,
)
from drf_spectacular.types import OpenApiTypes
from rest_framework import serializers as s

from .models import User, AuditLog, UserRole
from .serializers import (
    RegisterSerializer,
    VerifyOTPSerializer,
    ResendOTPSerializer,
    LoginSerializer,
    ForgotPasswordSerializer,
    ResetPasswordSerializer,
    UserListSerializer,
    UserDetailSerializer,
    UserCreateSerializer,
    UserPatchSerializer,
    AuditLogSerializer,
)


def _msg(message: str, code: int = status.HTTP_200_OK, **extra):
    return Response({"message": message, **extra}, status=code)


def _get_client_ip(request) -> str:
    return request.META.get("HTTP_X_FORWARDED_FOR", "").split(",")[
        0
    ].strip() or request.META.get("REMOTE_ADDR", "")


def _log(user, action, model_name="", object_id=None, description="", request=None):
    AuditLog.objects.create(
        user=user,
        action=action,
        model_name=model_name,
        object_id=object_id,
        description=description,
        ip_address=_get_client_ip(request) if request else None,
    )


class RegisterView(APIView):
    permission_classes = [AllowAny]

    @extend_schema(
        tags=["Auth"],
        summary="Register a new admin user",
        description=(
            "Creates an inactive user account and sends a 6-digit OTP to the "
            "supplied email. The account becomes active only after OTP verification."
        ),
        request=RegisterSerializer,
        responses={
            201: OpenApiResponse(
                description="Registration successful — OTP sent.",
                examples=[
                    OpenApiExample(
                        "Success",
                        value={
                            "message": "Registered successfully. Check your email for the OTP."
                        },
                    )
                ],
            ),
            400: OpenApiResponse(
                description="Validation error (e.g. duplicate email)."
            ),
        },
        examples=[
            OpenApiExample(
                "Register request",
                request_only=True,
                value={
                    "email": "admin@example.com",
                    "full_name": "Alice",
                    "password": "Str0ng!pw",
                    "phone": "+977-9800000000",
                },
            )
        ],
    )
    def post(self, request):
        ser = RegisterSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        ser.save()
        return _msg(
            "Registered successfully. Check your email for the OTP.",
            status.HTTP_201_CREATED,
        )


class VerifyOTPView(APIView):
    permission_classes = [AllowAny]

    @extend_schema(
        tags=["Auth"],
        summary="Verify email OTP",
        description="Validates the 6-digit OTP sent during registration. Activates the account on success.",
        request=VerifyOTPSerializer,
        responses={
            200: OpenApiResponse(description="Account verified."),
            400: OpenApiResponse(description="Invalid or expired OTP."),
        },
        examples=[
            OpenApiExample(
                "Verify OTP",
                request_only=True,
                value={"email": "admin@example.com", "otp": "482910"},
            ),
        ],
    )
    def post(self, request):
        ser = VerifyOTPSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        user = ser.save()
        _log(
            user,
            AuditLog.Action.CREATE,
            "User",
            user.pk,
            "Account verified via OTP",
            request,
        )
        return _msg("Account verified successfully.")


class ResendOTPView(APIView):
    permission_classes = [AllowAny]

    @extend_schema(
        tags=["Auth"],
        summary="Resend OTP",
        description="Regenerates and resends the OTP for an unverified account. Previous OTP is invalidated.",
        request=ResendOTPSerializer,
        responses={
            200: OpenApiResponse(description="OTP resent."),
            400: OpenApiResponse(description="No unverified account with that email."),
        },
        examples=[
            OpenApiExample(
                "Resend OTP", request_only=True, value={"email": "admin@example.com"}
            ),
        ],
    )
    def post(self, request):
        ser = ResendOTPSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        ser.save()
        return _msg("OTP resent successfully.")


class LoginView(APIView):
    permission_classes = [AllowAny]

    @extend_schema(
        tags=["Auth"],
        summary="Login",
        description="Authenticates the user and returns JWT access + refresh tokens.",
        request=LoginSerializer,
        responses={
            200: OpenApiResponse(
                description="JWT token pair.",
                examples=[
                    OpenApiExample(
                        "Success",
                        value={"access": "<jwt>", "refresh": "<jwt>"},
                    )
                ],
            ),
            400: OpenApiResponse(
                description="Invalid credentials or unverified account."
            ),
        },
        examples=[
            OpenApiExample(
                "Login",
                request_only=True,
                value={"email": "admin@example.com", "password": "Str0ng!pw"},
            ),
        ],
    )
    def post(self, request):
        ser = LoginSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        tokens = ser.get_tokens(request)
        _log(ser._user, AuditLog.Action.LOGIN, "User", ser._user.pk, "Login", request)
        return Response(tokens)


class LogoutView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        tags=["Auth"],
        summary="Logout",
        description="Blacklists the supplied refresh token, effectively ending the session.",
        request=inline_serializer("LogoutBody", fields={"refresh": s.CharField()}),
        responses={
            200: OpenApiResponse(description="Logged out."),
            400: OpenApiResponse(description="Invalid or already-blacklisted token."),
        },
        examples=[
            OpenApiExample(
                "Logout", request_only=True, value={"refresh": "<jwt-refresh>"}
            ),
        ],
    )
    def post(self, request):
        try:
            token = RefreshToken(request.data.get("refresh"))
            token.blacklist()
        except TokenError as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        _log(
            request.user,
            AuditLog.Action.LOGOUT,
            "User",
            request.user.pk,
            "Logout",
            request,
        )
        return _msg("Logged out successfully.")


class TokenRefreshView(_JWTRefreshView):
    """Wraps SimpleJWT's refresh with Swagger docs."""

    @extend_schema(
        tags=["Auth"],
        summary="Refresh access token",
        description="Exchanges a valid refresh token for a new access token.",
        responses={
            200: OpenApiResponse(
                description="New access token.",
                examples=[OpenApiExample("Success", value={"access": "<jwt>"})],
            ),
            401: OpenApiResponse(description="Refresh token invalid or expired."),
        },
        examples=[
            OpenApiExample(
                "Refresh", request_only=True, value={"refresh": "<jwt-refresh>"}
            ),
        ],
    )
    def post(self, request, *args, **kwargs):
        return super().post(request, *args, **kwargs)


class ForgotPasswordView(APIView):
    permission_classes = [AllowAny]

    @extend_schema(
        tags=["Auth"],
        summary="Forgot password — send OTP",
        description="Sends a password-reset OTP to the given email if an active account exists.",
        request=ForgotPasswordSerializer,
        responses={
            200: OpenApiResponse(description="OTP sent."),
            400: OpenApiResponse(description="No active account with that email."),
        },
        examples=[
            OpenApiExample(
                "Forgot pw", request_only=True, value={"email": "admin@example.com"}
            ),
        ],
    )
    def post(self, request):
        ser = ForgotPasswordSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        ser.save()
        return _msg("Password reset OTP sent to your email.")


class ResetPasswordView(APIView):
    permission_classes = [AllowAny]

    @extend_schema(
        tags=["Auth"],
        summary="Reset password",
        description="Verifies the OTP and sets the new password.",
        request=ResetPasswordSerializer,
        responses={
            200: OpenApiResponse(description="Password reset."),
            400: OpenApiResponse(description="Invalid OTP or validation error."),
        },
        examples=[
            OpenApiExample(
                "Reset pw",
                request_only=True,
                value={
                    "email": "admin@example.com",
                    "otp": "129384",
                    "new_password": "N3w!pass",
                },
            ),
        ],
    )
    def post(self, request):
        ser = ResetPasswordSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        user = ser.save()
        _log(user, AuditLog.Action.UPDATE, "User", user.pk, "Password reset", request)
        return _msg("Password has been reset successfully.")


@extend_schema_view(
    get=extend_schema(
        tags=["Users"],
        summary="List all users",
        description="Returns a paginated list of all users. SuperAdmin only.",
        responses={200: UserListSerializer(many=True)},
        parameters=[
            OpenApiParameter(
                "role",
                OpenApiTypes.STR,
                description="Filter by role: admin | superadmin",
            ),
            OpenApiParameter(
                "is_active",
                OpenApiTypes.BOOL,
                description="Filter active/inactive users",
            ),
        ],
    ),
    post=extend_schema(
        tags=["Users"],
        summary="Create a user",
        description="Creates a new user directly (bypasses OTP flow). SuperAdmin only.",
        request=UserCreateSerializer,
        responses={
            201: UserDetailSerializer,
            400: OpenApiResponse(description="Validation error."),
        },
        examples=[
            OpenApiExample(
                "Create user",
                request_only=True,
                value={
                    "email": "new@example.com",
                    "full_name": "Bob",
                    "password": "Str0ng!pw",
                    "role": "admin",
                },
            ),
        ],
    ),
)
class UserListCreateView(APIView):
    permission_classes = [IsAuthenticated, IsAdminUser]

    def get(self, request):
        qs = User.objects.all()
        role = request.query_params.get("role")
        is_active = request.query_params.get("is_active")

        if role:
            qs = qs.filter(role=role)
        if is_active is not None:
            qs = qs.filter(is_active=is_active.lower() == "true")

        ser = UserListSerializer(qs, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = UserCreateSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        user = ser.save()
        _log(
            request.user,
            AuditLog.Action.CREATE,
            "User",
            user.pk,
            f"User {user.email} created",
            request,
        )
        return Response(UserDetailSerializer(user).data, status=status.HTTP_201_CREATED)


@extend_schema_view(
    get=extend_schema(
        tags=["Users"],
        summary="Get user by ID",
        description="Fetch full details of a single user.",
        responses={
            200: UserDetailSerializer,
            404: OpenApiResponse(description="User not found."),
        },
    ),
    patch=extend_schema(
        tags=["Users"],
        summary="Partial update / Restore user",
        description=(
            "Partially updates user fields. "
            'Pass `"action": "restore"` to reactivate a soft-deleted user.'
        ),
        request=UserPatchSerializer,
        responses={
            200: UserDetailSerializer,
            404: OpenApiResponse(description="User not found."),
        },
        examples=[
            OpenApiExample(
                "Update bio", request_only=True, value={"bio": "New bio text"}
            ),
            OpenApiExample(
                "Restore user", request_only=True, value={"action": "restore"}
            ),
        ],
    ),
    delete=extend_schema(
        tags=["Users"],
        summary="Delete user",
        description=(
            "Soft delete (default) or hard delete.\n\n"
            "- `?isPermanent=false` → sets `is_active=False` (soft delete, reversible via PATCH restore)\n"
            "- `?isPermanent=true`  → permanently removes the user from the database"
        ),
        parameters=[
            OpenApiParameter(
                "isPermanent",
                OpenApiTypes.BOOL,
                OpenApiParameter.QUERY,
                description="true = hard delete | false = soft delete (default)",
                required=False,
            ),
        ],
        responses={
            200: OpenApiResponse(description="Soft-deleted."),
            204: OpenApiResponse(description="Hard-deleted."),
            404: OpenApiResponse(description="User not found."),
        },
    ),
)
class UserDetailView(APIView):
    permission_classes = [IsAuthenticated, IsAdminUser]

    def _get_user(self, pk):
        try:
            return User.objects.get(pk=pk)
        except User.DoesNotExist:
            return None

    def get(self, request, pk):
        user = self._get_user(pk)
        if not user:
            return Response(
                {"error": "User not found."}, status=status.HTTP_404_NOT_FOUND
            )
        return Response(UserDetailSerializer(user).data)

    def patch(self, request, pk):
        user = self._get_user(pk)
        if not user:
            return Response(
                {"error": "User not found."}, status=status.HTTP_404_NOT_FOUND
            )

        ser = UserPatchSerializer(user, data=request.data, partial=True)
        ser.is_valid(raise_exception=True)
        updated = ser.save()
        _log(
            request.user,
            AuditLog.Action.UPDATE,
            "User",
            updated.pk,
            "User patched",
            request,
        )
        return Response(UserDetailSerializer(updated).data)

    def delete(self, request, pk):
        user = self._get_user(pk)
        if not user:
            return Response(
                {"error": "User not found."}, status=status.HTTP_404_NOT_FOUND
            )

        is_permanent = (
            request.query_params.get("isPermanent", "false").lower() == "true"
        )

        if is_permanent:
            _log(
                request.user,
                AuditLog.Action.DELETE,
                "User",
                user.pk,
                f"Hard-deleted user {user.email}",
                request,
            )
            user.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)

        user.is_active = False
        user.save()
        _log(
            request.user,
            AuditLog.Action.DELETE,
            "User",
            user.pk,
            f"Soft-deleted user {user.email}",
            request,
        )
        return _msg("User soft-deleted. Use PATCH with action=restore to undo.")


@extend_schema_view(
    get=extend_schema(
        tags=["Audit Logs"],
        summary="List audit logs",
        description="Returns all audit log entries, most recent first. SuperAdmin only.",
        responses={200: AuditLogSerializer(many=True)},
        parameters=[
            OpenApiParameter(
                "action",
                OpenApiTypes.STR,
                description="Filter by action: create | update | delete | login | logout",
            ),
            OpenApiParameter(
                "model_name",
                OpenApiTypes.STR,
                description="Filter by model name e.g. 'User'",
            ),
        ],
    ),
)
class AuditLogListView(APIView):
    permission_classes = [IsAuthenticated, IsAdminUser]

    def get(self, request):
        qs = AuditLog.objects.select_related("user").all()
        action = request.query_params.get("action")
        model_name = request.query_params.get("model_name")

        if action:
            qs = qs.filter(action=action)
        if model_name:
            qs = qs.filter(model_name=model_name)

        ser = AuditLogSerializer(qs, many=True)
        return Response(ser.data)


@extend_schema_view(
    get=extend_schema(
        tags=["Audit Logs"],
        summary="Get audit log by ID",
        responses={
            200: AuditLogSerializer,
            404: OpenApiResponse(description="Not found."),
        },
    ),
)
class AuditLogDetailView(APIView):
    permission_classes = [IsAuthenticated, IsAdminUser]

    def get(self, request, pk):
        try:
            log = AuditLog.objects.select_related("user").get(pk=pk)
        except AuditLog.DoesNotExist:
            return Response(
                {"error": "Audit log not found."}, status=status.HTTP_404_NOT_FOUND
            )
        return Response(AuditLogSerializer(log).data)
