import logging
from urllib.parse import urljoin

import requests

from .exceptions import SendyError

log = logging.getLogger(__name__)


def convert_to_utf8(value):
    if isinstance(value, bytes):
        value = value.decode("utf-8")
    return value


def bind_api(**config):
    class APIMethod:
        path = config["path"]
        allowed_param = config.get("allowed_param", [])
        method = config.get("method", "GET")
        success_status_code = config.get("success_status_code", 200)
        success_message = config.get("success_message", None)
        extra_param = config.get("extra_param", None)
        fail_silently = config.get("fail_silently", True)

        def __init__(self, api, args, kwargs):
            self.api = api
            self.headers = kwargs.pop("headers", {})
            self.post_data = kwargs.pop("post_data", None)
            self.build_parameters(args, kwargs)
            self.path = self.path.format(**self.parameters)  # Sub any URL vars
            self.host = api.host

            if not isinstance(self.success_message, (list, tuple)):
                self.success_message = [self.success_message]

        def build_parameters(self, args, kwargs):
            self.parameters = {}
            for idx, arg in enumerate(args):
                if arg is None:
                    continue

                try:
                    self.parameters[self.allowed_param[idx]] = convert_to_utf8(arg)
                except IndexError:
                    raise ValueError("Too many parameters supplied!")

            for k, arg in kwargs.items():
                if arg is None:
                    continue
                if k in self.parameters:
                    raise ValueError(
                        "Multiple values for parameter {0} supplied!".format(k)
                    )

                self.parameters[k] = convert_to_utf8(arg)

            if self.extra_param is not None:
                self.parameters.update(self.extra_param)

            self.parameters["api_key"] = self.api.api_key

        def execute(self):
            # Build the request URL
            url = urljoin(self.host, self.path)

            if self.api.debug:
                log.info(
                    "* Sending {0}: {1}\nHeaders: {2}\nData:{3}".format(
                        self.method, url, self.headers, self.post_data
                    )
                )

            response = requests.request(
                self.method, url, headers=self.headers, data=self.parameters
            )
            data = response.content
            if callable(self.success_message):
                try:
                    return self.success_message(data)
                except ValueError:
                    if not self.fail_silently:
                        raise SendyError(data)

            success = data in self.success_message
            if not success and not self.fail_silently:
                raise SendyError(data)
            return convert_to_utf8(data)

    def _call(api, *args, **kwargs):
        method = APIMethod(api, args, kwargs)
        return method.execute()

    return _call
