from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import RegisterSerializer

from rest_framework.permissions import AllowAny
from rest_framework.decorators import permission_classes
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import AllowAny


@api_view(['GET'])
@permission_classes([AllowAny])
def health(_): return Response({'ok': True})


@permission_classes([AllowAny])
class RegisterAPIView(APIView):
    def post(self, request):
        serializer = RegisterSerializer(data=request.data)
        # print(request.data)
        if serializer.is_valid():
            user = serializer.save()
            return Response({"status": "success", "message": "User registered successfully"}, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# accounts/api_views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from accounts.models import CustomUser

@api_view(['GET'])
@permission_classes([AllowAny])
def validate_referral_code(request):
    code = request.GET.get('referral_code')
    if not code:
        return Response({"valid": False, "message": "No code provided"}, status=400)
    
    try:
        user = CustomUser.objects.get(referral_code=code)
        return Response({
            "valid": True,
            "referrer_first_name": user.first_name,
            "referrer_last_name": user.last_name
        })
    except CustomUser.DoesNotExist:
        return Response({"valid": False, "message": "Invalid referral code"})


from django.http import JsonResponse
from .models import State, District
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import DistrictSerializer
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from .models import State, District

@api_view(['GET'])
@permission_classes([AllowAny])
@authentication_classes([])  # optional; ensures no auth backend is even consulted
def get_districts(request, state_id=None):
    """
    Supports:
      - /api/get-districts/?state_id=9
      - /api/states/9/districts/
    """
    # Prefer path param; fallback to query param
    sid = state_id if state_id is not None else request.query_params.get('state_id')

    # Validate
    try:
        sid = int(sid)
    except (TypeError, ValueError):
        return Response(
            {"detail": "state_id is required and must be an integer."},
            status=status.HTTP_400_BAD_REQUEST
        )

    # Ensure state exists (gives 404 if not)
    get_object_or_404(State, id=sid)

    # Fetch districts
    qs = District.objects.filter(state_id=sid).order_by('name')
    data = DistrictSerializer(qs, many=True).data
    return Response({"districts": data})


from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import State
from rest_framework.response import Response
from .models import State, District
from .serializers import StateSerializer, DistrictSerializer


@api_view(['GET'])
@permission_classes([AllowAny])
@authentication_classes([])  # optional; ensures no auth backend is even consulted
def get_states(request):
    qs = State.objects.all().order_by('name')
    # optional: flat list of names for super-simple clients
    if request.query_params.get('only') == 'names':
        return Response({'states': list(qs.values_list('name', flat=True))})
    return Response({'states': StateSerializer(qs, many=True).data})


# https://eastgodavari.ap.gov.in/

from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authtoken.models import Token

from users.models import CustomUser
from accounts.models import LoginHistory  # adjust import if LoginHistory lives elsewhere


def _get_client_ip(request):
    """Best-effort client IP extraction (handles common proxy headers)."""
    xff = request.META.get('HTTP_X_FORWARDED_FOR')
    if xff:
        # X-Forwarded-For: client, proxy1, proxy2 ...
        return xff.split(',')[0].strip()
    return request.META.get('REMOTE_ADDR')


@api_view(['POST'])
@permission_classes([AllowAny])
# @authentication_classes([])  # unauthenticated endpoint
def login_api(request):
    """
    POST /api/login/
    Body: { "mobile_number": "9xxxxxxxxx", "login_pin": "123456" }
    """
    mobile = request.data.get('mobile_number')
    pin = request.data.get('login_pin')

    if not mobile or not pin:
        return Response(
            {'status': 'error', 'message': 'Mobile number and PIN are required'},
            status=status.HTTP_400_BAD_REQUEST
        )

    try:
        user = CustomUser.objects.get(mobile_number=mobile)
    except CustomUser.DoesNotExist:
        return Response(
            {'status': 'error', 'message': 'Invalid mobile number'},
            status=status.HTTP_404_NOT_FOUND
        )

    # Optional: block inactive accounts
    if not user.is_active:
        return Response(
            {'status': 'error', 'message': 'Account is inactive'},
            status=status.HTTP_403_FORBIDDEN
        )

    if user.login_pin != pin:
        return Response(
            {'status': 'error', 'message': 'Incorrect PIN'},
            status=status.HTTP_401_UNAUTHORIZED
        )

    # Issue or retrieve token
    token, _ = Token.objects.get_or_create(user=user)

    # Save login history (mirrors your web view)
    LoginHistory.objects.create(
        user=user,
        ip_address=_get_client_ip(request),
        user_agent=request.META.get('HTTP_USER_AGENT', '')[:512],  # avoid oversized UA strings
    )

    # If transaction_pin is missing, app should send user to Set PINs screen
    need_set_pins = not bool(user.transaction_pin)

    return Response({
        'status': 'success',
        'token': token.key,
        'need_set_pins': need_set_pins,
        'user': {
            'id': user.id,
            'first_name': user.first_name,
            'last_name': user.last_name,
            'name': f"{user.first_name} {user.last_name}".strip(),
            'mobile_number': user.mobile_number,
            'email': user.email,
            'role': user.role,
            'user_type': user.user_type,
            'referral_code': user.referral_code,
            'biometric_enabled': user.biometric_enabled,
            'is_staff': user.is_staff,
            'pincode': user.pincode,
        }
    }, status=status.HTTP_200_OK)

# from rest_framework.authtoken.models import Token
# Token.objects.all().delete()
from typing import List
from django.db import transaction
from django.contrib.auth import get_user_model
from rest_framework.views import APIView
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework import status
from rest_framework.throttling import UserRateThrottle

from rest_framework.authtoken.models import Token


class AdminBurstThrottle(UserRateThrottle):
    # e.g., max 5 calls/min per admin
    rate = "50/min"


class ForceLogoutAPI(APIView):
    """
    POST /api/admin/force-logout/
    Body:
      {
        "scope": "all" | "users",
        "mobile_numbers": ["9000000007", "8096248999"],   # required when scope="users"
        "dry_run": true | false,                          # default false
        "reason": "maintenance"                           # optional, for audit
      }
    """
    # permission_classes = [IsAdminUser]
    throttle_classes = [AdminBurstThrottle]

    def post(self, request):
        scope = (request.data.get("scope") or "all").lower()
        mobiles: List[str] = request.data.get("mobile_numbers") or []
        dry_run = bool(request.data.get("dry_run", False))
        reason = request.data.get("reason") or "force_logout"

        if scope not in ("all", "users"):
            return Response(
                {"status": "error", "message": "Invalid scope. Use 'all' or 'users'."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        User = get_user_model()

        if scope == "users":
            if not mobiles:
                return Response(
                    {"status": "error", "message": "mobile_numbers is required when scope='users'."},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            users_qs = User.objects.filter(mobile_number__in=mobiles)
            token_qs = Token.objects.filter(user__in=users_qs)
        else:
            # scope == "all"
            token_qs = Token.objects.all()

        total_tokens = token_qs.count()

        if dry_run:
            return Response(
                {
                    "status": "ok",
                    "dry_run": True,
                    "scope": scope,
                    "requested_users": mobiles if scope == "users" else None,
                    "tokens_to_delete": total_tokens,
                    "reason": reason,
                },
                status=status.HTTP_200_OK,
            )

        # Apply (delete tokens)
        with transaction.atomic():
            deleted, _ = token_qs.delete()

        # (Optional) add your own audit log here if you have an Audit model
        # AuditLog.objects.create(
        #     actor=request.user,
        #     action="force_logout",
        #     meta={"scope": scope, "mobiles": mobiles, "deleted_tokens": deleted, "reason": reason},
        # )

        return Response(
            {
                "status": "success",
                "scope": scope,
                "requested_users": mobiles if scope == "users" else None,
                "deleted_tokens": deleted,
                "reason": reason,
            },
            status=status.HTTP_200_OK,
        )

from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status

@api_view(['POST'])
@permission_classes([AllowAny])
@authentication_classes([])  # ensure no CSRF/session auth is enforced
def need_set_pins_api(request):
    """
    POST /api/auth/need-set-pins/
    Body: { "mobile_number": "9xxxxxxxxx" }

    Returns:
    {
      "status": "success",
      "need_set_pins": true/false,
      "needs": {
        "login_pin": true/false,
        "transaction_pin": true/false
      }
    }
    """
    mobile = request.data.get('mobile_number')
    if not mobile:
        return Response(
            {'status': 'error', 'message': 'Mobile number is required'},
            status=status.HTTP_400_BAD_REQUEST
        )

    try:
        # Only fetch what we need
        user = CustomUser.objects.only('id', 'login_pin', 'transaction_pin').get(mobile_number=mobile)
    except CustomUser.DoesNotExist:
        return Response(
            {'status': 'error', 'message': 'Invalid mobile number'},
            status=status.HTTP_404_NOT_FOUND
        )

    needs_login_pin = not bool(getattr(user, 'login_pin', None))
    needs_txn_pin = not bool(getattr(user, 'transaction_pin', None))

    return Response({
        'status': 'success',
        'need_set_pins': bool(needs_login_pin or needs_txn_pin),
        'needs': {
            'login_pin': bool(needs_login_pin),
            'transaction_pin': bool(needs_txn_pin),
        }
    }, status=status.HTTP_200_OK)


# 2. Set PIN API – /api/set-pins/
# Only for users who haven't yet set 4-digit login & transaction PINs.
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import api_view, permission_classes

from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.conf import settings
from django.utils import timezone
import re

def _is_4_digits(val):
    return bool(re.fullmatch(r"\d{4}", str(val or "")))

@api_view(['POST'])
@permission_classes([IsAuthenticated])
# @permission_classes([AllowAny])
# @authentication_classes([])
def set_pins_api(request):
    """
    POST /api/set-pins/
    Body: { "login_pin": "1234", "transaction_pin": "5678" }
    """
    user = request.user
    login_pin = request.data.get('login_pin')
    txn_pin = request.data.get('transaction_pin')
    if not (_is_4_digits(login_pin) and _is_4_digits(txn_pin)):
        return Response(
            {"status": "error", "message": "Both PINs must be 4 digits."},
            status=status.HTTP_400_BAD_REQUEST
        )
    first_time = user.pins_set_at is None  # we consider "first time" by this marker
    user.login_pin = login_pin
    user.transaction_pin = txn_pin
    if first_time:
        user.pins_set_at = timezone.now()
    user.save(update_fields=['login_pin', 'transaction_pin', 'pins_set_at'])
    if first_time:
        if user.email:
            ctx = {
                "user": user,
                "login_pin": login_pin,        # If you prefer not to include, just omit
                "transaction_pin": txn_pin,     # Mask if needed
                "when": timezone.localtime(user.pins_set_at),
            }
            html = render_to_string('email/pins_set_success.html', ctx)
            send_mail(
                subject="Viral Pe: PINs set successfully",
                message="",  # leave empty if using HTML
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[user.email],
                html_message=html,
                fail_silently=False,
            )
            user.pins_email_sent_at = timezone.now()
            user.save(update_fields=['pins_email_sent_at'])
    return Response({
        "status": "success",
        "message": "PINs updated successfully",
        "first_time": first_time,
    }, status=status.HTTP_200_OK)


# ✅ 3. Change Login PIN – /api/change-login-pin/
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def change_login_pin(request):
    user = request.user
    old_pin = request.data.get('old_pin')
    new_pin = request.data.get('new_pin')

    if user.login_pin != old_pin:
        return Response({'status': 'error', 'message': 'Old PIN does not match'}, status=403)

    if len(new_pin) != 4:
        return Response({'status': 'error', 'message': 'New PIN must be 4 digits'}, status=400)

    user.login_pin = new_pin
    user.save()

    return Response({'status': 'success', 'message': 'Login PIN changed successfully'})

# ✅ 4. Change Transaction PIN – /api/change-transaction-pin/
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def change_transaction_pin(request):
    user = request.user
    old_pin = request.data.get('old_pin')
    new_pin = request.data.get('new_pin')

    if user.transaction_pin != old_pin:
        return Response({'status': 'error', 'message': 'Old transaction PIN does not match'}, status=403)

    if len(new_pin) != 4:
        return Response({'status': 'error', 'message': 'New transaction PIN must be 4 digits'}, status=400)

    user.transaction_pin = new_pin
    user.save()

    return Response({'status': 'success', 'message': 'Transaction PIN changed successfully'})

# ✅ 5. Reset PIN (Email) – /api/reset-login-pin/
# Optional, use only if user has email.

from django.core.mail import send_mail
from django.conf import settings
import random
# from django.conf import settings
# from django.core.mail import send_mail
# from rest_framework.decorators import api_view, authentication_classes, permission_classes, throttle_classes
# from rest_framework.permissions import AllowAny
# from rest_framework.response import Response
# from rest_framework import status as http_status
# from rest_framework.throttling import AnonRateThrottle
# from .models import CustomUser  # adjust import

# import random

# class ResetPinThrottle(AnonRateThrottle):
#     scope = "reset_pin"  # add DRF setting for this

# @api_view(['POST'])
# @authentication_classes([])              # <-- disables SessionAuthentication (so no CSRF)
# @permission_classes([AllowAny])          # <-- public
# @throttle_classes([ResetPinThrottle])    # <-- basic abuse protection
# def reset_login_pin(request):
#     mobile = (request.data.get('mobile_number') or "").strip()

#     # Always return generic success to avoid user enumeration
#     generic_ok = Response(
#         {
#             'status': 'success',
#             'message': 'If the mobile exists, a new PIN has been emailed.'
#         },
#         status=http_status.HTTP_200_OK
#     )

#     if not mobile:
#         return Response({'status': 'error', 'message': 'mobile_number is required'}, status=http_status.HTTP_400_BAD_REQUEST)

#     try:
#         user = CustomUser.objects.get(mobile_number=mobile)

#         # If no email is set, still return generic OK to avoid enumerating
#         if not user.email:
#             return generic_ok

#         # Generate a fresh 4-digit PIN (short-term fix)
#         pin = f"{random.randint(100000, 999999)}"
#         user.login_pin = pin
#         user.transaction_pin = ""
#         user.save(update_fields=["login_pin","transaction_pin"])

#         try:
#             send_mail(
#                 subject="Your New Login PIN - ViralPe",
#                 message=f"Hi {user.first_name or 'User'}, your new login PIN is: {pin}",
#                 from_email=getattr(settings, "DEFAULT_FROM_EMAIL", None),
#                 recipient_list=[user.email],
#                 fail_silently=False,
#             )


#         except Exception:
#             # Even if email fails, don’t leak details. Log server-side.
#             pass
#         Reset_success_response = Response(
#             {
#                 "status": "success",
#                 "message": f"The PIN for mobile number {user.mobile_number} has been reset. "
#                         f"A new 6-digit PIN has been emailed to {user.email}."
#             },
#             status=http_status.HTTP_200_OK,
#         )


#         return Reset_success_response

#     except CustomUser.DoesNotExist:
#         # Don’t reveal existence
#         return generic_ok


from django.conf import settings
from django.core.mail import send_mail
from rest_framework.decorators import api_view, authentication_classes, permission_classes, throttle_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status as http_status
from rest_framework.throttling import AnonRateThrottle
from .models import CustomUser

import secrets  # crypto-safe random
# from django.contrib.auth.hashers import make_password  # ← enable when you migrate to hashed PINs
import logging
from django.utils import timezone

log = logging.getLogger(__name__)

class ResetPinThrottle(AnonRateThrottle):
    scope = "reset_pin"  # configure in settings.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']

@api_view(["POST"])
@authentication_classes([])             # no SessionAuth => CSRF exempt in DRF
@permission_classes([AllowAny])         # public
@throttle_classes([ResetPinThrottle])   # abuse protection
def reset_login_pin(request):
    # print("78978789789")
    mobile = (request.data.get("mobile_number") or "").strip()

    if not mobile:
        return Response(
            {"status": "error", "message": "mobile_number is required"},
            status=http_status.HTTP_400_BAD_REQUEST,
        )

    try:
        # print(mobile)
        user = CustomUser.objects.get(mobile_number=mobile)
    except CustomUser.DoesNotExist:
        return Response(
            {"status": "error", "message": f"No user found for mobile number {mobile}."},
            status=http_status.HTTP_404_NOT_FOUND,
        )
    except Exception as e:
        log.exception("Unexpected error looking up user for mobile %s", mobile)
        return Response(
            {"status": "error", "message": f"Lookup failed: {type(e).__name__}: {e}"},
            status=http_status.HTTP_500_INTERNAL_SERVER_ERROR,
        )

    if not user.email:
        return Response(
            {
                "status": "error",
                "message": f"User {mobile} does not have an email on file; cannot send the new PIN."
            },
            status=http_status.HTTP_409_CONFLICT,
        )

    # Generate a fresh 6-digit login PIN
    pin = f"{secrets.randbelow(900000) + 100000}"
    # print(pin)
    try:
        # Store login PIN (plain for now); clear transaction PIN as mandated
        user.login_pin = pin
        # user.login_pin = make_password(pin)  # ← use when switching to hashed PINs

        # Clear transaction PIN so the app forces a new one on next login
        user.transaction_pin = None     # if the field is nullable, you could use: None

        user.save(update_fields=["login_pin", "transaction_pin"])
        # print(user)
    except Exception as e:
        log.exception("Failed to update PINs for user %s", user.id)
        return Response(
            {"status": "error", "message": f"Could not update PINs: {type(e).__name__}: {e}"},
            status=http_status.HTTP_500_INTERNAL_SERVER_ERROR,
        )

    from_email = getattr(settings, "DEFAULT_FROM_EMAIL", "no-reply@viralpe.com")
    # print(from_email)
    try:
        context = {
            'user': user,
            'pin': pin,
            'year': timezone.now().year,
        }
        message = render_to_string('email/reset_pin.html', context)
        send_mail(
            subject="Your New Login PIN - ViralPe",
            message="",
            from_email=from_email,
            recipient_list=[user.email],
            html_message=message,  # ✅ important
            fail_silently=False,
        )
        # print("mail sent")
    except Exception as e:
        log.exception("PIN email send failed for user %s", user.id)
        print("PIN email send failed for user %s", user.id)
        return Response(
            {
                "status": "error",
                "message": (
                    f"PIN reset for {user.mobile_number} was saved, but email to {user.email} "
                    f"could not be sent ({type(e).__name__}: {e}). Please retry or contact support."
                ),
            },
            status=http_status.HTTP_502_BAD_GATEWAY,
        )

    # Success (kept as a named variable, per your preference)
    Reset_success_response = Response(
        {
            "status": "success",
            "message": (
                f"The login PIN for {user.mobile_number} has been reset and emailed to {user.email}. "
                f"The transaction PIN was cleared and will need to be set after you log in."
            ),
        },
        status=http_status.HTTP_200_OK,
    )
    return Reset_success_response



# ✅ 6. Profile API – /api/profile/
# View Profile:
def _photo_url(user, request):
    pic = getattr(user, "profile_pic", None)
    if not pic:
        return None
    try:
        url = pic.url  # raises if file missing
    except Exception:
        return None
    return request.build_absolute_uri(url)

@api_view(['GET', 'PATCH'])
@permission_classes([IsAuthenticated])
def profile_view(request):
    user = request.user

    if request.method == 'GET':
        return Response({
            'status': 'success',
            'data': {
                'first_name': user.first_name,
                'last_name': user.last_name,
                'mobile': user.mobile_number,
                'email': user.email,
                'user_type': user.user_type,
                'referral_code': user.referral_code,
                'district': user.district,
                'state': user.state,
                'city': user.city,
                'pincode': user.pincode,
                'role': user.role,
                'date_joined': user.date_joined.isoformat(),  # optional; DRF can serialize datetimes
                'photo_url': _photo_url(user, request),

            }
        })

    elif request.method == 'PATCH':
        data = request.data
        # (validate as needed)
        for k in ('first_name','last_name','email'):
            if k in data:
                setattr(user, k, data[k])
        user.save()
        return Response({'status': 'success', 'message': 'Profile updated successfully'})



# ✅ urls.py Setup
# In accounts/urls.py or api/urls.py:
# from django.urls import path
# from .views import (
#     login_api, set_pins_api, change_login_pin, change_transaction_pin,
#     reset_login_pin, profile_view
# )

# urlpatterns = [
#     path('login/', login_api),
#     path('set-pins/', set_pins_api),
#     path('change-login-pin/', change_login_pin),
#     path('change-transaction-pin/', change_transaction_pin),
#     path('reset-login-pin/', reset_login_pin),
#     path('profile/', profile_view),
# ]


from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def logout_api(request):
    request.user.auth_token.delete()  # Deletes the current token
    return Response({"success": True, "message": "Logged out successfully."})


# =============================

import random
from datetime import timedelta
from django.utils import timezone
from .models import EmailOTP
from django.core.mail import send_mail

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def send_email_otp(request):
    new_email = request.data.get('new_email')
    if not new_email:
        return Response({"error": "Email required"}, status=400)

    otp = str(random.randint(100000, 999999))
    EmailOTP.objects.create(user=request.user, new_email=new_email, otp=otp)

    send_mail(
        subject='ViralPe Email OTP',
        message=f'Your OTP to change email is: {otp}',
        from_email=settings.DEFAULT_FROM_EMAIL,
        recipient_list=[new_email],
        fail_silently=False,
    )

    return Response({"message": "OTP sent to email"})


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def verify_email_otp(request):
    new_email = request.data.get('new_email')
    otp = request.data.get('otp')

    try:
        otp_obj = EmailOTP.objects.get(
            user=request.user,
            new_email=new_email,
            otp=otp,
            created_at__gte=timezone.now() - timedelta(minutes=5)
        )
    except EmailOTP.DoesNotExist:
        return Response({"error": "Invalid or expired OTP"}, status=400)

    request.user.email = new_email
    request.user.save()
    otp_obj.delete()

    return Response({"message": "Email updated successfully"})


from rest_framework.decorators import api_view, parser_classes
from rest_framework.parsers import MultiPartParser, FormParser

@api_view(['POST'])
@permission_classes([IsAuthenticated])
@parser_classes([MultiPartParser, FormParser])
def upload_profile_picture(request):
    file = request.FILES.get('profile_pic')
    if not file:
        return Response({"error": "No file uploaded"}, status=400)

    user = request.user
    user.profile_pic = file
    user.save()

    return Response({"message": "Profile picture updated successfully"})

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rest_framework import status
from django.views.decorators.csrf import csrf_exempt
from users.models import CustomUser

@csrf_exempt
@api_view(['GET'])
def account_status_api(request):
    mobile = request.query_params.get('mobile')
    if not mobile:
        return Response({"detail": "Query param 'mobile' is required"}, status=status.HTTP_400_BAD_REQUEST)

    exists = CustomUser.objects.filter(mobile_number=mobile).exists()
    if not exists:
        return Response({"exists": False, "has_login_pin": False, "biometric_enabled": False})

    user = CustomUser.objects.filter(mobile_number=mobile).only("login_pin", "biometric_enabled").first()
    return Response({
        "exists": True,
        "has_login_pin": bool(user.login_pin),
        "biometric_enabled": bool(getattr(user, "biometric_enabled", False)),
    })

# Make it open to public without global defaults biting you
account_status_api.permission_classes = [AllowAny]
account_status_api.authentication_classes = []


# accounts/api_views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication

class ProfileAPI(APIView):
    """
    GET /api/profile/
    Authorization: Token <key>
    """
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request):
        u = request.user
        # Include only safe fields the app needs
        data = {
            "id": u.id,
            "first_name": u.first_name,
            "last_name": u.last_name,
            "name": f"{u.first_name} {u.last_name}".strip(),
            "email": u.email,
            "mobile_number": getattr(u, "mobile_number", None),
            "role": getattr(u, "role", None),
            "user_type": getattr(u, "user_type", None),
            "referral_code": getattr(u, "referral_code", None),
            "is_active": u.is_active,
            "date_joined": u.date_joined,
        }
        return Response(data)

# accounts/api_views.py (cont.)
from django.db import transaction
from rest_framework import status, permissions
from rest_framework.exceptions import PermissionDenied, ValidationError
from django.contrib.auth import get_user_model

CustomUser = get_user_model()

class IsAdminRole(permissions.BasePermission):
    """
    Allow only users whose role is 'admin'.
    Adjust the string if your project uses a different flag.
    """
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated and getattr(request.user, "role", "") == "admin")

class ManageRolesAPI(APIView):
    """
    GET  /api/manage-roles/               -> list manageable users
    POST /api/manage-roles/               -> bulk update roles
      Body JSON:
      {
        "changes": [
          {"user_id": 123, "role": "agent"},
          {"user_id": 456, "role": "vendor"}
        ]
      }
    """
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated, IsAdminRole]

    # centralize allowed roles to avoid bad data
    ALLOWED_ROLES = {"user", "agent", "vendor", "manager", "admin"}  # adjust to your set

    def get(self, request):
        # mirror your template logic: don't show admins to edit, and don't allow editing self
        qs = CustomUser.objects.exclude(role="admin").exclude(pk=request.user.pk)
        users = [{
            "id": u.id,
            "name": f"{u.first_name} {u.last_name}".strip(),
            "first_name": u.first_name,
            "last_name": u.last_name,
            "email": u.email,
            "mobile_number": getattr(u, "mobile_number", None),
            "role": getattr(u, "role", None),
            "is_active": u.is_active,
        } for u in qs]
        return Response({
            "pagetitle": "🔧 Manage User Roles",
            "breadcrumbs": [
                {"label": "Dashboard", "url": "/dashboard/"},
                {"label": "Manage User Roles"}
            ],
            "users": users,
            "allowed_roles": sorted(self.ALLOWED_ROLES),
        })

    def post(self, request):
        changes = request.data.get("changes")
        if not isinstance(changes, list) or not changes:
            raise ValidationError({"changes": "Provide a non-empty list of {user_id, role} objects."})

        # Validate upfront
        # - cannot change your own role
        # - cannot change admins (unless you want to allow demotion; default: blocked)
        # - role must be in allowed set
        user_ids = []
        desired_roles = {}
        for idx, item in enumerate(changes):
            if not isinstance(item, dict):
                raise ValidationError({f"changes[{idx}]": "Must be an object with user_id and role."})
            uid = item.get("user_id")
            role = item.get("role")
            if uid is None or role is None:
                raise ValidationError({f"changes[{idx}]": "Both user_id and role are required."})
            if role not in self.ALLOWED_ROLES:
                raise ValidationError({f"changes[{idx}].role": f"Role must be one of {sorted(self.ALLOWED_ROLES)}"})
            if uid == request.user.id:
                raise ValidationError({f"changes[{idx}].user_id": "You cannot change your own role."})
            desired_roles[uid] = role
            user_ids.append(uid)

        # Fetch users in one query
        users = list(CustomUser.objects.filter(pk__in=user_ids))
        found_ids = {u.id for u in users}
        missing = [uid for uid in user_ids if uid not in found_ids]
        if missing:
            raise ValidationError({"missing_user_ids": missing})

        # Block edits to existing admins by default
        admins = [u.id for u in users if getattr(u, "role", "") == "admin"]
        if admins:
            raise ValidationError({"admin_user_ids": admins, "detail": "Editing admin roles is blocked."})

        # Apply updates
        updated = []
        with transaction.atomic():
            for u in users:
                new_role = desired_roles[u.id]
                if getattr(u, "role", None) != new_role:
                    u.role = new_role
                    u.save(update_fields=["role"])
                    updated.append({"user_id": u.id, "role": new_role})

        return Response({"updated": updated}, status=status.HTTP_200_OK)


from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.generics import RetrieveUpdateAPIView
from rest_framework.permissions import IsAuthenticated, IsAdminUser

from .models import CustomUser
from users.serializers import CustomUserSerializer


class UserByMobileQueryAPI(APIView):
    """
    GET  /api/users/by-mobile/?mobile=9999999999  -> details
    PATCH /api/users/by-mobile/?mobile=9999999999 -> partial update (admin only)
    """
    # For reading you may want auth, but leaving open like your snippet.
    # For patching, we enforce IsAdminUser.
    def get_permissions(self):
        if self.request.method in ("PATCH", "PUT", "POST", "DELETE"):
            return [IsAuthenticated(), IsAdminUser()]
        return []  # public read; change to [IsAuthenticated()] if needed

    def get(self, request):
        mobile = request.query_params.get("mobile")
        if not mobile:
            return Response({"detail": "Query param 'mobile' is required."},
                            status=status.HTTP_400_BAD_REQUEST)
        user = get_object_or_404(CustomUser, mobile_number=mobile)
        data = CustomUserSerializer(user).data
        return Response(data, status=status.HTTP_200_OK)

    def patch(self, request):
        mobile = request.query_params.get("mobile")
        if not mobile:
            return Response({"detail": "Query param 'mobile' is required."},
                            status=status.HTTP_400_BAD_REQUEST)
        user = get_object_or_404(CustomUser, mobile_number=mobile)
        serializer = CustomUserSerializer(user, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_200_OK)


class UserByMobilePathAPI(RetrieveUpdateAPIView):
    """
    GET   /api/users/<mobile>/
    PATCH /api/users/<mobile>/
    """
    serializer_class = CustomUserSerializer
    lookup_field = "mobile_number"
    lookup_url_kwarg = "mobile"
    queryset = CustomUser.objects.all()

    # Same permission rule: anyone can GET (adjust as needed); only admins can PATCH
    def get_permissions(self):
        if self.request.method in ("PATCH", "PUT", "POST", "DELETE"):
            return [IsAuthenticated(), IsAdminUser()]
        return []  # change to [IsAuthenticated()] if you want protected reads

# users/views.py
from rest_framework.generics import ListAPIView
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from users.models import CustomUser
from users.serializers import CustomUserSerializer
from users.pagination import StandardResultsSetPagination
from users.filters import UserFilter


class UserListAPI(ListAPIView):
    serializer_class = CustomUserSerializer
    queryset = CustomUser.objects.all().order_by("id")
    pagination_class = StandardResultsSetPagination
    permission_classes = [IsAuthenticated, IsAdminUser]
    # permission_classes = [IsAuthenticated, AllowAny]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = UserFilter
    search_fields = ["mobile_number", "first_name", "last_name", "email"]
    ordering_fields = ["id", "first_name", "last_name", "mobile_number", "date_joined", "last_login"]


# users/views.py (add this)
from rest_framework.views import APIView
from django.http import HttpResponse
from openpyxl import Workbook
from datetime import datetime
from users.filters import UserFilter
from django.utils.timezone import localtime

class UserExportExcelAPI(APIView):
    """
    GET /api/users/export.xlsx
      Supports same query params as /api/users/:
        ?search=...&role=...&user_type=...&kyc_status=...&ordering=...
      Optional: ?columns=mobile_number,first_name,last_name,email,role
    """
    permission_classes = [IsAuthenticated, IsAdminUser]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = UserFilter
    search_fields = ["mobile_number", "first_name", "last_name", "email"]
    ordering_fields = ["id", "first_name", "last_name", "mobile_number", "date_joined", "last_login"]

    DEFAULT_COLUMNS = [
        "id",
        "mobile_number",
        "first_name",
        "last_name",
        "email",
        "user_type",
        "role",
        "kyc_status",
        "is_active",
        "date_joined",
        "last_login",
    ]

    # helper to apply the same filters/order used by DRF backends
    def get_filtered_queryset(self, request):
        qs = CustomUser.objects.all()
        # Apply DRF backends manually (since we're not in a GenericAPIView)
        for backend in list(self.filter_backends):
            qs = backend().filter_queryset(request, qs, view=self)
        return qs

    def get(self, request):
        # pick columns
        cols = request.GET.get("columns")
        if cols:
            columns = [c.strip() for c in cols.split(",") if c.strip()]
        else:
            columns = self.DEFAULT_COLUMNS

        # get data (no pagination)
        qs = self.get_filtered_queryset(request)

        # build workbook
        wb = Workbook()
        ws = wb.active
        ws.title = "Users"

        # headers
        ws.append(columns)

        # rows
        def fmt(value):
            if value is None:
                return ""
            if hasattr(value, "isoformat"):  # datetime/date
                try:
                    return localtime(value).strftime("%Y-%m-%d %H:%M:%S")
                except Exception:
                    return str(value)
            return str(value)

        for user in qs.iterator(chunk_size=1000):
            row = []
            for c in columns:
                val = getattr(user, c, "")
                row.append(fmt(val))
            ws.append(row)

        # response
        now = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"users_{now}.xlsx"
        resp = HttpResponse(
            content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        )
        resp["Content-Disposition"] = f'attachment; filename="{filename}"'
        wb.save(resp)
        return resp
    
# accounts/api_views.py
# from rest_framework.decorators import api_view, permission_classes
# from rest_framework.permissions import IsAuthenticated
# from rest_framework.response import Response
# from rest_framework import status
# from accounts.models import User
# from geo.models import Pincode  # or wherever your valid pincodes are stored

# @api_view(["POST"])
# @permission_classes([IsAuthenticated])
# def update_pincode(request):
#     pincode = str(request.data.get("pincode", "")).strip()
#     if not (pincode.isdigit() and len(pincode) == 6):
#         return Response({"error": "Invalid pincode"}, status=status.HTTP_400_BAD_REQUEST)

#     # validate pincode exists in DB (optional)
#     if not Pincode.objects.filter(code=pincode).exists():
#         return Response({"error": "Unknown pincode"}, status=status.HTTP_404_NOT_FOUND)

#     user = request.user
#     user.pincode = pincode
#     user.save(update_fields=["pincode"])

#     return Response({"success": True, "pincode": pincode})
