from datetime import timedelta
import random

from django.conf import settings
from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status, permissions
from rest_framework import serializers
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
from apps.core.utils import error_response

from apps.accounts.services import auth_service
from apps.accounts.models import Occupation, User
from apps.geo.models import Pincode
from apps.platform.accounts.throttles import LoginRateThrottle
from apps.platform.settings.models import PlatformFeatureFlag


class WebLoginRequestSerializer(serializers.Serializer):
    mobile_number = serializers.CharField(required=True, allow_blank=False)
    login_pin = serializers.CharField(required=True, allow_blank=False)


class WebProfileUpdateSerializer(serializers.Serializer):
    first_name = serializers.CharField(required=False, allow_blank=True, max_length=150)
    last_name = serializers.CharField(required=False, allow_blank=True, max_length=150)
    date_of_birth = serializers.DateField(required=False, allow_null=True)
    location = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=255)
    address_line1 = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=255)
    address_line2 = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=255)
    city = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=120)
    state = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=120)
    country = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=120)
    occupation = serializers.UUIDField(required=False, allow_null=True)
    pincode = serializers.UUIDField(required=False, allow_null=True)
    profile_image = serializers.ImageField(required=False, allow_null=True)
    remove_profile_image = serializers.BooleanField(required=False, default=False)


class LoginView(APIView):
    throttle_classes = [LoginRateThrottle]
    def post(self, request):
        if not PlatformFeatureFlag.is_on(PlatformFeatureFlag.LOGIN_ENABLED):
            return error_response(
                message='Logins are disabled right now. Please try again later.',
                code='LOGIN_DISABLED',
                status_code=status.HTTP_403_FORBIDDEN,
            )

        payload = dict(request.data)
        # Backward compatibility for older clients still sending mobile/pin keys.
        if 'mobile_number' not in payload and 'mobile' in payload:
            payload['mobile_number'] = payload.get('mobile')
        if 'login_pin' not in payload and 'pin' in payload:
            payload['login_pin'] = payload.get('pin')

        serializer = WebLoginRequestSerializer(data=payload)
        if not serializer.is_valid():
            return error_response(
                message='Validation failed',
                code='VALIDATION_ERROR',
                fields=serializer.errors,
                status_code=status.HTTP_400_BAD_REQUEST,
            )

        data = serializer.validated_data
        result = auth_service.login_user(data)
        if not result.get('ok'):
            return error_response(
                message=result.get('error', 'Login failed'),
                code="LOGIN_FAILED",
                status_code=status.HTTP_400_BAD_REQUEST
            )
        # Return tokens at top level for frontend
        user_data = result.get('data', {})
        serialized_user = user_data.get('user')
        try:
            from apps.accounts.models import User
            from apps.accounts.serializers import UserSerializer

            login_user_payload = user_data.get('user') or {}
            login_user_id = login_user_payload.get('id')
            if login_user_id:
                db_user = User.objects.filter(id=login_user_id).first()
                if db_user:
                    serialized_user = UserSerializer(db_user, context={'request': request}).data
        except Exception:
            # Fall back to original auth service payload if serialization fails.
            serialized_user = user_data.get('user')

        return Response({
            "success": True,
            "access_token": user_data.get('access_token'),
            "refresh_token": user_data.get('refresh_token'),
            "role": user_data.get('role'),
            "must_change_login_pin": user_data.get('must_change_login_pin', False),
            "user": serialized_user
        }, status=status.HTTP_200_OK)


