import json
from http import HTTPStatus
from typing import Union, Dict

from requests import Request, Session, RequestException, HTTPError
from requests.exceptions import SSLError

from .configuration import Configuration
from .errors import IdCloudException, ForbiddenError
from .utils import JSONEncoder
from . import version


class Requestor(object):
    def __init__(self, configuration: Configuration):
        self._user_agent = f"idcloud_python/{version.__version__}"
        self._configuration = configuration
        self._session = Session()

        if self._configuration:
            self._session.cert = self._configuration.mtls

    def request(
        self, method: str, endpoint_url: str, payload: dict = None, query: dict = None, parse: bool = True
    ) -> Union[str, Dict]:
        req = Request(
            method=method,
            url=self._configuration.environment.api_url + endpoint_url,
            headers={
                "User-Agent": self._user_agent,
                "X-Api-Key": self._configuration.api_key,
                "Authorization": f"Bearer {self._configuration.jwt}"
            },
        )

        if payload and isinstance(payload, dict):
            req.data = json.dumps(payload, cls=JSONEncoder)
            req.headers['Content-Type'] = 'application/json'

        if query and isinstance(query, dict):
            req.params = query

        prepared = req.prepare()

        try:
            r = self._session.send(
                prepared,
                timeout=self._configuration.timeout
            )
        except SSLError as e:
            raise IdCloudException(request=prepared, message='SSL Error', previous=e)
        except RequestException as e:
            raise IdCloudException(request=prepared, message='Request to server timed out.', previous=e)

        try:
            r.raise_for_status()
        except HTTPError as e:
            if e.response.status_code == HTTPStatus.FORBIDDEN:
                raise ForbiddenError(request=prepared, message=e.args[0], previous=e)
            raise IdCloudException(request=prepared, message=e.args[0], previous=e)

        data = r.text

        if parse:
            try:
                data = json.loads(data)
            except json.JSONDecodeError:
                raise IdCloudException(request=prepared, message='Cannot parse response data to json.')

        return data

    def __str__(self) -> str:
        return self._user_agent
