from rest_framework.views import APIView
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK, HTTP_500_INTERNAL_SERVER_ERROR, HTTP_400_BAD_REQUEST, HTTP_409_CONFLICT
from django.contrib.auth import get_user_model
from ..services import send_message
from ..settings import auth_init_check
from ..serializers import MessageSerializer, OTPCodeSerializer
from django.utils.timezone import now, timedelta
from django.db import transaction
UserGet = get_user_model()



class MessageSend(APIView):
    permission_classes = (AllowAny, )
    serializer_class = MessageSerializer

    def post(self, request):
        """
        prams:
            mobile_number:      str (len: 11)  (exp: 09211892425)
            message_text:       str (len: 290)
            template_id:        int (required for PARSIAN_WEBCO_IR)

        response:
            HTTP_400_BAD_REQUEST            {'error': [params requirements and validations]}
            HTTP_500_INTERNAL_SERVER_ERROR  {'error': 'contact the support..'}
            HTTP_200_OK                     {'details': 'The Message was sent correctly.'}
            HTTP_502_BAD_GATEWAY            {'details': 'The SMS service provider was unable to process the request.'}
            HTTP_401_UNAUTHORIZED           {'details': 'Authentication is not accepted...'}
        """
        try:
            serializer = self.serializer_class(data=request.data)

            # 1. Validate data using serializer
            if not serializer.is_valid():
                return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

            # 2. Extract validated data and create the message object
            mobile_number = serializer.validated_data.get('mobile_number')
            message_text = serializer.validated_data.get('message_text')
            
            # Use serializer.save() to create the object instance initially
            obj = serializer.save() # -1 as a temporary status

            response = send_message(mobile_number, message_text, request.data)

            response_data = response[1].get('data')
            obj_status = response[1].get('obj_status')
            response_status_code = response[1].get('status')

            # Save the updated object once at the end
            obj.status = obj_status
            obj.save()
            
            # Return the response with updated data and status
            return Response(response_data, status=response_status_code)

        except Exception as e:
            # Catch all unexpected errors and return a generic 500
            print(f"An unexpected error occurred: {e}")
            return Response({'error': 'An error occurred, please contact support.'}, status=HTTP_500_INTERNAL_SERVER_ERROR)


class OTPCodeSend(APIView):
    permission_classes = (AllowAny,)
    # Use a separate serializer for the request data validation
    serializer_class = OTPCodeSerializer 
    model = OTPCodeSerializer.Meta.model

    def post(self, request):
        """
        Sends an OTP code to the provided mobile number.
        """
        # 1. Validate the request data using a serializer.
        serializer = self.serializer_class(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

        mobile_number = serializer.validated_data['mobile_number']
        
        try:
            # 2. Get authentication settings.
            icheck = auth_init_check()
            if not (icheck and icheck.get('AUTH_METHOD') == 'OTP'):
                return Response({'error': 'Authentication method is not configured correctly.'}, 
                                status=HTTP_500_INTERNAL_SERVER_ERROR)
            
            otp_exp_time = icheck['OPTIONS']['exp_time']
            template_id = icheck['OPTIONS'].get('default_sms_template')
            
            # 3. Use an atomic transaction to prevent race conditions.
            with transaction.atomic():
                # Attempt to get an existing OTP code and lock the row for the duration of the transaction.
                obj = self.model.objects.filter(mobile_number=mobile_number).first()

                if obj:
                    # An OTP code already exists. Check if it's expired.
                    expiration_time = obj.created_at + timedelta(minutes=otp_exp_time)
                    if now() < expiration_time:
                        # The existing code is still valid. Tell the user to wait.
                        remaining_seconds = (expiration_time - now()).total_seconds()
                        return Response({
                            'details': f'An OTP code has already been sent. Please wait {int(remaining_seconds)} seconds before trying again.'
                        }, status=HTTP_409_CONFLICT)
                    else:
                        # The code has expired. Delete it.
                        obj.delete()

            # 4. Create a new OTP code instance.
            new_otp_obj = self.model.objects.create(mobile_number=mobile_number)

            # 5. Send the message using a dedicated service function.
            # Assuming 'send_message' returns a tuple: (success_bool, response_dict)
            success, sms_response = send_message(
                mobile_number=new_otp_obj.mobile_number, 
                message_text=new_otp_obj.code, 
                data=request.data,
                template_id=template_id
            )
            
            if success:
                # If the SMS was sent successfully, return success response.
                return Response({'details': 'The OTP code was sent correctly.'}, status=HTTP_200_OK)
            else:
                # If the SMS sending failed, delete the newly created OTP object
                # and return the error from the service.
                new_otp_obj.delete()
                return Response(sms_response, status=sms_response.get('status', HTTP_500_INTERNAL_SERVER_ERROR))

        except Exception as e:
            # Catch and log unexpected errors for debugging.
            new_otp_obj.delete()
            print(f"An unexpected error occurred: {e}")
            return Response({'error': 'An internal server error occurred.'},
                            status=HTTP_500_INTERNAL_SERVER_ERROR)
