# Used for micro-service which developed by nameko
# install nameko before use
"""
    Example::

        from wisdoms.auth import permit

        host = {'AMQP_URI': "amqp://guest:guest@localhost"}

        auth = permit(host)

        class A:
            @auth
            def func():
                pass
"""
from nameko.rpc import rpc
from nameko.standalone.rpc import ServiceRpcProxy
from nameko.exceptions import RpcTimeout, MethodNotFound

import json
import traceback

from functools import wraps
from operator import methodcaller

from wisdoms.utils import xpt_func
from wisdoms.exec import UserException, WisdomErrorException
from wisdoms.commons import revert, codes

default_host = {'AMQP_URI': 'pyamqp://guest:guest@localhost'}
default_base_service = 'baseUserApp'


def rpc_with_timeout(host, service, func, data=None, timeout=8):
    try:
        with ServiceRpcProxy(service, host, timeout=timeout) as proxy:
            if data is not None:
                res = methodcaller(func, data)(proxy)
            else:
                res = methodcaller(func)(proxy)
            return res
    except RpcTimeout as e:
        print(service, ' ~~连接超时 %s sec......，检查是否启动......' % e)
        return revert(codes.TIMEOUT, desc=service + ' 连接超时')
    except MethodNotFound as e:
        print('function of this server %s not found,未找到方法 %s ' % (service, e))
        return revert(codes.GET_WAY_ERROR, desc='未找到方法' + e)


def rpc_wrapper(service_, func_, *args, host_=default_host, timeout_=8, **kwargs):
    """
    rpc包装方法

    :param service_:
    :param func_:
    :param host_:
    :param timeout_:
    :param args:
    :param kwargs:
    :return:
    """
    try:
        with ServiceRpcProxy(service_, host_, timeout=timeout_) as proxy:
            res = methodcaller(func_, *args, **kwargs)(proxy)
            return res
    except RpcTimeout as e:
        print(service_, ' ~~连接超时 %s sec......，检查是否启动......' % e)
        return revert(codes.TIMEOUT, desc=service_ + ' 连接超时')
    except MethodNotFound as e:
        print('function of this server %s not found,未找到方法 %s ' % (service_, e))
        return revert(codes.GET_WAY_ERROR, desc='未找到方法' + e)


def ms_base(ms_host=None, base_service=None, func=None, **extra):
    """
    返回父类，闭包，传参数ms host
    :param ms_host:
    :param base_service
    :param func
    :param extra: 额外信息
    :extra: roles 角色权限
    :extra: name 微服务名称
    :extra: types 微服务类型 essential 类型的应用 角色必须带creator
    :extra: entrance 微服务pc入口
    :extra: entrance4app 小程序入口
    :extra: entrance4back 后台入口
    :extra: identity 唯一标识 暂时只用于平台base service 其他微服务不添加
    :return: class of base
    """

    ms_host = ms_host if ms_host else default_host
    base_service = base_service if base_service else default_base_service
    func = func if func else 'app2db'

    class MsBase:
        name = 'ms-base'

        @rpc
        # @exception()
        def export_info2db(self):
            """
            export information of this service to database

            :return:
            """
            clazz = type(self)
            service = clazz.name
            functions = dir(clazz)

            origin = extra
            origin['service'] = service
            origin['functions'] = functions

            rpc_with_timeout(ms_host, base_service, func, origin)

    return MsBase


def permit(host=None, **extra):
    """
    调用微服务功能之前，进入基础微服务进行权限验证

    :param: host: micro service host
    :extra: service 基础应用微服务名称
    :extra: func 用户验证方法名称
    :extra: full_info 是否携带用户和组织全部信息 默认false
    :return decorator
    """
    host = host if host else default_host
    base_service = extra.get('service', default_base_service)
    verify_func = extra.get('func', 'verify')

    full_info = extra.get('full_info', False)

    def wrapper(f):
        @wraps(f)
        def inner(*args, **kwargs):
            service = args[0].name
            func = f.__name__
            token = args[1].get('token')

            res = rpc_with_timeout(host, base_service, verify_func, {'service': service, 'func': func, 'token': token})

            try:
                # judge except
                if res.get('code'):
                    if res.get('code') < 0:
                        return res
            except:
                pass

            if res:
                org = None
                try:
                    # 添加组织信息 add info of org
                    org = res.get('org').get(str(res.get('current_org')))
                except:
                    pass

                try:
                    # delete user passoword
                    del res['password_hash']
                    del res['partner']
                except:
                    pass

                cut_user = dict()
                cut_org = dict()

                cut_user['id'] = res['id']
                cut_user['account'] = res['account']
                cut_user['username'] = res.get('username')
                cut_user['current_org'] = res.get('current_org')
                cut_user['phone'] = res.get('phone')
                cut_user['recommend_phone'] = res.get('recommend_phone')
                cut_user["agent_oid"] = res.get("agent_oid")
                cut_user["from_agent"] = res.get("from_agent")
                cut_user["member_phone"] = res.get("member_phone")
                cut_user['email'] = res.get('email')
                cut_user['appid'] = res.get('appid')
                cut_user['openid'] = res.get('openid')
                try:
                    if org:
                        cut_org['id'] = org['id']
                        cut_org['name'] = org['name']
                        cut_org['desc'] = org.get('desc')
                        cut_org['owner'] = org.get('owner')
                        cut_org['region'] = org.get('region')
                        cut_org['es_extend'] = org.get('es_extend')
                        cut_org['share_profit_id'] = org.get('share_profit_id')
                        cut_org['mch_id'] = org.get('mch_id')
                        cut_org['sp_appid'] = org.get('sp_appid')
                        cut_org['appid'] = org.get('appid')
                        cut_org['images'] = org.get('images')
                        cut_org['videos'] = org.get('videos')
                        cut_org['location'] = org.get('location')
                except:
                    pass

                if full_info:
                    args[1]['full_user'] = res
                    args[1]['full_org'] = org

                args[1]['uid'] = res.get('id')
                args[1]['user'] = cut_user
                args[1]['org'] = cut_org

                return f(*args, **kwargs)

            raise UserException('verified failed-用户权限验证失败')

        return inner

    return wrapper


