# -*- coding:utf-8 -*-
"""
@Author  :   g1879
@Contact :   g1879@qq.com
"""
from pathlib import Path

from requests import Session
from requests.structures import CaseInsensitiveDict

from .options_manage import OptionsManager
from .._commons.web import cookies_to_tuple, set_session_cookies


class SessionOptions(object):
    """requests的Session对象配置类"""

    def __init__(self, read_file=True, ini_path=None):
        """
        :param read_file: 是否从文件读取配置
        :param ini_path: ini文件路径
        """
        self.ini_path = None
        self._download_path = None
        self._timeout = 10
        self._del_set = set()  # 记录要从ini文件删除的参数

        self._headers = None
        self._cookies = None
        self._auth = None
        self._proxies = None
        self._hooks = None
        self._params = None
        self._verify = None
        self._cert = None
        self._adapters = None
        self._stream = None
        self._trust_env = None
        self._max_redirects = None
        self._retry_times = 3
        self._retry_interval = 2

        if read_file is False:
            return

        ini_path = str(ini_path) if ini_path else None
        om = OptionsManager(ini_path)
        self.ini_path = om.ini_path

        options = om.session_options
        if options.get('headers', None) is not None:
            self.set_headers(options['headers'])

        if options.get('cookies', None) is not None:
            self.set_cookies(options['cookies'])

        if options.get('auth', None) is not None:
            self._auth = options['auth']

        if options.get('params', None) is not None:
            self._params = options['params']

        if options.get('verify', None) is not None:
            self._verify = options['verify']

        if options.get('cert', None) is not None:
            self._cert = options['cert']

        if options.get('stream', None) is not None:
            self._stream = options['stream']

        if options.get('trust_env', None) is not None:
            self._trust_env = options['trust_env']

        if options.get('max_redirects', None) is not None:
            self._max_redirects = options['max_redirects']

        self.set_proxies(om.proxies.get('http', None), om.proxies.get('https', None))
        self._timeout = om.timeouts.get('implicit', 10)
        self._download_path = om.paths.get('download_path', None) or None

        others = om.others
        self._retry_times = others.get('retry_times', 3)
        self._retry_interval = others.get('retry_interval', 2)

    # ===========须独立处理的项开始============
    @property
    def download_path(self):
        """返回默认下载路径属性信息"""
        return self._download_path

    def set_download_path(self, path=None):
        """设置默认下载路径
        :param path: 下载路径
        :return: 返回当前对象
        """
        self._download_path = str(path)
        return self

    @property
    def timeout(self):
        """返回timeout属性信息"""
        return self._timeout

    def set_timeout(self, second):
        """设置超时信息
        :param second: 秒数
        :return: 返回当前对象
        """
        self._timeout = second
        return self

    @property
    def proxies(self):
        """返回proxies设置信息"""
        if self._proxies is None:
            self._proxies = {}
        return self._proxies

    def set_proxies(self, http=None, https=None):
        """设置proxies参数
        :param http: http代理地址
        :param https: https代理地址
        :return: 返回当前对象
        """
        self._sets('proxies', {'http': http, 'https': https})
        return self

    @property
    def retry_times(self):
        """返回连接失败时的重试次数"""
        return self._retry_times

    @property
    def retry_interval(self):
        """返回连接失败时的重试间隔（秒）"""
        return self._retry_interval

    def set_retry(self, times=None, interval=None):
        """设置连接失败时的重试操作
        :param times: 重试次数
        :param interval: 重试间隔
        :return: 当前对象
        """
        if times is not None:
            self._retry_times = times
        if interval is not None:
            self._retry_interval = interval
        return self

    # ===========须独立处理的项结束============

    @property
    def headers(self):
        """返回headers设置信息"""
        if self._headers is None:
            self._headers = {}
        return self._headers

    def set_headers(self, headers):
        """设置headers参数
        :param headers: 参数值，传入None可在ini文件标记删除
        :return: 返回当前对象
        """
        if headers is None:
            self._headers = None
            self._del_set.add('headers')
        else:
            self._headers = {key.lower(): headers[key] for key in headers}
        return self

    def set_a_header(self, attr, value):
        """设置headers中一个项
        :param attr: 设置名称
        :param value: 设置值
        :return: 返回当前对象
        """
        if self._headers is None:
            self._headers = {}

        self._headers[attr.lower()] = value
        return self

    def remove_a_header(self, attr):
        """从headers中删除一个设置
        :param attr: 要删除的设置
        :return: 返回当前对象
        """
        if self._headers is None:
            return self

        attr = attr.lower()
        self._headers.pop(attr, None)

        return self

    @property
    def cookies(self):
        """以list形式返回cookies"""
        if self._cookies is None:
            self._cookies = []
        return self._cookies

    def set_cookies(self, cookies):
        """设置cookies信息
        :param cookies: cookies，可为CookieJar, list, tuple, str, dict，传入None可在ini文件标记删除
        :return: 返回当前对象
        """
        cookies = cookies if cookies is None else list(cookies_to_tuple(cookies))
        self._sets('cookies', cookies)
        return self

    @property
    def auth(self):
        """返回认证设置信息"""
        return self._auth

    def set_auth(self, auth):
        """设置认证元组或对象
        :param auth: 认证元组或对象
        :return: 返回当前对象
        """
        self._sets('auth', auth)
        return self

    @property
    def hooks(self):
        """返回回调方法"""
        if self._hooks is None:
            self._hooks = {}
        return self._hooks

    def set_hooks(self, hooks):
        """设置回调方法
        :param hooks: 回调方法
        :return: 返回当前对象
        """
        self._hooks = hooks
        return self

    @property
    def params(self):
        """返回连接参数设置信息"""
        if self._params is None:
            self._params = {}
        return self._params

    def set_params(self, params):
        """设置查询参数字典
        :param params: 查询参数字典
        :return: 返回当前对象
        """
        self._sets('params', params)
        return self

    @property
    def verify(self):
        """返回是否验证SSL证书设置"""
        return self._verify

    def set_verify(self, on_off):
        """设置是否验证SSL证书
        :param on_off: 是否验证 SSL 证书
        :return: 返回当前对象
        """
        self._sets('verify', on_off)
        return self

    @property
    def cert(self):
        """返回SSL证书设置信息"""
        return self._cert

    def set_cert(self, cert):
        """SSL客户端证书文件的路径(.pem格式)，或(‘cert’, ‘key’)元组
        :param cert: 证书路径或元组
        :return: 返回当前对象
        """
        self._sets('cert', cert)
        return self

    @property
    def adapters(self):
        """返回适配器设置信息"""
        if self._adapters is None:
            self._adapters = []
        return self._adapters

    def add_adapter(self, url, adapter):
        """添加适配器
        :param url: 适配器对应url
        :param adapter: 适配器对象
        :return: 返回当前对象
        """
        self._adapters.append((url, adapter))
        return self

    @property
    def stream(self):
        """返回是否使用流式响应内容设置信息"""
        return self._stream

    def set_stream(self, on_off):
        """设置是否使用流式响应内容
        :param on_off: 是否使用流式响应内容
        :return: 返回当前对象
        """
        self._sets('stream', on_off)
        return self

    @property
    def trust_env(self):
        """返回是否信任环境设置信息"""
        return self._trust_env

    def set_trust_env(self, on_off):
        """设置是否信任环境
        :param on_off: 是否信任环境
        :return: 返回当前对象
        """
        self._sets('trust_env', on_off)
        return self

    @property
    def max_redirects(self):
        """返回最大重定向次数"""
        return self._max_redirects

    def set_max_redirects(self, times):
        """设置最大重定向次数
        :param times: 最大重定向次数
        :return: 返回当前对象
        """
        self._sets('max_redirects', times)
        return self

    def _sets(self, arg, val):
        """给属性赋值或标记删除
        :param arg: 属性名称
        :param val: 参数值
        :return: None
        """
        if val is None:
            self.__setattr__(f'_{arg}', None)
            self._del_set.add(arg)
        else:
            self.__setattr__(f'_{arg}', val)
            if arg in self._del_set:
                self._del_set.remove(arg)

    def save(self, path=None):
        """保存设置到文件
        :param path: ini文件的路径，传入 'default' 保存到默认ini文件
        :return: 保存文件的绝对路径
        """
        if path == 'default':
            path = (Path(__file__).parent / 'configs.ini').absolute()

        elif path is None:
            if self.ini_path:
                path = Path(self.ini_path).absolute()
            else:
                path = (Path(__file__).parent / 'configs.ini').absolute()

        else:
            path = Path(path).absolute()

        path = path / 'config.ini' if path.is_dir() else path

        if path.exists():
            om = OptionsManager(str(path))
        else:
            om = OptionsManager(self.ini_path or str(Path(__file__).parent / 'configs.ini'))

        options = session_options_to_dict(self)

        for i in options:
            if i not in ('download_path', 'timeout', 'proxies'):
                om.set_item('session_options', i, options[i])

        om.set_item('paths', 'download_path', self.download_path or '')
        om.set_item('timeouts', 'implicit', self.timeout)
        om.set_item('proxies', 'http', self.proxies.get('http', None))
        om.set_item('proxies', 'https', self.proxies.get('https', None))
        om.set_item('others', 'retry_times', self.retry_times)
        om.set_item('others', 'retry_interval', self.retry_interval)

        for i in self._del_set:
            if i == 'download_path':
                om.set_item('paths', 'download_path', '')
            elif i == 'proxies':
                om.set_item('proxies', 'http', '')
                om.set_item('proxies', 'https', '')
            else:
                om.remove_item('session_options', i)

        path = str(path)
        om.save(path)

        return path

    def save_to_default(self):
        """保存当前配置到默认ini文件"""
        return self.save('default')

    def as_dict(self):
        """以字典形式返回本对象"""
        return session_options_to_dict(self)

    def make_session(self):
        """根据内在的配置生成Session对象，ua从对象中分离"""
        s = Session()
        h = CaseInsensitiveDict(self.headers) if self.headers else CaseInsensitiveDict()

        if self.cookies:
            set_session_cookies(s, self.cookies)
        if self.adapters:
            for url, adapter in self.adapters:
                s.mount(url, adapter)

        for i in ['auth', 'proxies', 'hooks', 'params', 'verify', 'cert', 'stream', 'trust_env', 'max_redirects']:
            attr = self.__getattribute__(i)
            if attr:
                s.__setattr__(i, attr)

        return s, h

    def from_session(self, session, headers=None):
        """从Session对象中读取配置
        :param session: Session对象
        :param headers: headers
        :return: 当前对象
        """
        self._headers = CaseInsensitiveDict(**session.headers, **headers) if headers else session.headers
        self._cookies = session.cookies
        self._auth = session.auth
        self._proxies = session.proxies
        self._hooks = session.hooks
        self._params = session.params
        self._verify = session.verify
        self._cert = session.cert
        self._stream = session.stream
        self._trust_env = session.trust_env
        self._max_redirects = session.max_redirects
        if session.adapters:
            self._adapters = [(k, i) for k, i in session.adapters.items()]
        return self

    # --------------即将废弃---------------

    def set_paths(self, download_path=None):
        """设置默认下载路径
        :param download_path: 下载路径
        :return: 返回当前对象
        """
        if download_path is not None:
            self._download_path = str(download_path)
        return self


def session_options_to_dict(options):
    """把session配置对象转换为字典
    :param options: session配置对象或字典
    :return: 配置字典
    """
    if options in (False, None):
        return SessionOptions(read_file=False).as_dict()

    if isinstance(options, dict):
        return options

    re_dict = dict()
    attrs = ['headers', 'cookies', 'proxies', 'params', 'verify', 'stream', 'trust_env', 'cert',
             'max_redirects', 'timeout', 'download_path']

    for attr in attrs:
        val = options.__getattribute__(f'_{attr}')
        if val is not None:
            re_dict[attr] = val

    return re_dict
