# =============================================================================
# codePost v2.0 SDK
#
# API REQUESTOR SUB-MODULE
# =============================================================================

from __future__ import print_function # Python 2

# Python stdlib imports
import copy as _copy
import functools as _functools
import inspect as _inspect
import json as _json
import logging as _logging
import os as _os
import platform as _platform
import threading as _threading
import time as _time
import uuid as _uuid
import sys as _sys
try:
    # Python 3
    from urllib.parse import urljoin
    from urllib.parse import quote as urlquote
    from urllib.parse import urlencode as urlencode
except ImportError: # pragma: no cover
    # Python 2
    from urlparse import urljoin
    from urllib import quote as urlquote
    from urllib import urlencode as urlencode

# External dependencies
# import better_exceptions as _better_exceptions
import requests as _requests

# Local imports
import codepost

from . import __version__ as _CODEPOST_SDK_VERSION
from . import http_client as _http_client
from . import errors as _errors

from .util import config as _config
from .util import custom_logging as _logging

from .util.misc import _make_f

# =============================================================================

# Replacement f"..." compatible with Python 2 and 3
_f = _make_f(globals=lambda: globals(), locals=lambda: locals())

# =============================================================================

# Global submodule constants
_LOG_SCOPE = "{}".format(__name__)

_API_VERSION = "1"

_PY_VERSION = "{v.major}.{v.minor}.{v.micro}-{v.releaselevel}".format(
    v=_sys.version_info)

_UA = _f(
    "codePost API/v{_API_VERSION} "
    "SDK/v{_CODEPOST_SDK_VERSION} "
    "Python/v{_PY_VERSION}")

_APP_INFO_PATTERN = "{name}"

# Global submodule protected attributes
_logger = _logging.get_logger(name=_LOG_SCOPE)

# =============================================================================

class APIRequestor(object):

    def __init__(
        self,
        api_key=None,
        base_url=_config.BASE_URL,
        client=None,
        **kwargs
    ):
        self._api_key = api_key
        self._base_url = base_url
        self._client = client

        if self._api_key:
            _config.validate_api_key(api_key=self._api_key, log_outcome=True)

        if not isinstance(self._client, _http_client.HTTPClient):
            # Reinitialize a default HTTPClient
            self._client = _http_client.HTTPClient(**kwargs)

    @property
    def api_key(self):
        # Specifically configured local API key
        if self._api_key:
            return self._api_key

        # Call to get the global cached API key
        global_api_key = _config.configure_api_key()
        return global_api_key

    @api_key.setter
    def api_key(self, value):
        self._api_key = value
        _config.validate_api_key(api_key=self._api_key, log_outcome=True)

    @api_key.deleter
    def api_key(self):
        self._api_key = None

    @classmethod
    def _format_app_info(cls, **kwargs):
        s = ""
        d = kwargs if kwargs and len(kwargs) > 0 else codepost.app_info
        if isinstance(d, dict):
            if "name" in d:
                s += d.get("name")
                if "version" in d:
                    s += "/v{version}".format(**d)
                if "url" in d:
                    s += " ({url})".format(**d)
        return s

    @classmethod
    def _build_headers(cls, api_key=None, method="get", **kwargs):

        # The SDK's user agent info
        user_agent = _UA

        # (Optionally) the client's user agent info
        app_str = ""
        if codepost.app_info:
            app_str = cls._format_app_info(**codepost.app_info)
            user_agent += " " + app_str

        # Diagnostic information
        diag = {
            "sdk": _CODEPOST_SDK_VERSION,
            "lang": "python",
            "publisher": "codepost",
            "lang_version": _PY_VERSION,
            "platform": _platform.platform(),
            "uname": _platform.uname(),
            "app": app_str,
        }

        headers = {
            "Authorization": "Token {}".format(api_key),
            "User-Agent": user_agent,
            "X-codePost-SDK-User-Agent": _json.dumps(diag)
        }

        if method.upper() in ["POST", "PATCH"] and not "Content-Type" in headers:
            headers["Content-Type"] = "application/json"

        if method.upper() == "POST":
            headers.setdefault("Idempotency-Key", str(_uuid.uuid4()))

        return headers

    @classmethod
    def _handle_request_error(cls, response):
        _errors.handle_api_error(
            status_code=response.status_code,
            response=response)

    def _request(self, endpoint, method="get", **kwargs):
        kws = {}

        if kwargs and isinstance(kwargs, dict) and len(kwargs) > 0:
            kws.update(_copy.deepcopy(kwargs))

        kws["method"] = method

        # Configure API key

        api_key = kws.get("api_key", self.api_key)
        if "api_key" in kws:
            del kws["api_key"]

        # Compile headers

        default_headers = self._build_headers(
            api_key=api_key,
            **kws
        )
        kws["headers"] = kws.get("headers", dict())
        kws["headers"].update(default_headers)

        # Provide POSTed data as a JSON string

        if "application/json" in kws["headers"].get("Content-Type", ""):
            if "data" in kws and not isinstance(type(kws["data"]), str):
                kws["data"] = _json.dumps(kws["data"])
        
        ret = self._client.request(
            url=urljoin(self._base_url, endpoint),
            **kws
        )

        # Handle error codes here
        if not ret.status_code == 200:
            self._handle_request_error(response=ret)

        return ret

# =============================================================================

STATIC_REQUESTOR = APIRequestor()

# =============================================================================