def add_uid(host=None, **extra):
    """
    用户token 返回用户id
    :param host:
    :extra: service 基础应用微服务名称
    :extra: func 获取用户uid方法名称
    :return: decorator
    """
    host = host if host else default_host
    base_service = extra.get('service', default_base_service)
    get_uid_func = extra.get('func', 'get_uid')

    def wrapper(f):
        @wraps(f)
        def inner(*args, **kwargs):
            token = args[1].get('token')

            res = rpc_with_timeout(host, base_service, get_uid_func, {'token': token})

            try:
                if res.get('code'):
                    if res.get('code') < 0:
                        return res
            except:
                pass

            if res:
                args[1]['uid'] = res
                return f(*args, **kwargs)

            raise UserException('verified failed-用户token验证失败')

        return inner

    return wrapper


def add_user(host=None, **extra):
    """
    用户token 返回用户信息
    :param host:
    :extra: service 基础应用微服务名称
    :extra: func 获取用户信息方法名称
    :extra: full_info 是否携带用户和组织全部信息 默认false
    :return: decorator
    """

    host = host if host else default_host
    base_service = extra.get('service', default_base_service)
    get_user_func = extra.get('func', 'get_user')
    full_info = extra.get('full_info', False)

    def wrapper(f):
        @wraps(f)
        def inner(*args, **kwargs):
            token = args[1].get('token')

            res = rpc_with_timeout(host, base_service, get_user_func, {'token': token})

            try:
                if res.get('code'):
                    if res.get('code') < 0:
                        return res
            except:
                pass

            if res:

                org = None
                try:
                    # 添加组织信息 add info of org
                    org = res.get('org').get(str(res.get('current_org')))
                except:
                    pass

                try:
                    # delete user passoword
                    del res['password_hash']
                    del res['partner']
                except:
                    pass

                cut_user = dict()
                cut_org = dict()

                cut_user['id'] = res['id']
                cut_user['account'] = res['account']
                cut_user['username'] = res.get('username')
                cut_user['current_org'] = res.get('current_org')
                cut_user['phone'] = res.get('phone')
                cut_user['recommend_phone'] = res.get('recommend_phone')
                cut_user["agent_oid"] = res.get("agent_oid")
                cut_user["from_agent"] = res.get("from_agent")
                cut_user["member_phone"] = res.get("member_phone")
                cut_user['email'] = res.get('email')
                cut_user['avatar'] = res.get('avatar')
                cut_user['appid'] = res.get('appid')
                cut_user['openid'] = res.get('openid')
                try:
                    if org:
                        cut_org['id'] = org['id']
                        cut_org['name'] = org['name']
                        cut_org['desc'] = org.get('desc')
                        cut_org['owner'] = org.get('owner')
                        cut_org['region'] = org.get('region')
                        cut_org['es_extend'] = org.get('es_extend')
                        cut_org['share_profit_id'] = org.get('share_profit_id')
                        cut_org['mch_id'] = org.get('mch_id')
                        cut_org['sp_appid'] = org.get('sp_appid')
                        cut_org['appid'] = org.get('appid')
                        cut_org['images'] = org.get('images')
                        cut_org['videos'] = org.get('videos')
                        cut_org['location'] = org.get('location')
                except:
                    pass

                if full_info:
                    args[1]['full_user'] = res
                    args[1]['full_org'] = org

                args[1]['uid'] = res.get('id')
                args[1]['user'] = cut_user
                args[1]['org'] = cut_org

                return f(*args, **kwargs)

            raise UserException('verified failed-用户token验证失败')

        return inner

    return wrapper


