"""
    LanXin+ OpenAPI

    LanXin+ OpenAPI Platform  # noqa: E501

    Generated by: https://openapi.lanxin.cn
"""


import copy
import logging
import multiprocessing
import sys
import urllib3

from http import client as http_client
from lanxinplus_openapi.exceptions import ApiValueError, ApiException
from time import time


JSON_SCHEMA_VALIDATION_KEYWORDS = {
    'multipleOf', 'maximum', 'exclusiveMaximum',
    'minimum', 'exclusiveMinimum', 'maxLength',
    'minLength', 'pattern', 'maxItems', 'minItems'
}

class Configuration(object):
    """NOTE: This class is auto generated by LanXin+

    Ref: https://openapi.lanxin.cn
    Do not edit the class manually.

    :param host: 网关地址
    :param app_id: 应用ID, 创建应用时取得
    :param app_secret: 应用对应的AppSecret, 创建应用时取得
    :param hook_token: 通过机器人访问api所使用的token, 创建机器人时获得
    :param app_token, user_token: 应用访问api对应的token
    :param user_code: 一次性使用的人员免登录授权码, 使用oauth认证获得
    :Example:
    >>> config = lanxinplus_openapi.Configuration(host="https://open.lanxin.cn/open/apigw", \
    >>>             app_id="APP_ID", app_secret="APP_SECRET")
    >>> lanxinplus_openapi.Configuration.set_default(config) # 设置默认配置
    >>> default_config = lanxinplus_openapi.Configuration.get_default_copy() # 获取默认配置
    >>> config.set_app_token("app_token", 7200) # 获取 app_token, user_token
    >>> config.set_user_token("user_token", 7200)
    >>> app_token = config.get_app_token()
    >>> user_token = config.get_user_token()
    >>> # 设置 refresh_app_token_hook token 过期刷新函数
    >>> config.refresh_app_token_hook = func()

    :param discard_unknown_keys: 是否接受未在model定义内的 json 键值
    :param disabled_client_side_validations (string):
    """

    _default = None

    def __init__(self, host:str,
                 app_id:str, app_secret:str,
                 app_token="", user_token="",
                 user_code="", hook_token="",
                 refresh_app_token_hook=None,
                 refresh_user_token_hook=None,
                 discard_unknown_keys=True,
                 disabled_client_side_validations="",
                 verify_ssl=True,
                 server_index=None, server_variables=None,
                 server_operation_index=None, server_operation_variables=None,
                 ):
        """Constructor
        """
        self._base_path = "https://open.lanxin.cn/open/apigw" if host == "" else host
        # Authentication Settings
        self.app_id = app_id
        self.app_secret = app_secret
        self.hook_token = hook_token

        self.app_token = app_token
        self.user_token = user_token

        # expired time for tokens
        self.app_token_expireTime = ""
        self.user_token_expireTime = ""

        # use api to create app _token and user_token
        """function hook to refresh API key if expired
        """
        self.refresh_app_token_hook = refresh_app_token_hook
        self.refresh_user_token_hook = refresh_user_token_hook
        self.user_code = user_code
        """Temp file folder for downloading files
        """
        self.temp_folder_path = None
        self.server_index = 0 if server_index is None and host is None else server_index
        self.server_operation_index = server_operation_index or {}
        """Default server index
        """
        self.server_variables = server_variables or {}
        self.server_operation_variables = server_operation_variables or {}
        """Default server variables
        """
        self.discard_unknown_keys = discard_unknown_keys
        self.disabled_client_side_validations = disabled_client_side_validations

        self.logger = {}
        """Logging Settings
        """
        self.logger["package_logger"] = logging.getLogger("lanxinplus_openapi")
        self.logger["urllib3_logger"] = logging.getLogger("urllib3")
        self.logger_format = '%(asctime)s %(levelname)s %(message)s'
        """Log format
        """
        self.logger_stream_handler = None
        """Log stream handler
        """
        self.logger_file_handler = None
        """Log file handler
        """
        self.logger_file = None
        """Debug file location
        """
        self.debug = False
        """Debug switch
        """

        self.verify_ssl = verify_ssl
        """SSL/TLS verification
        Set this to false to skip verifying SSL certificate when calling API
        from https server.
        """
        self.ssl_ca_cert = None
        """Set this to customize the certificate file to verify the peer.
        """
        self.cert_file = None
        """client certificate file
        """
        self.key_file = None
        """client key file
        """
        self.assert_hostname = None
        """Set this to True/False to enable/disable SSL hostname verification.
        """

        self.connection_pool_maxsize = multiprocessing.cpu_count() * 5
        """urllib3 connection pool's maximum number of connections saved
           per pool. urllib3 uses 1 connection as default value, but this is
           not the best value when you are making a lot of possibly parallel
           requests to the same host, which is often the case here.
           cpu_count * 5 is used as default value to increase performance.
        """

        self.proxy = None
        """Proxy URL
        """
        self.proxy_headers = None
        """Proxy headers
        """
        self.safe_chars_for_path_param = ''
        """Safe chars for path_param
        """
        self.retries = None
        """Adding retries to override urllib3 default value 3
        """
        # Enable client side validation
        self.client_side_validation = True

        # Options to pass down to the underlying urllib3 socket
        self.socket_options = None

    def get_app_token(self):
        '''
            get appToken from config.refresh_app_token_hook
        '''
        try:
            if self.app_token != "" and self.app_token_expireTime \
                != "" and self.app_token_expireTime > time():
                return self.app_token
            elif self.refresh_app_token_hook is not None:
                app_token, expireTime = self.refresh_app_token_hook(self._base_path, self.app_id, self.app_token)
            else:
                raise ApiException(reason="config getAppToken refresh_app_token_hook is None")
            self.app_token = app_token
            self.app_token_expireTime = expireTime
            return app_token
        except ApiException as e:
            print('config getAppToken err: %s' % e)
            raise e

    def set_app_token(self, app_token:str, expireIn:float):
        '''
        set app_token
        '''
        try:
            self.app_token = app_token
            self.app_token_expireTime = expireIn + time()
        except ApiException as e:
            print('apiClient setAppToken err: %s' % e)
            raise e

    def get_user_token(self):
        '''
            get userToken from config.refresh_user_token_hook
        '''
        try:
            if self.user_token != "" and self.user_token_expireTime \
                != "" and self.user_token_expireTime > time():
                return self.user_token
            elif self.refresh_user_token_hook is not None:
                user_token, expireTime = self.configuration.refresh_user_token_hook(self.configuration)
            else:
                raise ApiException(reason="config getUserToken refresh_user_token_hook is None")
            self.user_token = user_token
            self.user_token_expireTime = expireTime
            return user_token
        except ApiException as e:
            print('apiClient getUserToken err: %s' % e)
            raise e

    def set_user_token(self, user_token:str, expireIn:float):
        '''
            set user_token
        '''
        try:
            self.user_token = user_token
            self.user_token_expireTime = expireIn + time()
        except ApiException as e:
            print('apiClient setUserToken err: %s' % e)
            raise e

    def __deepcopy__(self, memo):
        cls = self.__class__
        result = cls.__new__(cls)
        memo[id(self)] = result
        for k, v in self.__dict__.items():
            if k not in ('logger', 'logger_file_handler'):
                setattr(result, k, copy.deepcopy(v, memo))
        # shallow copy of loggers
        result.logger = copy.copy(self.logger)
        # use setters to configure loggers
        result.logger_file = self.logger_file
        result.debug = self.debug
        return result

    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)
        if name == 'disabled_client_side_validations':
            s = set(filter(None, value.split(',')))
            for v in s:
                if v not in JSON_SCHEMA_VALIDATION_KEYWORDS:
                    raise ApiValueError(
                        "Invalid keyword: '{0}''".format(v))
            self._disabled_client_side_validations = s

    @classmethod
    def set_default(cls, default):
        """Set default instance of configuration.

        It stores default configuration, which can be
        returned by get_default_copy method.

        :param default: object of Configuration
        """
        cls._default = copy.deepcopy(default)

    @classmethod
    def get_default_copy(cls):
        """Return new instance of configuration.

        This method returns newly created, based on default constructor,
        object of Configuration class or returns a copy of default
        configuration passed by the set_default method.

        :return: The configuration object.
        """
        if cls._default is not None:
            return copy.deepcopy(cls._default)
        return Configuration()

    @property
    def logger_file(self):
        """The logger file.

        If the logger_file is None, then add stream handler and remove file
        handler. Otherwise, add file handler and remove stream handler.

        :param value: The logger_file path.
        :type: str
        """
        return self.__logger_file

    @logger_file.setter
    def logger_file(self, value):
        """The logger file.

        If the logger_file is None, then add stream handler and remove file
        handler. Otherwise, add file handler and remove stream handler.

        :param value: The logger_file path.
        :type: str
        """
        self.__logger_file = value
        if self.__logger_file:
            # If set logging file,
            # then add file handler and remove stream handler.
            self.logger_file_handler = logging.FileHandler(self.__logger_file)
            self.logger_file_handler.setFormatter(self.logger_formatter)
            for _, logger in self.logger.items():
                logger.addHandler(self.logger_file_handler)

    @property
    def debug(self):
        """Debug status

        :param value: The debug status, True or False.
        :type: bool
        """
        return self.__debug

    @debug.setter
    def debug(self, value):
        """Debug status

        :param value: The debug status, True or False.
        :type: bool
        """
        self.__debug = value
        if self.__debug:
            # if debug status is True, turn on debug logging
            for _, logger in self.logger.items():
                logger.setLevel(logging.DEBUG)
            # turn on http_client debug
            http_client.HTTPConnection.debuglevel = 1
        else:
            # if debug status is False, turn off debug logging,
            # setting log level to default `logging.WARNING`
            for _, logger in self.logger.items():
                logger.setLevel(logging.WARNING)
            # turn off http_client debug
            http_client.HTTPConnection.debuglevel = 0

    @property
    def logger_format(self):
        """The logger format.

        The logger_formatter will be updated when sets logger_format.

        :param value: The format string.
        :type: str
        """
        return self.__logger_format

    @logger_format.setter
    def logger_format(self, value):
        """The logger format.

        The logger_formatter will be updated when sets logger_format.

        :param value: The format string.
        :type: str
        """
        self.__logger_format = value
        self.logger_formatter = logging.Formatter(self.__logger_format)

    def get_api_key_with_prefix(self, identifier, alias=None):
        """Gets API key (with prefix if set).

        :param identifier: The identifier of apiKey.
        :param alias: The alternative identifier of apiKey.
        :return: The token for api key authentication.
        """
        if self.refresh_api_key_hook is not None:
            self.refresh_api_key_hook(self)
        key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None)
        if key:
            prefix = self.api_key_prefix.get(identifier)
            if prefix:
                return "%s %s" % (prefix, key)
            else:
                return key

    def get_basic_auth_token(self):
        """Gets HTTP basic authentication header (string).

        :return: The token for basic HTTP authentication.
        """
        username = ""
        if self.username is not None:
            username = self.username
        password = ""
        if self.password is not None:
            password = self.password
        return urllib3.util.make_headers(
            basic_auth=username + ':' + password
        ).get('authorization')

    def auth_settings(self):
        """Gets Auth Settings dict for api client.

        :return: The Auth Settings information dict.
        """
        auth = {}
        return auth

    def to_debug_report(self):
        """Gets the essential information for debugging.

        :return: The report for debugging.
        """
        return "Python SDK Debug Report:\n"\
               "OS: {env}\n"\
               "Python Version: {pyversion}\n"\
               "Version of the API: 1.0.0\n"\
               "SDK Package Version: 1.0.0".\
               format(env=sys.platform, pyversion=sys.version)

    def get_host_settings(self):
        """Gets an array of host settings

        :return: An array of host settings
        """
        return [
            {
                'url': "https://open.lanxin.cn/open/apigw",
                'description': "No description provided",
            }
        ]

    def get_host_from_settings(self, index, variables=None, servers=None):
        """Gets host URL based on the index and variables
        :param index: array index of the host settings
        :param variables: hash of variable and the corresponding value
        :param servers: an array of host settings or None
        :return: URL based on host settings
        """
        if index is None:
            return self._base_path

        variables = {} if variables is None else variables
        servers = self.get_host_settings() if servers is None else servers

        try:
            server = servers[index]
        except IndexError:
            raise ValueError(
                "Invalid index {0} when selecting the host settings. "
                "Must be less than {1}".format(index, len(servers)))

        url = server['url']

        # go through variables and replace placeholders
        for variable_name, variable in server.get('variables', {}).items():
            used_value = variables.get(
                variable_name, variable['default_value'])

            if 'enum_values' in variable \
                    and used_value not in variable['enum_values']:
                raise ValueError(
                    "The variable `{0}` in the host URL has invalid value "
                    "{1}. Must be {2}.".format(
                        variable_name, variables[variable_name],
                        variable['enum_values']))

            url = url.replace("{" + variable_name + "}", used_value)

        return url

    @property
    def host(self):
        """Return generated host."""
        return self.get_host_from_settings(self.server_index, variables=self.server_variables)

    @host.setter
    def host(self, value):
        """Fix base path."""
        self._base_path = value
        self.server_index = None
