import os
import sys
from abc import abstractmethod
from urllib.parse import urlparse

import aiohttp
import httpx
import jwt
import requests
from aiohttp import ClientResponse
from cryptography.fernet import Fernet

from scripts.constants.secrets import Secrets
# from scripts.logging import logger
from scripts.utils.security.apply_encrytion_util import create_token

FERNET_KEY = os.environ.get("FERNET_KEY",
                            default=b'VE68aW4wNAkpz37bJtEA6P4gXKixpIF_FckqTmwrEUU=')

SECRET_KEY = os.environ.get("SECRET_KEY",
                            default=b'gAAAAABhxEqUbE5SkJEfFCC6VtV4BQhIdHcUs6Y0dhFCTOCxZtz7rDwBeapR-Q3ecZIhkwq7sgAfxzq0mEY50WnjEjE9g3Tc8OHkhq3hj2j_1EGcosDfM2Y=')

if not FERNET_KEY:
    print("Error, environment variable FERNET_KEY is not set")
    sys.exit(1)

if not SECRET_KEY:
    print("Error, environment variable SECRET_KEY is not set")
    sys.exit(1)


class AuthenticationError(Exception): ...


class ForbiddenError(Exception): ...


token = ""
secrets = {}


class BaseRequestHandler:
    """
    BaseRequestHandler
    """

    def __init__(self, url, time_out=None) -> None:
        self.time_out = time_out
        self.url = url
        self.verify = False

    @property
    def get_timeout(self):
        return self.time_out

    def post(self, path="", json=None, data=None, update_args=True, **kwargs) -> requests.Response:
        """doc"""
        url = self.url
        if path:
            url = self.get_url(path)
        print(url)
        response = requests.post(url=url, data=data, json=json, **self.prepare_args(**kwargs))

        if response.status_code == 401:
            raise AuthenticationError("Not authorized")

        if response.status_code == 403:
            raise ForbiddenError("Not Permitted")

        if update_args:
            self.update_args(response=response)
        return response

    def get(self, path="", params=None, **kwargs) -> requests.Response:
        """doc"""
        url = self.url
        if path:
            url = self.get_url(path)
        print(url)
        # Commenting this as currently there is no use case for this
        # self.update_args(response = response)
        response = requests.get(url=url, params=params, **self.prepare_args(**kwargs))
        if response.status_code == 401:
            raise AuthenticationError("Not authorized")

        if response.status_code == 403:
            raise ForbiddenError("Not Permitted")
        return response

    def get_url(self, path):
        return f"{self.url.rstrip('/')}/{path.lstrip('/').rstrip('/')}"

    def verify_request(self):
        if self.url_scheme(self.url) == 'https':
            self.verify = True
        return self.verify

    @staticmethod
    def url_scheme(url):
        return urlparse(url).scheme

    @abstractmethod
    def prepare_args(self, **kwargs):
        ...

    @abstractmethod
    def update_args(self, **kwargs):
        ...


class HTTPXRequestHandler:
    def __init__(self, url, time_out=None) -> None:
        self.time_out = time_out
        self.url = url
        self.verify = False

    @property
    def get_timeout(self):
        return self.time_out

    def httpx_post(self, path="", json=None, data=None, update_args=True, **kwargs) -> requests.Response:
        """doc"""
        url = self.url
        if path:
            url = self.get_url(path)
        print(url)
        with httpx.Client() as client:
            response = client.post(url=url, data=data, json=json, **self.prepare_args(**kwargs))

        if response.status_code == 401:
            raise AuthenticationError("Not authorized")

        if response.status_code == 403:
            raise ForbiddenError("Not Permitted")

        if update_args:
            self.update_args(response=response)
        return response

    def httpx_get(self, path="", params=None, **kwargs) -> requests.Response:
        """doc"""
        url = self.url
        if path:
            url = self.get_url(path)
        print(url)
        # Commenting this as currently there is no use case for this
        # self.update_args(response = response)
        with httpx.Client() as client:
            response = client.get(url=url, params=params, **self.prepare_args(**kwargs))

        if response.status_code == 401:
            raise AuthenticationError("Not authorized")

        if response.status_code == 403:
            raise ForbiddenError("Not Permitted")
        return response

    def get_url(self, path):
        return f"{self.url.rstrip('/')}/{path.lstrip('/').rstrip('/')}"

    def verify_request(self):
        if self.url_scheme(self.url) == 'https':
            self.verify = True
        return self.verify

    @staticmethod
    def url_scheme(url):
        return urlparse(url).scheme

    @abstractmethod
    def prepare_args(self, **kwargs):
        ...

    @abstractmethod
    def update_args(self, **kwargs):
        ...


