"""Exposes an interface for external clients"""
import logging
import threading

import requests
# proxy to requests library methods
from requests import adapters  # noqa
from requests import auth  # noqa
from requests import exceptions  # noqa
from requests.exceptions import (
    ConnectionError,
    ConnectTimeout,
    FileModeWarning,
    HTTPError,
    ReadTimeout,
    RequestException,
    Timeout,
    TooManyRedirects,
    URLRequired,
)
from requests.status_codes import codes

#  -----
import gtransport._config as _config
import gtransport._transport_client as transport

logging.getLogger(__name__).addHandler(logging.NullHandler())

_DEFAULT_PROVIDER = requests

_LOCAL = threading.local()

# The global config
_CONFIG = None


def configure(headers=None, strict=False, **cfg):
    """
    Configures the gtransport library
    @param headers: Dict[str, Any], headers to be forwarded with each
    request originating from gtransport
    @param strict: bool, flag indicating the strictness of validations
    @param cfg: optional other configuration params
    """
    global _CONFIG
    provider = cfg.get('provider') or _DEFAULT_PROVIDER
    if not provider:
        raise RuntimeError('No transport provider found')

    _CONFIG = _config.Configuration(
        provider=provider, strict=bool(strict), headers=headers, **cfg
    )


def _is_configured():
    """
    Tests whether gtransport has been configured or not.
    """
    return _CONFIG is not None


def _get_transport_client():
    """
    Creates and returns a new transport client for the current thread.
    If the client was already created for this thread, it simply
    returns the same old client.

    @return: TransportClient
    @rtype: transport.TransportClient
    """

    # Check if the library is properly configured, if not
    # configure it right about now
    if not _is_configured():
        configure()

    if not hasattr(_LOCAL, 'transport_client'):
        _LOCAL.transport_client = transport.TransportClient(
            provider=_CONFIG.provider, extra_headers=_CONFIG.headers
        )
    return _LOCAL.transport_client


def bind_transport_client(**kwargs):
    """
    Creates a new transport client for the current thread.
    It also bounds the client with a set of default headers.
    @return: TransportClient, configured instance of transport client
    @rtype: transport.TransportClient
    """
    client = _get_transport_client()
    assert isinstance(client, transport.TransportClient)

    # Fetch any extra headers to be bounded with the client
    # and associate them
    extra_headers = kwargs.get('headers') or {}
    extra_headers.update(_CONFIG.headers)
    client.update_extra_headers(retain=False, **extra_headers)
    return client


def get(url, params=None, **kwargs):
    """Convenience method to make an HTTP GET call"""
    kwargs.setdefault('allow_redirects', True)
    return _get_transport_client().send('get', url, params=params, **kwargs)


def put(url, data=None, **kwargs):
    """Convenience method to make an HTTP PUT call"""
    return _get_transport_client().send('put', url, data=data, **kwargs)


def post(url, data=None, json=None, **kwargs):
    """Convenience method to make an HTTP POST call"""
    return _get_transport_client().send('post', url, data=data, json=json, **kwargs)


def patch(url, data=None, **kwargs):
    """Convenience method to make an HTTP PATCH call"""
    return _get_transport_client().send('patch', url, data=data, **kwargs)


def delete(url, **kwargs):
    """Convenience method to make an HTTP DELETE call"""
    return _get_transport_client().send('delete', url, **kwargs)


def request(method, url, data=None, json=None, **kwargs):
    """
    A generic function to make an HTTP call
    @param method: str, the http verb (GET, HEAD, POST, PUT, etc)
    @param url: str, the URL string
    @param data: Any, the request body
    @param json: Dict[Hashable, Any], the request body as dict,
    in case the content/type was application/json
    @param kwargs: other parameters like headers, timeouts, etc. Please
    check the documentation of requests library
    """
    return _get_transport_client().send(method, url, data=data, json=json, **kwargs)


def session():
    """
    A proxy function for replicating functionality of
     requests.session() method
    """
    # importing locally to manage circular import
    from gtransport._session import GTransportSession

    return GTransportSession()


Session = session
