import jwt
import json
import datetime
from django.conf import settings
from idpwoa.models import AuthToken,loginTbl
from django.http import HttpResponse
from Idpwoa_Backend import dbsession
from sqlalchemy.exc import SQLAlchemyError
from rest_framework import status, exceptions
from rest_framework.authentication import get_authorization_header, BaseAuthentication


class TokenAuthentication(BaseAuthentication):

    model = None

    def get_model(self):
        return loginTbl

    def authenticate(self, request):
        session = dbsession.Session()
        auth = get_authorization_header(request).split()
        if not auth or auth[0].lower() != b'bearer':
            return None
        if len(auth) == 1:
            msg = 'Invalid token header. No credentials provided.'
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = 'Invalid token header'
            raise exceptions.AuthenticationFailed(msg)
        try:
            token = auth[1]
            if token=="null":
                msg = 'Null token not allowed'
                raise exceptions.AuthenticationFailed(msg)
        except UnicodeError:
            msg = 'Invalid token header. Token string should not contain invalid characters.'
            raise exceptions.AuthenticationFailed(msg)
        session.close()
        return self.authenticate_credentials(token)
    
    def authenticate_credentials(self, token):
        # model = self.get_model()
        session = dbsession.Session()
        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
        except jwt.DecodeError as e:
            print(e)
            session.rollback()
            msg = {'response':'Error','message': "Token mismatch"}
            raise exceptions.AuthenticationFailed(msg)
            # raise exceptions.AuthenticationFailed(msg)
        if payload:
            user_id = payload['user_id']
            expiry = payload['expiry']
        msg = {'response':'Error','message': "Token mismatch",'status' :"401"}
        try:
            try:
                user = session.query(AuthToken).filter(AuthToken.user_id==user_id,AuthToken.key==token).one()
            except SQLAlchemyError as e:
                print("Token Not Exit" ,e)
                session.rollback()
                session.close()
                msg = {'response':'Error','message': "Token mismatch or Token not found",'status' :"401"}
                raise exceptions.AuthenticationFailed(msg)
            try:
                user = session.query(loginTbl).filter_by(userid=user_id).one()
            except SQLAlchemyError as e:
                print("User not Exit",e)
                raise exceptions.AuthenticationFailed({'response':'Error','message':'Token is invalid'})
                session.rollback()
                session.close()
                user = None
            try:
                auth_token = session.query(AuthToken).filter_by(user_id=user_id,key=token).one()
            except SQLAlchemyError as e:
                print(e)
                session.rollback()
                auth_token = None
            stored_token = b'' + (auth_token.key).encode("utf-8")
            if not stored_token == token:
                raise exceptions.AuthenticationFailed(msg)

            if datetime.datetime.strptime(expiry, "%Y-%m-%d").date() < datetime.date.today():
                raise exceptions.AuthenticationFailed({'response':'Error','message':'Token Expired.'})
               
        except jwt.ExpiredSignatureError:
            return HttpResponse({'response': 'Error', 'message': "Token is expired"}, status="403")
        except (jwt.DecodeError, jwt.InvalidTokenError):
            return HttpResponse({'response': 'Error', 'message': "Token is invalid"}, status="403")
            #return HttpResponse({'response':'Error','message': "Token is invalid"}, status="403")
        except SQLAlchemyError as e:
            return HttpResponse({'response':'Error','message': "Internal server error"}, status="500")
        session.close()
        return (user, token)

    def authenticate_header(self, request):
        return 'Bearer'