# -*- coding:utf-8 -*-
"""
    封装用户接口
"""

import time
import json
import traceback

import pymysql
from redlock import RedLock
from django.db import transaction
from django.contrib.auth.models import User
from django.conf import settings
from gm_types.gaia import POINTS_TYPE, LOGIN_AUTH_TYPE, USER_ACCOUNT_TYPE, WECHAT_PLATFORM_TYPE
from gm_types.error import ERROR as ERROR_CODES

import point
from hippo.models import Doctor
from api.models import Person, PlatformUser, IOSUserDeviceMap, UserExtra
from api.tasks.async_send_msg_for_new_user import send_msg_for_new_user
from rpc.tool.random_tool import random_str
from rpc.tool.error_code import gen
from rpc.cache import user_cancellation_cache
from rpc.tool.log_tool import user_logger, logging_exception
from api.tool.user_tool import get_user_extra_by_user_id
from api.trigger.user_trigger import reg_trigger, user_change_trigger
from api.util import wechat_util

AUTH_ACCOUNT_MAP = {
    LOGIN_AUTH_TYPE.phone: USER_ACCOUNT_TYPE.PHONE,
    LOGIN_AUTH_TYPE.SinaWeibo: USER_ACCOUNT_TYPE.BLOG,
    LOGIN_AUTH_TYPE.QZone: USER_ACCOUNT_TYPE.QQ,
    LOGIN_AUTH_TYPE.life_360: USER_ACCOUNT_TYPE.LIFE_360,
    LOGIN_AUTH_TYPE.Wechat: USER_ACCOUNT_TYPE.WECHAT,
    LOGIN_AUTH_TYPE.Email: USER_ACCOUNT_TYPE.EMAIL,
    LOGIN_AUTH_TYPE.WechatPub: USER_ACCOUNT_TYPE.WECHAT_PUB,
    LOGIN_AUTH_TYPE.WechatSmall: USER_ACCOUNT_TYPE.WECHAT,
    LOGIN_AUTH_TYPE.WechatSmall_MouthGun: USER_ACCOUNT_TYPE.Wechat_MouthGun,
    LOGIN_AUTH_TYPE.MOMO: USER_ACCOUNT_TYPE.MOMO,
    LOGIN_AUTH_TYPE.IOS: USER_ACCOUNT_TYPE.IOS,
}

THIRD_TYPE_LIST = [
    USER_ACCOUNT_TYPE.BLOG,
    USER_ACCOUNT_TYPE.QQ,
    USER_ACCOUNT_TYPE.LIFE_360,
    USER_ACCOUNT_TYPE.WECHAT,
    USER_ACCOUNT_TYPE.WECHAT_PUB,
    USER_ACCOUNT_TYPE.Wechat_MouthGun,
    USER_ACCOUNT_TYPE.MOMO,
    USER_ACCOUNT_TYPE.IOS,
]

lock_key = 'USER_REG'
lock_ttl = 180
connection_detail = [settings.REDIS['user_reg_lock']]

def gen_username(account_id, account_type):
    '''
    生成auth_user表中的username, 不再直接使用第三方id, 去除username本身业务含义
    '''
    return '_'.join(['gm', str(account_type) + random_str(strlength=1), str(account_id)])

class UserOperate(object):
    """
    用户add操作, 包括绑定. 注册
    """
    @classmethod
    def gen_lock_key(cls, account_id, account_type, *args, **kwargs):
        """
        新用户注册时, 添加锁处理, 防止同时创建的情况
        """
        return ':'.join([lock_key, str(account_type), str(account_id)])

    @classmethod
    def get_lock(cls, account_id, account_type, *args, **kwargs):
        return RedLock(cls.gen_lock_key(account_id, account_type), connection_details=connection_detail, ttl=lock_ttl)


