from rest_framework import status
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
from django.template.loader import render_to_string

import json
import base64
import random
from django.core.mail import send_mail, EmailMultiAlternatives
from django.core.exceptions import ValidationError
from django.contrib.auth import authenticate, login

import pytz
from datetime import datetime, timedelta
import jwt
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

from django.core.files.base import ContentFile

from .userBaseService import UserBaseService
from api.models import User, UserSession
from api.serializers.user import (UserLoginDetailSerializer,
                                  UserSignupSerializer,
                                  UserPasswordSerializer,
                                  UserSerializer,
                                  UserDetailsSerailizer)

from api.utils.messages.userMessages import *
from api.utils.messages.commonMessages import *
from itrack import settings

class UserService(UserBaseService):
    """
    Allow any user (authenticated or not) to access this url 
    """

    def __init__(self):
        pass

    def login(self, request, format=None):

        validated_data = self.validate_auth_data(request)

        username = request.data['email']
        username = username.lower()
        password = request.data['password']

        try:
            user = authenticate(username=username, password=password)
        except:
            user = None

        if user is not None:
            
            login(request, user)

            serializer = UserLoginDetailSerializer(user)

            payload = jwt_payload_handler(user)
            token = jwt.encode(payload, settings.SECRET_KEY)

            user_details = serializer.data
            user_details['token'] = token
            # User.objects.filter(pk=user.pk).update(auth_token=token)

            user_session = self.create_update_user_session(user, token, request)

            return ({"data": user_details,"code": status.HTTP_200_OK,"message": "LOGIN_SUCCESSFULLY"})

        return ({"data": None,"code": status.HTTP_400_BAD_REQUEST, "message": "INVALID_CREDENTIALS"})

    def validate_auth_data(self, request):
        error = {}
        if not request.data.get('email'):
            error.update({'email' : "FIELD_REQUIRED" })

        if not request.data.get('password'):
            error.update({'password' : "FIELD_REQUIRED" })

        if request.headers.get('device-type')=='android'or request.headers.get('device-type')=='ios':
            if not request.data.get('device_id'):
                error.update({'device_id': "FIELD_REQUIRED"})

        if error:
            raise ValidationError(error)
    
    def create_update_user_session(self, user, token, request):
        """
        Create User Session
        """
        print(request.headers.get('device-type'))
        print(request.data.get('device_id'))

        user_session = self.get_user_session_object(user.pk, request.headers.get('device-type'), request.data.get('device_id'))

        if user_session is None:
            UserSession.objects.create(
                user = user,
                token = token,
                device_id = request.data.get('device_id'),
                device_type = request.headers.get('device-type'),
                app_version = request.headers.get('app-version')
            )

        else:
            user_session.token = token
            user_session.app_version = request.headers.get('app-version')
            user_session.save()

        return user_session

    
    def get_user_session_object(self, user_id, device_type, device_id=None):
        try:
            if device_id:
                try:
                    return UserSession.objects.get(user=user_id, device_type=device_type, device_id=device_id)
                except UserSession.DoesNotExist:
                    return None

            return UserSession.objects.get(user=user_id, device_type=device_type, device_id=device_id)

        except UserSession.DoesNotExist:
            return None


    def sign_up(self, request, format=None):
        self.validate_signup_data(request.data)
        serializer = UserSignupSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return ({"data":serializer.data, "code":status.HTTP_201_CREATED, "message":"User Created Successfully"})
        #if not valid
        return ({"data":None, "code":status.HTTP_400_BAD_REQUEST, "message":"Oops! Something went wrong."})

    def validate_signup_data(self, data):
        # if data.get('dob'):
        date_string = data.get('dob')
        date_format = '%m/%d/%Y'
        try:
            date_obj = datetime.strptime(date_string, date_format)
            print(date_obj)
        except ValueError:
            raise Exception ({
                'dob': "Incorrect data format, should be MM/DD/YYYY"
            })

    def logout(self, request, format=None):

        validated_data = self.validate_logout_data(request)
        try:
            jwt_token_str = request.META['HTTP_AUTHORIZATION']
            jwt_token = jwt_token_str.replace('Bearer', '')
            user_detail = jwt.decode(jwt_token, None, None)
            user = User.objects.get(pk=user_detail['user_id'])

            user_session_instance = self.get_user_session_object(user.pk, request.headers.get('device-type'), request.data.get('device_id'))

            if user_session_instance:
                user_session = self.create_update_user_session(user, None, request)
                return ({"data": None, "code": status.HTTP_200_OK, "message": "LOGOUT_SUCCESSFULLY"})
            else:
                return ({"data":None, "code":status.HTTP_400_BAD_REQUEST, "message":"RECORD_NOT_FOUND"})

        except User.DoesNotExist:
            return ({"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": "RECORD_NOT_FOUND"})
    

    def generate_otp(self, request, email, format=None):
        try:
            tz = pytz.timezone ('Asia/Kolkata')
            current_time = datetime.now (tz)

            user = self.get_object_by_email (email)
            print("user: ", user)
            otp = random.randint (100000, 999999)
            user.otp = otp
            user.otp_send_time = current_time
            user.save ()

            body_msg = 'Your OTP is {} to reset your password. OTP is valid for 1 hour or 1 successfull attempt.'.format (
                otp)
            msg = EmailMultiAlternatives('iTrack : Password reset OTP', body_msg, settings.DEFAULT_FROM_EMAIL, [email])
            msg.send()
            # send_mail ('iTrack : Password reset OTP', body_msg, settings.DEFAULT_FROM_EMAIL, [email])

            return ({"data": None, "code": status.HTTP_200_OK, "message": OTP_SENT})
        except Exception as e:
            print(e)
            return ({"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": USER_NOT_EXIST})

    def verify_otp(self, request, format=None):
        self.validate_otp_data(request.data)
        tz = pytz.timezone ('Asia/Kolkata')
        current_time = datetime.now (tz)
        now_date = current_time.strftime ('%m/%d/%y')
        now_time = current_time.strftime ('%H:%M')

        user_email = request.data['email']
        otp = request.data['otp']

        user = User.objects.filter (email=user_email.lower(), otp=otp).first ()
        if user:
            otp_send_time = user.otp_send_time
            otp_send_time = otp_send_time.astimezone (tz) + timedelta (hours=1)

            otp_date = datetime.strftime (otp_send_time, '%m/%d/%y')
            otp_time = datetime.strftime (otp_send_time, '%H:%M')

            if now_date == otp_date and now_time <= otp_time:
                return {"data": None, "code": status.HTTP_200_OK, "message": OTP_VERIFID}
            else:
                return {"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": OTP_EXPIRED}
        else:
            return {"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": DETAILS_INCORRECT}

    def reset_password(self, request, format=None):
        self.validate_reset_password_data (request.data)

        try:
            user = self.get_object_by_email (request.data['email'])
        except Exception:
            return {"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": EMAIL_NOT_EXIST}

        serializer = UserPasswordSerializer (user, data=request.data)
        if serializer.is_valid ():
            serializer.save ()
            return {"data": None, "code": status.HTTP_200_OK, "message": PASSWORD_RESET_SUCCESSFULLY}
        else:
            return {"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": PASSWORD_RESET_FAILED}
    
    def get_object_by_email(self, email):
        # print(email)
        try:
            return User.objects.get(email=email.lower(), is_active=1)
        except User.DoesNotExist:
            raise Exception ({
                'email': EMAIL_NOT_EXIST
            })
    
    def validate_otp_data(self, data):
        error = {}
        if not data.get ('email'):
            error.update ({'email': FIELD_REQUIRED})

        if not data.get ('otp'):
            error.update ({'otp': FIELD_REQUIRED})

        if error:
            raise ValidationError (error)
    
    def validate_reset_password_data(self, data):
        error = {}
        if not data.get ('email'):
            error.update ({'email': FIELD_REQUIRED})

        if not data.get ('password'):
            error.update ({'password': FIELD_REQUIRED})

        if error:
            raise ValidationError (error)

    def create(self, request, format=None):
        s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?"
        passlen = 8
        password =  "".join(random.sample(s,passlen ))

        request.POST._mutable = True
        request.data['image'] = self.decode_base64_image (request.data.get ('image'))
        request.data['password'] = password
        serializer = UserSerializer (data=request.data)

        if serializer.is_valid ():
            serializer.save ()
            # -------------------------------------------------

            context = {
                'name': '{} {}'.format (serializer.data['first_name'], serializer.data['last_name']),
                'email':serializer.data['email'],
                'password':password
            }

            body_msg = render_to_string ('api/email/welcome-user-by-admin.html', context)

            subject, from_email = 'Welcome in ITrack', settings.DEFAULT_FROM_EMAIL

            msg = EmailMultiAlternatives (subject, body_msg, from_email, [request.data['email']])
            msg.content_subtype = "html"
            msg.send ()
            # -------------------------------------------------

            return (
                {"data": serializer.data, "code": status.HTTP_200_OK, "message": USER_CREATED})

        # if not valid
        return ({"data": serializer.errors, "code": status.HTTP_400_BAD_REQUEST, "message": BAD_REQUEST})
    
    def decode_base64_image(self, data):
        tz = pytz.timezone ('Asia/Kolkata')
        current_date = datetime.now (tz)
        formated_date = current_date.strftime ("%Y-%m-%d")
        formated_time = current_date.strftime ("%H:%M")
        format, imgstr = data.split (';base64,')
        ext = format.split ('/')[-1]

        data = ContentFile (base64.b64decode (imgstr), name='temp_' + formated_date + "_" + formated_time + "." + ext)
        return data
    

    def update_user(self, request, pk, format=None):
        user = self.get_object (pk)
        if user:
            try:
                user_email = User.objects.get(email=request.data.get ('email'), role = request.data['role'])
            except User.DoesNotExist:
                user_email = None

            if user_email is not None and user_email.pk != user.pk:
                return ({"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": EMAIL_ALREADY_EXIST})

            request.POST._mutable = True
            if 'image' in request.data and request.data.get ('image') is not None and (
                    request.data.get ('image')).startswith ('data:image'):
                request.data['image'] = self.decode_base64_image (request.data.get ('image'))
            else:
                request.data['image'] = None

            serializer = UserSerializer (user, data=request.data)
            if serializer.is_valid ():
                serializer.save ()
                return ({"data": serializer.data, "code": status.HTTP_200_OK, "message": USER_UPDATED})

            # if not valids
            return ({"data": serializer.errors, "code": status.HTTP_400_BAD_REQUEST, "message": BAD_REQUEST})
        else:
            return ({"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": RECORD_NOT_FOUND})
    
    def get_object(self, pk):
        try:
            return User.objects.get (pk=pk, is_deleted=False)
        except User.DoesNotExist:
            return None
    
    def get_user_by_id(self, request, pk, format=None):
        user = self.get_object(pk)
        if user:
            serializer = UserDetailsSerailizer(user)
            return({"data":serializer.data, "code":status.HTTP_200_OK, "message":OK})
        else:
            return({"data": None, "code": status.HTTP_400_BAD_REQUEST, "message": RECORD_NOT_FOUND})