class RegisterView(APIView):
    def post(self, request):
        if not PlatformFeatureFlag.is_on(PlatformFeatureFlag.REGISTRATION_ENABLED):
            return error_response(
                message='Registrations are disabled right now. Please wait for admin to enable them.',
                code='REGISTRATION_DISABLED',
                status_code=status.HTTP_403_FORBIDDEN,
            )

        import logging
        import os
        from datetime import timedelta
        from django.utils.safestring import mark_safe
        logger = logging.getLogger(__name__)
        logger.info("Starting registration flow (web API)")
        from apps.accounts.serializers import RegisterSerializer
        from apps.accounts.models import EmailVerificationToken, User
        from django.db import IntegrityError
        import secrets
        from django.utils import timezone
        from django.conf import settings

        serializer = RegisterSerializer(data=request.data)
        logger.info("Serializer initialized")
        serializer.is_valid(raise_exception=True)
        logger.info("Serializer is valid")
        try:
            user = serializer.save(is_active=False)
            logger.info(f"User created: {user.email}")
        except IntegrityError as ie:
            msg = str(ie)
            logger.warning(f"IntegrityError during register: {msg}")
            fields = {}
            low = msg.lower()
            if 'accounts_user.mobile' in low or 'mobile_number' in low or 'mobile' in low:
                fields['mobile_number'] = ["A user with this mobile number already exists."]
            if 'accounts_user.email' in low or 'email' in low:
                fields['email'] = ["A user with this email already exists."]
            if not fields:
                fields = {'non_field_errors': ["User could not be created due to a database constraint."]}
            return error_response(
                message='Validation failed',
                code='VALIDATION_ERROR',
                fields=fields,
                status_code=status.HTTP_400_BAD_REQUEST,
            )

        # Generate verification token and store
        token = secrets.token_urlsafe(32)
        expiry = timezone.now() + timedelta(minutes=settings.EMAIL_OTP_EXPIRY_MINUTES)
        logger.info(f"Creating EmailVerificationToken for {user.email}")
        EmailVerificationToken.objects.filter(email=user.email).delete()
        EmailVerificationToken.objects.create(email=user.email, token=token, expires_at=expiry)
        logger.info(f"Token created for {user.email}")

        # Build verification link
        frontend_url = os.getenv('FRONTEND_URL', 'http://localhost:5173')
        verify_link = f"{frontend_url}/verify-email?token={token}&email={user.email}"
        logger.info(f"Verification link: {verify_link}")

        # Send verification link via notification service
        try:
            from apps.notifications.services import send_notification
            logger.info(f"Attempting to send verification email to {user.email}")
            context = {
                'user': user,
                'verify_link': mark_safe(verify_link),
                'expiry_minutes': settings.EMAIL_OTP_EXPIRY_MINUTES,
            }
            result = send_notification(
                channel='email',
                recipient=user.email,
                template_code='email_verification',
                context=context,
                user=user,
            )
            logger.info(f"Notification send result: {result}")
        except Exception as e:
            logger.error(f"Failed to send notification: {e}")

        return Response({"success": True, "data": {"id": str(user.id), "email": user.email}}, status=status.HTTP_201_CREATED)


class ProfileView(APIView):
    """Get the current authenticated user's profile."""
    permission_classes = [permissions.IsAuthenticated]
    parser_classes = [JSONParser, MultiPartParser, FormParser]
    
    def get(self, request):
        user = request.user
        from apps.accounts.serializers import UserSerializer
        serializer = UserSerializer(user, context={'request': request})
        return Response({"success": True, "data": serializer.data}, status=status.HTTP_200_OK)

    def patch(self, request):
        user = request.user
        from apps.accounts.serializers import UserSerializer

        serializer = WebProfileUpdateSerializer(data=request.data, partial=True)
        if not serializer.is_valid():
            return error_response(
                message='Validation failed',
                code='VALIDATION_ERROR',
                fields=serializer.errors,
                status_code=status.HTTP_400_BAD_REQUEST,
            )

        data = serializer.validated_data
        update_fields = []

        for field in (
            'first_name',
            'last_name',
            'date_of_birth',
            'location',
            'address_line1',
            'address_line2',
            'city',
            'state',
            'country',
        ):
            if field in data:
                setattr(user, field, data[field])
                update_fields.append(field)

        if 'occupation' in data:
            user.occupation_id = str(data['occupation']) if data['occupation'] else None
            update_fields.append('occupation')

        if 'pincode' in data:
            user.pincode_id = str(data['pincode']) if data['pincode'] else None
            update_fields.append('pincode')

        if data.get('remove_profile_image'):
            if user.profile_image:
                user.profile_image.delete(save=False)
            user.profile_image = None
            update_fields.append('profile_image')

        if 'profile_image' in data and data.get('profile_image') is not None:
            user.profile_image = data.get('profile_image')
            update_fields.append('profile_image')

        if update_fields:
            user.save(update_fields=list(dict.fromkeys(update_fields)))

        return Response({"success": True, "data": UserSerializer(user, context={'request': request}).data}, status=status.HTTP_200_OK)

    def put(self, request):
        return self.patch(request)


class OccupationListView(APIView):
    """List active occupations for profile and onboarding forms."""
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        rows = Occupation.objects.filter(is_active=True).order_by('name').values('id', 'name')
        return Response({"success": True, "data": list(rows)}, status=status.HTTP_200_OK)


class PincodeLookupView(APIView):
    """Lookup active pincodes by code prefix for profile and onboarding forms."""
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        query = (request.query_params.get('pincode') or '').strip()
        if not query:
            return Response({"success": True, "data": []}, status=status.HTTP_200_OK)

        rows = (
            Pincode.objects
            .filter(is_active=True, code__istartswith=query)
            .order_by('code', 'location')[:20]
        )

        data = [
            {
                'id': str(pin.id),
                'code': pin.code,
                'location': pin.location,
                'label': f"{pin.code}{f' - {pin.location}' if pin.location else ''}",
            }
            for pin in rows
        ]

        return Response({"success": True, "data": data}, status=status.HTTP_200_OK)


class RequestPinOTPSerializer(serializers.Serializer):
    mobile_number = serializers.CharField(required=True, allow_blank=False)


class VerifyPinOTPSerializer(serializers.Serializer):
    token = serializers.CharField(required=True, allow_blank=False)