class UserRegister(UserOperate):
    """
    用户注册
    """

    @classmethod
    def operate_user_db(*args, **kwargs):
        """
        重载方法时需注意！！！
        注: operate_user_db函数返回值约定：
            True---注册成功but用户不是新创建的;
            None---注册失败;
            int---注册成功, 返回用户id
        """
        pass

    @classmethod
    def register(cls, *args, **kwargs):
        user_id = None
        with cls.get_lock(*args, **kwargs):
            user_id = cls.operate_user_db(*args, **kwargs)


        cls.reg_user_trigger(user_id, kwargs.get('extra_info'))
        return user_id


    @classmethod
    def reg_user_trigger(cls, user_id, extra_info=None):
        if user_id and user_id is not True:
            reg_trigger(user_id, extra_info)
            user_change_trigger(user_id)


    @classmethod
    def gen_new_user(cls, account_id, account_type=USER_ACCOUNT_TYPE.PHONE, extra_info=None, third_info=None, **kwargs):
        """
        创建user, userextra, person表提交
        account_id:  用做 auth_user的username
        """
        third_info = third_info or {}
        nick_name = third_info.get('converted_third_nick_name') or "更美用户%s" % random_str(10)
        portrait = third_info.get('converted_third_portrait')
        gm_username = gen_username(account_id, account_type)
        user_obj = User.objects.create_user(gm_username, last_name=nick_name)
        cls.gen_new_person(account_type, account_id, user_obj)
        userextra_obj = get_user_extra_by_user_id(user_obj.id)
        point.add(user_id=user_obj.id, reason=POINTS_TYPE.REGISTER)
        if portrait:
            userextra_obj.portrait = portrait
        if account_type == USER_ACCOUNT_TYPE.PHONE:
            userextra_obj.phone = account_id
        userextra_obj.auth_type = kwargs.get('_auth_type') or LOGIN_AUTH_TYPE.phone
        userextra_obj.save()
        return user_obj


    @classmethod
    def gen_new_person(cls, account_type, account_id, userobj):
        person_obj = Person()
        person_obj.user = userobj
        if account_type == USER_ACCOUNT_TYPE.PHONE:
            person_obj.phone = account_id
        elif account_type == USER_ACCOUNT_TYPE.EMAIL:
            person_obj.email = account_id
        person_obj.save()


class PhoneRegister(UserRegister):
    """
    通过手机号注册
    """

    @classmethod
    def operate_user_db(cls, account_id=None, account_type=USER_ACCOUNT_TYPE.PHONE, extra_info=None, **kwargs):
        """
            生成数据库信息
        """
        person_obj = get_user_by_type_id(USER_ACCOUNT_TYPE.PHONE, account_id)
        if person_obj:
            return True

        new_userobj = None
        try:
            with transaction.atomic():
                new_userobj = cls.gen_new_user(account_id, account_type=USER_ACCOUNT_TYPE.PHONE, extra_info=extra_info)
            return new_userobj and new_userobj.id
        except Exception as e:
            user_logger.error('[ERROR][USER_REG] phone(%s) reg failed for reason: \n%s '% (account_id, traceback.format_exc()))
            return None


class ThirdpartyRegister(UserRegister):
    """
        第三方用户注册
    """
    @classmethod
    def register(cls, *args, **kwargs):
        user_id = None
        with cls.get_lock(kwargs['account_id'], kwargs['account_type']):
            if kwargs.get('phone'):
                with cls.get_lock(kwargs['phone'], USER_ACCOUNT_TYPE.PHONE):
                    user_id = cls.operate_user_db(*args, **kwargs)
            else:
                user_id = cls.operate_user_db(*args, **kwargs)

        cls.reg_user_trigger(user_id, kwargs.get('extra_info'))
        return user_id

    @classmethod
    def gen_platform_user(cls, account_id=None, account_type=None, third_info=None):
        p_user = PlatformUser()
        p_user.platform_type = account_type
        p_user.platform_id = account_id
        p_user.extra_info = (third_info or {}).get('third_detail') or '{}'
        return p_user


    @classmethod
    def operate_user_db(cls, phone=None, account_id=None, account_type=None, extra_info=None, third_info=None, **kwargs):
        """
        数据库操作
        """
        existing = get_user_by_type_id(account_type, account_id)
        if existing:
            return True

        base_user = None
        if phone:
            base_user = get_user_by_type_id(USER_ACCOUNT_TYPE.PHONE, phone)
            if _is_exist_puser_for_user(base_user, account_type):
                gen(ERROR_CODES.EXIST_THRID_FOR_PHONE)
        is_new = False

        try:
            with transaction.atomic():
                if not base_user:
                    new_userobj = cls.gen_new_user(account_id, account_type=account_type, extra_info=extra_info, third_info=third_info, **kwargs)
                    base_user = new_userobj
                    is_new = True

                new_puser_obj = cls.gen_platform_user(account_id, account_type, third_info=third_info)
                new_puser_obj.user = base_user
                new_puser_obj.save()
                cls.ios_user_device_map(account_id, account_type, extra_info=extra_info, third_info=third_info)
            return not is_new or base_user.id
        except Exception as e:
            user_logger.error('[ERROR][USER_REG] account(%s_%s)-phone(%s) reg failed for reason: \n%s' % (account_type, account_id, phone, traceback.format_exc()))
            return None

    @classmethod
    def ios_user_device_map(cls, account_id=None, account_type=None, extra_info=None, third_info=None):
        """
        苹果用户和设备号映射
        :param account_id:
        :param account_type:
        :param extra_info:
        :param third_info:
        :return:
        """
        device_id = extra_info.get('device_id')
        if not all([account_type == USER_ACCOUNT_TYPE.IOS, account_id, device_id]):
            return
        create_info = {
            'ios_user_id': account_id,
            'device_id': device_id,
        }
        IOSUserDeviceMap.objects.create(**create_info)


