import base64
import datetime
import json

from rest_framework import authentication
from rest_framework.exceptions import NotAuthenticated

from fwd_api.annotation.api import throw_on_failure
from fwd_api.utils import date as DateUtil
from fwd_api.utils.crypto import ctel_cryptor, sds_authenticator, image_authenticator
from fwd_api.exception.exceptions import InvalidException, TokenExpiredException, PermissionDeniedException


@throw_on_failure(TokenExpiredException())
def authenticate_by_param_token(request):
    token = request.query_params.get('token')
    data = image_authenticator.decode_data(token)
    if 'expired_at' not in data or 'internal_id' not in data:
        raise PermissionDeniedException()

    token_end_date = DateUtil.to_date(data['expired_at'], DateUtil.FORMAT.DD_MM_YYYY_HHMMSS.value)
    if token_end_date < DateUtil.get_date_time_now():
        raise TokenExpiredException()
    request.user_data = data


# @throw_on_failure(TokenExpiredException())
def authenticate_by_authorization_header(request):
    token = request.headers['Authorization']
    print(f"[INFO]: recived request at {datetime.datetime.utcnow()}, with path {request.path}")
    # print(f"[DEBUG]: token: {token}")
    data = sds_authenticator.decode_data(token)
    if 'expired_at' not in data or 'status' not in data or 'subscription_id' not in data:
        raise PermissionDeniedException()
    if data['status'] != 1:
        raise PermissionDeniedException()

    token_end_date = DateUtil.to_date(data['expired_at'], DateUtil.FORMAT.DD_MM_YYYY_HHMMSS.value)
    if token_end_date < DateUtil.get_date_time_now():
        raise TokenExpiredException()
    request.user_data = data


@throw_on_failure(TokenExpiredException())
def authenticate_by_ctel_token(request):
    raw_data = base64.b64decode(request.headers['user'])
    json_data = json.loads(raw_data)
    if 'content' not in json_data or 'iv' not in json_data:
        raise InvalidException(excArgs='data')
    en_text = json_data['content']
    iv = json_data['iv']
    user_data = json.loads(ctel_cryptor.decrypt_ctel(en_text, iv))
    if 'userId' not in user_data or 'expiredAt' not in user_data:
        raise InvalidException(excArgs='data')
    date_str = user_data['expiredAt']

    expired_at = DateUtil.to_date(date_str, DateUtil.FORMAT.DD_MM_YYYY_HHMMSS.value)
    if expired_at < DateUtil.get_date_time_now():
        raise TokenExpiredException()

    request.user_data = user_data


class AuthFilter(authentication.BaseAuthentication):
    white_list_path = [
        '/api/schema/swagger-ui', '/api/schema/swagger-ui/', '/api/schema/',
        '/api/ctel/users/gen-token/', # Todo : Debug API
        '/api/ctel/image/process/app/', '/api/ctel/login/', '/api/ctel/login/'
    ]

    @throw_on_failure(TokenExpiredException())
    def authenticate(self, request):

        if request.path in self.white_list_path:
            print("App API")
            return None, None
        if '/api/ctel/media/' in request.path or '/api/ctel/v2/media/' in request.path:
            return authenticate_by_param_token(request)

        if 'Authorization' in request.headers:
            return authenticate_by_authorization_header(request)
        # Todo Check Ip only CMC
        # Todo request add expried date

        if 'user' in request.headers and request.path == '/api/ctel/users/':
            return authenticate_by_ctel_token(request)
        raise NotAuthenticated()
