#!/usr/bin/env python

import logging

import requests
from requests.auth import HTTPBasicAuth
from requests.exceptions import HTTPError

try:
    from urllib.parse import urlencode
except ImportError:
    from urllib import urlencode

_LOGGER = logging.getLogger(__name__)

class OAuth2Client(object):
    """Wrapper class for OAuth2 requests"""

    def __init__(self, url, authorization_url, access_token_url, redirect_url,
                 client_id, client_secret):
        self.url = url
        self.authorization_url = authorization_url
        self.access_token_url = access_token_url
        self.redirect_url = redirect_url
        self.client_id = client_id
        self.client_secret = client_secret

    def get_auth_headers(self, access_token):
        """Get authorization headers for user level api resources"""

        return {
            "Authorization": "Bearer {}".format(access_token),
            "Content-Type": "application/json",
            "Accept": "application/json"
        }

    def get_authorization_url(self, response_type="code", state=None):
        """Build authorization url for the client"""

        params = {
            "client_id": self.client_id,
            "response_type": response_type,
        }

        if state:
            params["state"] = state

        if self.redirect_url:
            params["redirect_uri"] = self.redirect_url

        return "{url}?{params}".format(url=self.authorization_url,
                                       params=urlencode(params))

    def get_access_token(self, authorization_code):
        """Exchange authorization code for an access token"""

        headers = {
            "Content-Type" : "application/x-www-form-urlencoded",
            "Accept" : "application/json;charset=UTF-8"
        }

        data = {
            "grant_type" : "authorization_code",
            "code" : authorization_code
        }

        if self.redirect_url:
            data["redirect_uri"] = self.redirect_url

        _LOGGER.debug('Fetching access token from auth code')

        return self.post(endpoint=None,
                         url=self.access_token_url,
                         data=data,
                         headers=headers)

    def __build_endpoint_kwargs(self, **kwargs):
        """Create endpoint url for requests

        If `endpoint` argument is given, it is appended to the api url
        and used as the request url. Otherwise `url` argument is used.
        """

        if "endpoint" in kwargs:
            if kwargs["endpoint"] is not None:
                kwargs["url"] = self.url + kwargs["endpoint"]
            del kwargs["endpoint"]

        return kwargs

    def __build_auth_kwargs(self, **kwargs):
        """Setup authentication for requests

        If `access_token` is given, it is used in Authentication header.
        Otherwise basic auth is used with the client credentials.
        """

        if "access_token" in kwargs:
            headers = self.get_auth_headers(kwargs["access_token"])

            if "headers" in kwargs:
                headers.update(kwargs["headers"])

            kwargs["headers"] = headers
            del kwargs["access_token"]
        elif "auth" not in kwargs:
            kwargs["auth"] = HTTPBasicAuth(self.client_id, self.client_secret)

        return kwargs

    def __build_request_kwargs(self, **kwargs):
        kwargs = self.__build_endpoint_kwargs(**kwargs)
        kwargs = self.__build_auth_kwargs(**kwargs)
        return kwargs

    def __parse_response(self, response):
        if response.status_code >= 400:
            message = "{code} {reason}: {body}".format(code=response.status_code,
                                                       reason=response.reason,
                                                       body=response.text)
            raise HTTPError(message, response=response)

        if response.status_code is 204:
            return {}

        try:
            return response.json()
        except ValueError:
            return response.text

    def __request(self, method, **kwargs):
        kwargs = self.__build_request_kwargs(**kwargs)

        _LOGGER.debug("%s request to URL: %s", method.upper(), kwargs["url"])

        response = requests.request(method, **kwargs)
        return self.__parse_response(response)

    def get(self, endpoint, **kwargs):
        return self.__request("get", endpoint=endpoint, **kwargs)

    def post(self, endpoint, **kwargs):
        return self.__request("post", endpoint=endpoint, **kwargs)

    def put(self, endpoint, **kwargs):
        return self.__request("put", endpoint=endpoint, **kwargs)

    def delete(self, endpoint, **kwargs):
        return self.__request("delete", endpoint=endpoint, **kwargs)