def _is_exist_puser_for_user(user_id, platform_type):
    """
    根据用户id判定某一第三方平台是否已设置绑定
    """
    if not user_id:
        return False
    pr = PlatformUser.get_platform_record_by_user_id(user_id, platform_type)
    return bool(pr)


def get_user_by_authtype_id(auth_type, account_id):
    """
    通过账号类型-id获取Person Obj LOGIN_AUTH_TYPE
    """
    account_type = _convert_auth2account_type(auth_type)
    return get_user_by_type_id(account_type, account_id)


def get_user_by_type_id(account_type, account_id):
    if account_type in (USER_ACCOUNT_TYPE.PHONE, USER_ACCOUNT_TYPE.EMAIL):
        return Person.get_user_by_type_id(account_type, account_id)
    else:
        return PlatformUser.get_user_by_type_id(account_type, account_id)


def _user_register(account_id=None, account_type=None, extra_info=None, third_info=None, **kwargs):
    """
    用户注册
    """
    _auth_type = account_type
    account_type = _convert_auth2account_type(account_type)
    kwargs['_auth_type'] = _auth_type

    if account_type == USER_ACCOUNT_TYPE.PHONE:
        user_id = PhoneRegister.register(account_type=USER_ACCOUNT_TYPE.PHONE, account_id=account_id, extra_info=extra_info, **kwargs)
    else:
        phone = kwargs.pop('phone', None)
        user_id = ThirdpartyRegister.register(phone=phone, account_type=account_type, account_id=account_id, extra_info=extra_info, third_info=third_info, **kwargs)

    # 完美所长给新注册的用户发送欢迎私信
    send_msg_for_new_user.delay(target_user_id=user_id)
    return user_id


def _do_login(ctx, user):
    '''
    登录操作, 记录session等
    '''
    user.backend = "django.contrib.auth.backends.ModelBackend"
    ctx.session.do_login(user)
    data = user.person.data(True)
    data['session_key'] = ctx.session.session_key
    data['login_user_id'] = user.id
    return data


def _convert_auth2account_type(auth_type):
    '''
    转换登录类型到账户类型的切换, 如微信app登录与小程序登录是一个账户, 但验证类型不同
    LOGIN_AUTH_TYPE -> USER_ACCOUNT_TYPE
    '''
    return AUTH_ACCOUNT_MAP.get(auth_type)


def create_platform_user(auth_type, account_id, user_id):
    '''
    创建第三方平台用户记录
    '''
    account_type = _convert_auth2account_type(auth_type)
    if account_type not in THIRD_TYPE_LIST:
        return
    puser_obj = PlatformUser()
    puser_obj.platform_type = account_type
    puser_obj.platform_id = account_id
    puser_obj.user_id = user_id
    puser_obj.save()


def simple_user_info_by_user_ids(user_ids):
    '''
    获取简单用户信息
    return
        {   str:  dict
            uid1: {}
            uid2: {}
        }
    '''
    result = {}
    if not user_ids:
        return result
    user_ids = list(set(user_ids))
    user_ids = [int(u) for u in filter(None, user_ids)]
    res_users = User.objects.filter(id__in=user_ids).select_related('userextra').all()
    for u in res_users:
        u_info = {}
        u_info['nick_name'] = u.last_name
        u_info['portrait'] = u.userextra.portrait
        u_info['user_id'] = u.id
        result[str(u.id)] = u_info
    return result


