import logging

from saas.library.python.sane_requests import get_session


class JsonAPI(object):
    def __init__(self, base_url, headers=None):
        self._base_url = base_url
        self._session = self._create_session()
        self._session.headers = headers

    @staticmethod
    def _create_session():
        return get_session()

    def _make_request(
        self,
        method,
        endpoint,
        headers=None,
        data=None,
        params=None,
        json_params=None
    ):
        logging.debug('Requesting %s: (%s, %s, %s, %s)', self._base_url, method, endpoint, params, json_params)

        response = self._session.request(
            method,
            '{base_url}/{endpoint}'.format(base_url=self._base_url, endpoint=endpoint),
            headers=headers,
            data=data,
            params=params,
            json=json_params,
            timeout=30,
        )

        logging.debug('API endpoint %s responded with %s', endpoint, response.content)

        try:
            response_data = response.json()
        except ValueError:
            response_data = {}
            logging.error('Non-json response from endpoint %s: %s', endpoint, response.content)

        if not response.ok or self._is_error_response(response_data):
            raise JsonAPIException(
                status_code=response.status_code,
                error=self._get_error_from_response_data(response_data)
            )

        return response_data

    @staticmethod
    def _is_error_response(response_data):
        return False

    @staticmethod
    def _get_error_from_response_data(response_data):
        return None


class JsonAPIException(Exception):
    DEFAULT_ERROR = 'unknown'

    def __init__(self, status_code, error=None):
        self.status_code = status_code
        self.error = error or self.DEFAULT_ERROR

    def __str__(self):
        return '{cls}: ({status_code}, {error})'.format(
            cls=self.__class__.__name__,
            status_code=self.status_code,
            error=self.error
        )