class ResetPinSerializer(serializers.Serializer):
    token = serializers.CharField(required=True, allow_blank=False)
    new_pin = serializers.CharField(required=True, allow_blank=False)


class RequestPinOTPView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = RequestPinOTPSerializer(data=request.data)
        if not serializer.is_valid():
            return error_response(
                message='Validation failed',
                code='VALIDATION_ERROR',
                fields=serializer.errors,
                status_code=status.HTTP_400_BAD_REQUEST,
            )

        mobile_number = serializer.validated_data['mobile_number'].strip()
        user = User.objects.filter(mobile=mobile_number).first()
        if not user:
            return Response({'detail': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

        otp = f"{random.randint(100000, 999999)}"
        user.email_otp = otp
        user.otp_expiry = timezone.now() + timedelta(minutes=10)
        user.save(update_fields=['email_otp', 'otp_expiry'])

        if user.email:
            try:
                from apps.notifications.services import send_notification

                send_notification(
                    channel='email',
                    recipient=user.email,
                    template_code='login_pin_reset_otp',
                    context={
                        'user': user,
                        'otp': otp,
                        'expiry_minutes': 10,
                    },
                    user=user,
                )
            except Exception:
                # Email delivery is best-effort in local/dev environments.
                pass

        payload = {
            'success': True,
            'first_name': user.first_name,
            'email': user.email,
            'message': 'OTP sent successfully',
        }
        if settings.DEBUG:
            payload['debug_otp'] = otp
        return Response(payload, status=status.HTTP_200_OK)


class VerifyPinOTPView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = VerifyPinOTPSerializer(data=request.data)
        if not serializer.is_valid():
            return error_response(
                message='Validation failed',
                code='VALIDATION_ERROR',
                fields=serializer.errors,
                status_code=status.HTTP_400_BAD_REQUEST,
            )

        token = serializer.validated_data['token'].strip()
        user = User.objects.filter(email_otp=token, otp_expiry__gte=timezone.now()).first()
        if not user:
            return Response({'detail': 'Invalid or expired OTP'}, status=status.HTTP_400_BAD_REQUEST)

        return Response({'success': True, 'message': 'OTP verified'}, status=status.HTTP_200_OK)


class ResetPinView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = ResetPinSerializer(data=request.data)
        if not serializer.is_valid():
            return error_response(
                message='Validation failed',
                code='VALIDATION_ERROR',
                fields=serializer.errors,
                status_code=status.HTTP_400_BAD_REQUEST,
            )

        token = serializer.validated_data['token'].strip()
        new_pin = serializer.validated_data['new_pin'].strip()

        if not new_pin.isdigit() or len(new_pin) != 6:
            return Response({'detail': 'PIN must be exactly 6 digits.'}, status=status.HTTP_400_BAD_REQUEST)

        user = User.objects.filter(email_otp=token, otp_expiry__gte=timezone.now()).first()
        if not user:
            return Response({'detail': 'Invalid or expired token'}, status=status.HTTP_400_BAD_REQUEST)

        user.set_login_pin(new_pin)
        user.email_otp = None
        user.otp_expiry = None
        user.save(update_fields=['login_pin', 'email_otp', 'otp_expiry'])

        from rest_framework_simplejwt.tokens import RefreshToken
        from apps.accounts.serializers import UserSerializer

        refresh = RefreshToken.for_user(user)

        return Response(
            {
                'success': True,
                'access_token': str(refresh.access_token),
                'refresh_token': str(refresh),
                'role': user.role,
                'user': UserSerializer(user, context={'request': request}).data,
            },
            status=status.HTTP_200_OK,
        )


class SetLoginPinView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        new_pin = str(request.data.get('new_pin', '')).strip()
        confirm_pin = str(request.data.get('confirm_pin', new_pin)).strip()

        if not new_pin or not confirm_pin:
            return Response({'detail': 'New PIN and confirmation are required.'}, status=status.HTTP_400_BAD_REQUEST)

        if new_pin != confirm_pin:
            return Response({'detail': 'PIN confirmation does not match.'}, status=status.HTTP_400_BAD_REQUEST)

        if not new_pin.isdigit() or len(new_pin) != 6:
            return Response({'detail': 'PIN must be exactly 6 digits.'}, status=status.HTTP_400_BAD_REQUEST)

        user = request.user
        user.set_login_pin(new_pin)
        user.must_change_login_pin = False
        user.save(update_fields=['login_pin', 'must_change_login_pin', 'updated_at'])

        from apps.accounts.serializers import UserSerializer

        return Response(
            {
                'success': True,
                'user': UserSerializer(user, context={'request': request}).data,
            },
            status=status.HTTP_200_OK,
        )


class TestErrorView(APIView):
    """A simple endpoint to raise an exception for testing Sentry integration.

    This endpoint should only be exposed in DEBUG mode (wired from urls.py).
    """
    def get(self, request):
        raise RuntimeError('Sentry test error')