def privilege(host=None, **extra):
    """

    :param host:
    :param extra:
    :extra: service 基础应用微服务名称
    :extra: func 验证超级用户
    :extra: level 权限级别：default = 10
    :return:
    """
    host = host if host else default_host
    base_service = extra.get('service', default_base_service)
    get_user_func = extra.get('func', 'get_user')
    level = extra.get('level', 10)

    def wrapper(f):
        @wraps(f)
        def inner(*args, **kwargs):
            token = args[1].get('token')

            res = rpc_with_timeout(host, base_service, get_user_func, {'token': token})

            try:
                if res.get('code'):
                    if res.get('code') < 0:
                        return res
            except:
                pass

            if res:
                try:
                    del res['password_hash']
                    del res['partner']
                except:
                    pass

                #  验证用户特权级别
                p = res.get('privilege', None)
                if p:
                    if p.get('level', 0) >= level:
                        args[1]['user'] = res
                        return f(*args, **kwargs)

            raise UserException('verified failed-用户特殊权限验证失败，用户特殊权限，或权限级别不够。权限级别:' + str(level))

        return inner

    return wrapper


def agent(host=None, **extra):
    """
    用户token 返回用户信息
    :param host:
    :extra: service 基础应用微服务名称
    :extra: func 获取用户信息方法名称
    :return: decorator
    """

    host = host if host else default_host
    base_service = extra.get('service', default_base_service)
    get_user_func = extra.get('func', 'get_agent')

    def wrapper(f):
        @wraps(f)
        def inner(*args, **kwargs):
            token = args[1].get('token')

            res = rpc_with_timeout(host, base_service, get_user_func, {'token': token})

            try:
                if res.get('code'):
                    if res.get('code') < 0:
                        return res
            except:
                pass

            if res:

                org = None
                try:
                    # 添加组织信息 add info of org
                    org = res.get('org').get(str(res.get('current_org')))
                except:
                    pass

                try:
                    # delete user passoword
                    del res['password_hash']
                    del res['partner']
                except:
                    pass

                cut_user = dict()
                cut_org = dict()

                cut_user['id'] = res['id']
                cut_user['account'] = res['account']
                cut_user['username'] = res.get('username')
                cut_user['current_org'] = res.get('current_org')
                cut_user['phone'] = res.get('phone')
                cut_user['email'] = res.get('email')

                try:
                    if org:
                        cut_org['id'] = org['id']
                        cut_org['name'] = org['name']
                        cut_org['desc'] = org.get('desc')
                        cut_org['owner'] = org.get('owner')
                        cut_org['region'] = org.get('region')
                        cut_org['es_extend'] = org.get('es_extend')
                except:
                    pass

                tmp_permit = dict()

                tmp_permit['privilege'] = res.get('privilege')
                tmp_permit['org'] = res.get('agent_org')
                tmp_permit['region'] = res.get('agent_region')
                tmp_permit['agent'] = res.get('agent_sub')

                args[1]['uid'] = res.get('id')
                args[1]['user'] = cut_user
                args[1]['org'] = cut_org
                args[1]['permit'] = tmp_permit

                return f(*args, **kwargs)

            raise UserException('verified failed-用户token验证失败')

        return inner

    return wrapper


def config_server(host=default_host, service='configServerFunc', func='import_config', timeout=30):
    """
    从配置微服务获取配置
    :param host:
    :param service:
    :param func:
    :param timeout:
    :return:
    """
    try:
        with ServiceRpcProxy(service, host, timeout=timeout) as proxy:
            res = methodcaller(func)(proxy)
            data = json.dumps(res, indent=4, ensure_ascii=False, sort_keys=False, separators=(',', ':'))
            print(data)
            return res
    except RpcTimeout as e:
        print('配置中心微服务连接超时，或未启动, 时长:%s' % e)
        return {}
        # return {'code': -30, 'desc': '配置中心微服务连接超时，或未启动, 时长:%s' % e, 'data': None}


def my_rpc(func):
    @rpc
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            traceback.print_exc()
            print(e)
            if isinstance(e, WisdomErrorException):
                return e.revert()
            return revert(codes.ERROR, desc='其他异常类错误')

    return wrapper


class MsFuncFactory:
    """
    微服务方法工厂class
    统一管理rabbit mq host
    """

    def __init__(self, host):
        self.host = host

    def rpc_wrapper(self, service, func, *args, **kwargs):
        """
        rpc包装器
        :param service:
        :param func:
        :param args:
        :param kwargs:
        :return: result of process of rpc
        """
        return rpc_wrapper(service, func, *args, self.host, **kwargs)

    def ms_base(self):
        """
        应用微服务基础类，父类
        :return: class
        """
        return ms_base(self.host)

    def permit(self, **extra):
        """
        权限验证
        :param extra:
        :return: decorator
        """
        return permit(self.host, **extra)

    def add_user(self, **extra):
        """
        user
        :param extra:
        :return: decorator
        """
        return add_user(self.host, **extra)

    def add_uid(self, **extra):
        """
        user id
        :param extra:
        :return: decorator
        """
        return add_uid(self.host, **extra)

    def privilege(self, **extra):
        """
        平台管理员权限验证装饰器
        :param extra:
        :return: decorator
        """
        return privilege(self.host, **extra)

    def config_server(self):
        """
        从配置中心获取配置
        :return: result type of dict
        """
        return config_server(self.host)