def simple_doctor_info_by_user_ids(user_ids):
    '''
    获取简单医生信息
    '''
    result = {}
    if not user_ids:
        return result
    user_ids = list(set(user_ids))
    user_ids = [int(u) for u in filter(None, user_ids)]
    res_docs = Doctor.objects.filter(is_online=1, user_id__in=user_ids).values('id', 'doctor_type', 'user_id').all()
    for d in res_docs:
        d_info = {}
        d_info['doctor_id'] = d['id']
        d_info['doctor_type'] = d['doctor_type']
        result[str(d['user_id'])] = d_info
    return result


def get_phone_by_user_ids(user_ids):
    '''
    根据用户id查询手机号
    '''
    result = {}
    if not user_ids:
        return result

    step = 1000
    user_ids_list = [user_ids[i:i+step] for i in range(0, len(user_ids), step)]
    for uids in user_ids_list:
        info_map_list = list(Person.objects.filter(user_id__in=user_ids).values('user_id', 'phone'))
        for im in info_map_list:
            result[str(im['user_id'])] = im['phone']

    return result


def get_user_platform_info(user_id):
    """
    获取用户的第三方（微信）相关信息, 后续可能会增加传入参数数量
    :param user_id: 用户 id
    :return:
    """
    HOST = settings.DATABASES.get('default').get('HOST')
    PORT = settings.DATABASES.get('default').get('PORT')
    NAME = settings.DATABASES.get('default').get('USER')
    PASSWORD = settings.DATABASES.get('default').get('PASSWORD')
    DB_NAME = settings.DATABASES.get('default').get('NAME')

    kwargs = {"host": HOST, "port": PORT, "user": NAME, "password": PASSWORD, "database": DB_NAME}
    if not PORT:
        kwargs.pop('port')
    else:
        kwargs['port'] = int(kwargs['port'])

    conn = pymysql.connect(
        charset="utf8",
        cursorclass=pymysql.cursors.DictCursor,
        **kwargs
    )
    cur = conn.cursor()

    sql = "select * from `api_platformuser` where user_id = {}".format(user_id)
    cur.execute(sql)
    res = cur.fetchall()
    if not res:
        return None

    extra_info = res[0]['extra_info']
    if not extra_info:
        return None

    return json.loads(res[0]['extra_info'])


def get_open_id_by_user_id(user_id, wechat_platform_type=WECHAT_PLATFORM_TYPE.GM_SMALL):
    '''
    根据user_id获取open_id
    :param user_id: user_id
    :param wechat_platform_type: 默认为更美小程序
    :return:
    '''
    try:
        open_id = wechat_util.get_open_id_by_user_id(user_id, wechat_platform_type)
        if open_id:
            return open_id
        extra_info = get_user_platform_info(user_id)
        if extra_info:
            return extra_info.get('openid')
        return ''
    except Exception as e:
        logging_exception()
        return ''


def user_is_third_party(user_id):
    try:
        user_extra = UserExtra.objects.get(user_id=user_id)
    except:
        return False

    return user_extra.auth_type in [
        LOGIN_AUTH_TYPE.SinaWeibo,
        LOGIN_AUTH_TYPE.QZone,
        LOGIN_AUTH_TYPE.life_360,
        LOGIN_AUTH_TYPE.Wechat,
        LOGIN_AUTH_TYPE.WechatPub,
        LOGIN_AUTH_TYPE.WechatSmall,
        LOGIN_AUTH_TYPE.WechatSmall_MouthGun,
        LOGIN_AUTH_TYPE.MOMO,
        LOGIN_AUTH_TYPE.IOS,
    ]


class UserCancellation(object):

    cache = user_cancellation_cache
    key = "user_cancellation_cache"

    @staticmethod
    def time_score():
        return time.time()

    @classmethod
    def record_cancellation_user(cls, user_id):
        """注销用户"""
        cls.cache.hset(cls.key, str(user_id), cls.time_score())

    @classmethod
    def del_cancellation_user(cls, user_id):
        """注销用户记录删除"""
        cls.cache.hdel(cls.key, str(user_id))

    @classmethod
    def get_cancellation_user(cls, user_id):
        """注销用户记录获取"""
        return cls.cache.hget(cls.key, str(user_id))