class AIOHTTPRequestHandler:
    def __init__(self, url, time_out=None) -> None:
        self.time_out = time_out
        self.url = url
        self.verify = False

    @property
    def get_timeout(self):
        return self.time_out

    async def aiohttp_post(self, path="", json=None, data=None, update_args=True, **kwargs) -> ClientResponse:
        """doc"""
        url = self.url
        if path:
            url = self.get_url(path)
        print(url)
        async with aiohttp.ClientSession() as client:
            async with client.post(
                    url=url, data=data, json=json, **self.prepare_args(**kwargs)
            ) as resp:
                response = resp

        if response.status == 401:
            raise AuthenticationError("Not authorized")

        if response.status == 403:
            raise ForbiddenError("Not Permitted")

        if update_args:
            self.update_args(response=response)
        return response

    async def aiohttp_get(self, path="", params=None, **kwargs) -> ClientResponse:
        """doc"""
        url = self.url
        if path:
            url = self.get_url(path)
        print(url)
        # Commenting this as currently there is no use case for this
        # self.update_args(response = response)
        async with aiohttp.ClientSession() as client:
            async with client.get(
                    url=url, params=params, **self.prepare_args(**kwargs)
            ) as resp:
                response = resp

        if response.status == 401:
            raise AuthenticationError("Not authorized")

        if response.status == 403:
            raise ForbiddenError("Not Permitted")
        return response

    def get_url(self, path):
        return f"{self.url.rstrip('/')}/{path.lstrip('/').rstrip('/')}"

    def verify_request(self):
        if self.url_scheme(self.url) == 'https':
            self.verify = True
        return self.verify

    @staticmethod
    def url_scheme(url):
        return urlparse(url).scheme

    @abstractmethod
    def prepare_args(self, **kwargs):
        ...

    @abstractmethod
    def update_args(self, **kwargs):
        ...


class ILensRequest(BaseRequestHandler, HTTPXRequestHandler, AIOHTTPRequestHandler):
    """
    Utility to use ilens API's Directly
    """

    def __init__(self, url, project_id) -> None:
        super().__init__(url)
        self.project_id = project_id

    def prepare_args(self, **kwargs) -> dict:
        """
        doc
        """
        post_args = {}
        post_args.update(**kwargs)
        post_args.update(headers=self.get_headers(**kwargs))
        post_args.update(timeout=self.get_timeout)
        post_args.update(verify=self.verify_request())
        post_args.update(cookies=self.get_cookies())
        return post_args

    def update_args(self, **kwargs) -> bool:
        data = kwargs.get('response').headers.get('token')
        if data:
            global token
            token = data
        return data

    def get_headers(self, **kwargs) -> dict:
        headers = {'X-Content-Type-Options': 'nosniff',
                   'X-Frame-Options': 'SAMEORIGIN', 'Cache-Control': 'no-store', 'projectId': self.project_id}
        headers.update(kwargs.get("headers", {}))
        return headers

    @staticmethod
    def get_cookies(**kwargs) -> dict:
        cookies = {}
        cookies.update(kwargs.get("cookies", {}))
        return cookies

    def get_jwt_token(self):
        key = FERNET_KEY
        crypto_key = Fernet(key)
        decoded_key = crypto_key.decrypt(SECRET_KEY).decode("utf-8")
        return jwt.encode(self.details, decoded_key.split('_$#')[0], algorithm=decoded_key.split('_$#')[-1])

    @staticmethod
    def create_token(host: str = '127.0.0.1', user_id=None, internal_token=Secrets.token):
        """
        This method is to create a cookie
        """

        try:
            if user_id is None:
                user_id = "user_099"
            new_token = create_token(
                user_id=user_id,
                ip=host,
                token=internal_token,
            )
            return new_token
        except Exception as e:
            # logger.exception(str(e))
            raise
