import random
import re
import json
from sanic import response
from unify_api.utils.response_code import RET
from pot_libs.settings import SETTING
from pot_libs.sanic_api import summary
from pot_libs.utils.pendulum_wrapper import my_pendulum
from unify_api.constants import PRODUCT_INFOS
from unify_api.modules.users.components.current_user_info_cps import *
from pot_libs.common.components.responses import success_res
from unify_api.modules.users.procedures.jwt_user import jwt_user
from unify_api.modules.users.procedures.send_sms import sample
from pot_libs.aredis_util.aredis_utils import RedisUtils
from unify_api.modules.users.dao.current_user_info_dao import *
from unify_api.modules.common.dao.common_dao import user_by_phone_number, \
    user_by_user_id
from pot_libs.aiohttp_util.aiohttp_utils import AioHttpUtils
from unify_api.modules.common.dao.common_dao import load_user_lang
from unify_api.modules.common.procedures.multi_lang import (
    load_login_tips, load_product_info
)
from unify_api.modules.users.procedures.jwt_user import auth_phone_verify, \
    check_password
from unify_api.modules.common.procedures.multi_lang import parse_user_lang

AUTH_EXP = 300  # redis缓存时间
VALIDATION_EXP = 60  # 发送验证码间隔时间


@summary('用户头像昵称等信息')
async def get_user_info(request) -> UserInfoResponse:
    # 1.从jwt中获取user_id
    user_id = jwt_user(request)
    if not user_id:
        valid_body = {
            "code": 401,
            "message": "token is valid",
            "data": {},
            "srv_time": my_pendulum.now().to_datetime_string()
        }
        return response.json(body=valid_body, status=401)
    # 2.查询用户
    user_info = await user_by_user_id(user_id)
    if not user_info:
        return UserInfoResponse().user_error()
    # head_image_url = ""
    # 3.查询出用户昵称等信息
    # if user_info["wechat_id"]:
    #     sql = "select nickname, headimgurl, user.zhiweiu_auth, " \
    #           "user.real_name, user.unit from wechat_mp_user wu " \
    #           "inner join user on " \
    #           "wu.unionid = user.wechat_id where user.user_id = %s"
    #     async with MysqlUtil() as conn:
    #         user_info = await conn.fetchone(sql=sql, args=(int(user_id)), )
    #     head_image_url = user_info.get("headimgurl")[5:]
    # 4. 查询用户产品信息
    pro_sql = "select product from user_product_auth where user_id = %s"
    async with MysqlUtil() as conn:
        pro_info = await conn.fetchall(sql=pro_sql, args=(int(user_id)), )
    # [{'product': 1}, {'product': 2}, {'product': 3}]

    lang = user_info["lang"]
    product_list = []
    if pro_info:
        for pro in pro_info:
            # ulock的app产品跳过, PRODUCT_INFOS只存u+云的产品
            prod_id = pro["product"]
            if prod_id not in PRODUCT_INFOS or prod_id in (1,):
                continue

            d_prod_info = load_product_info(prod_id, lang)
            product_list.append(d_prod_info)

        if product_list:
            d_prod_info = load_product_info(7, lang)
            product_list.append(d_prod_info)
        product_list = sorted(product_list, key=lambda x: x["sort_num"])
    # 4. 查询zhiweiu权限, 默认普通用户
    zhiweiu_auth = user_info.get("zhiweiu_auth")
    if not zhiweiu_auth and zhiweiu_auth != 0:
        zhiweiu_auth = 1
    log.info(f"user info:{user_id}, {user_info}")
    return UserInfoResponse(
        user_id=user_id,
        # nick_name=user_info.get("nickname") or user_info.get("real_name"),
        nick_name=user_info.get("real_name"),
        head_image_url="http://cdn.ucyber.cn/common/default-avatar.png",
        real_name=user_info.get("real_name"),
        unit=user_info.get("unit"),
        product_list=product_list,
        zhiweiu_auth=zhiweiu_auth,
        lang=lang
    )


@summary("发送手机验证码")
async def get_send_sms(request):
    phone = request.args.get("phone")
    lang = await parse_user_lang(request)
    if re.match(r'^1[3-9]\d{9}$', phone):
        send_flag = await RedisUtils().get(f"sms:send_sms_flag_{phone}")
        if send_flag:
            # 请求验证码过于频繁
            vfy_code_freq_tip = load_login_tips("vfy_code_freq", lang)
            return success_res(code=RET.send_sms_quick, msg=vfy_code_freq_tip)
        code = '%06d' % random.randint(1, 999999)
        # 短信间隔
        await RedisUtils().setex(f"sms:send_sms_flag_{phone}", VALIDATION_EXP,
                                 1)
        await RedisUtils().setex(f"sms:sms_{phone}", AUTH_EXP, code)
        result_body = sample.main([phone, code])
        log.info(f"send_sms {phone}, {code}, {result_body}")
        if result_body.code == "OK":
            # 发送成功
            tip = load_login_tips("send_suc", lang)
            return success_res(msg=tip)

        else:
            if result_body.message == "触发天级流控Permits:10":
                # 您今日发送短信次数已用完
                tip = load_login_tips("msg_send_lim", lang)
            elif result_body.message == "触发小时级流控Permits:5":
                # 请求验证码过于频繁，请1小时后重新发送
                tip = load_login_tips("vfy_code_freq_hr", lang)
            elif result_body.message == "触发分钟级流控Permits:1":
                # 请求验证码过于频繁，请1分钟后重新发送
                tip = load_login_tips("vfy_code_freq_min", lang)
            else:
                # 验证码发送失败
                tip = load_login_tips("vfy_code_send_fail", lang)
            return success_res(code=RET.send_sms_quick2, msg=tip)

    # 手机号码不合法
    tip = load_login_tips("phone_not_invalid", lang)
    return success_res(code=RET.send_sms_fail, msg=tip)


@summary("首次登录保存用户信息")
async def post_save_userinfo(request, body: SaveUserReq):
    real_name = body.real_name
    unit = body.unit
    job = body.job
    phone = body.phone
    password = body.password
    password2 = body.password2
    wechat_id = body.wechat_id
    verify = body.verify
    user_id = jwt_user(request)
    lang = await load_user_lang(user_id)
    if not all([real_name, job, unit, phone, password]):
        # 缺少必传参数
        tip = load_login_tips("miss_req_param", lang)
        return success_res(code=RET.params_loss, msg=tip)

    if password != password2:
        # 两次密码不一致
        tip = load_login_tips("passwd_inconst", lang)
        return success_res(code=RET.password_error, msg=tip)

    passwd = check_password(password)
    if verify:
        auth_verify = await auth_phone_verify(phone, verify)
        if not auth_verify:
            # 验证码不一致
            tip = load_login_tips("vfy_code_inconst", lang)
            return success_res(code=RET.verify_error, msg=tip)

    if wechat_id:
        wechat_info = await is_having_wechat_id(wechat_id)
        is_having = await phone_is_having_dao(phone)
        if wechat_info:
            await update_user_info_by_wechat_id(real_name, unit, job, passwd,
                                                phone, wechat_id)
        elif is_having and is_having["wechat_id"]:
            # 该手机号已注册
            tip = load_login_tips("phone_registered", lang)
            return success_res(code=RET.verify_error, msg=tip)

        elif is_having:
            # 修改信息
            await update_user_info(real_name, unit, job, passwd, phone,
                                   wechat_id)
        else:
            # 注册
            await insert_user_info(real_name, unit, job, passwd, phone,
                                   wechat_id)
    else:
        await insert_user_info(real_name, unit, job, passwd, phone)

    user = await user_by_phone_number(phone)
    # 获取token
    request_body = {
        "phone": phone,
        "client_name": "validation",
        "host": request.host,
        "user_id": user["user_id"],
        "db": SETTING.mysql_db
    }
    resp_str, status = await AioHttpUtils().post(
        SETTING.auth_url,
        request_body,
        timeout=50,
    )
    log.info(f"post_save_userinfo resp_str={resp_str} status={status}")
    resp = json.loads(resp_str)
    if status == 200:
        return success_res(data=resp)
    else:
        # 保存用户信息成功
        tip = load_login_tips("user_save_suc", lang)
        return success_res(msg=tip)


@summary("修改手机号")
async def post_update_phone(request, body: UpdatePhoneReq):
    user_id = body.user_id
    phone = body.phone
    verify = body.verify
    log.info(f"post_update_phone {phone} {verify}")
    # 检验手机号
    wechat_product_dict, old_zhiwei_u, old_role = {}, None, None
    wechat_product_auth, wechat_product_auth_dict = None, {}
    phone_info = await user_by_phone_number(phone)
    lang = await load_user_lang(user_id)
    if phone_info:
        if phone_info.get("wechat_id"):
            err_tip = load_login_tips("phone_bound", lang)
            return 401, {"code": 40001, "data": None, "message": err_tip}

        else:
            # 删除这条记录
            await update_not_wechat(user_id, phone)

        old_zhiwei_u = phone_info["zhiweiu_auth"]
        old_role = phone_info["role"]
        wechat_product_auth = await \
            load_user_product_auth(phone_info["user_id"])

        wechat_product_auth_dict = \
            {wechat_product["product"]: wechat_product["cid_ext"]
             for wechat_product in wechat_product_auth} \
                if wechat_product_auth else {}
        wechat_product_dict = \
            {wechat_product["product"]: wechat_product["proxy"]
             for wechat_product in wechat_product_auth} \
                if wechat_product_auth else {}
    # 效验验证码
    auth_verify = await auth_phone_verify(phone, verify)
    if not auth_verify:
        # 验证码不一致
        tip = load_login_tips("vfy_code_inconst", lang)
        return success_res(code=RET.verify_error, msg=tip)

    user_product_auth = await load_user_product_auth(user_id)
    user_product_auth_dict = \
        {user_product["product"]: user_product["cid_ext"]
         for user_product in user_product_auth} if user_product_auth else {}
    user_product_dict = \
        {user_product["product"]: user_product["proxy"]
         for user_product in user_product_auth} if user_product_auth else {}
    if all([wechat_product_auth, user_product_auth]):
        product_auth = wechat_product_auth_dict.update(
            user_product_auth_dict)
    elif wechat_product_auth:
        product_auth = wechat_product_auth_dict
    elif user_product_auth:
        product_auth = user_product_auth_dict
    else:
        product_auth = None
    # 绑定手机和智维u、role
    sql_list = [f"phone_number='{phone}'"]
    if old_zhiwei_u:
        sql_list.append(f"zhiweiu_auth={old_zhiwei_u}")
    if old_role:
        sql_list.append(f"role={old_role}")
    mid_sql = ",".join(sql_list)
    await update_user_auth_dao(user_id, mid_sql)

    # 修改权限
    if product_auth:
        for key, value in product_auth.items():
            is_auth = await search_product_auth_dao(user_id, key)
            if is_auth:
                proxy = user_product_dict.get(key) or wechat_product_dict.get(
                    key)
                await update_product_auth_dao(key, value, user_id, proxy=proxy)
            else:
                proxy = (user_product_dict.get(key) or wechat_product_dict.get(
                    key))
                await insert_product_auth_dao(user_id, key, value, proxy=proxy)

    # 修改成功
    tip = load_login_tips("update_suc", lang)
    return success_res(msg=tip)


@summary("验证手机号")
async def post_auth_phone(request, body: AuthPhoneReq):
    phone = body.phone
    verify = body.verify
    user_id = jwt_user(request)
    lang = await load_user_lang(user_id)
    auth_verify = await auth_phone_verify(phone, verify)
    if not auth_verify:
        # 验证码不一致
        tip = load_login_tips("vfy_code_inconst", lang)
        return success_res(code=RET.verify_error, msg=tip)

    # 验证通过
    tip = load_login_tips("vfy_pass", lang)
    return success_res(msg=tip)


@summary("找回密码")
async def post_back_password(request, body: PhoneIsHavingReq):
    phone = body.phone
    user_id = jwt_user(request)
    lang = await load_user_lang(user_id)
    is_delete = await phone_is_having_dao(phone)
    if not is_delete:
        # 手机号未注册或已注销
        tip = load_login_tips("phone_no_reg_dea", lang)
        return success_res(code=RET.phone_not_register, msg=tip)

    # 成功
    tip = load_login_tips("succeed", lang)
    return success_res(msg=tip)


@summary("修改密码")
async def post_update_password(request, body: UpdatePasswordReq):
    phone = body.phone
    user_id = jwt_user(request)
    lang = await load_user_lang(user_id)
    is_delete = await phone_is_having_dao(phone)
    if not is_delete:
        # 手机号未注册
        tip = load_login_tips("phone_not_reg", lang)
        return success_res(code=RET.phone_not_register, msg=tip)

    password = body.password
    password2 = body.password2
    if password != password2:
        # 两次密码不一致
        tip = load_login_tips("passwd_inconst", lang)
        return success_res(code=RET.password_error, msg=tip)

    passwd = check_password(password)
    await update_password_dao(phone, passwd)

    # 修改成功
    tip = load_login_tips("update_suc", lang)
    return success_res(msg=tip)


@summary("获取个人信息")
async def post_userinfo(request, body: UserinfoReq) -> UserinfoResp:
    user_id = body.user_id
    data = await user_by_user_id(user_id)
    is_wechat = 1 if data["wechat_id"] else 0
    return UserinfoResp(user_id=data["user_id"], phone=data["phone_number"],
                        unit=data.get("unit") or "", job=data.get("job") or "",
                        name=data.get("real_name") or "", is_wechat=is_wechat,
                        lang=data["lang"])


@summary("修改个人信息")
async def post_update_userinfo(request, body: UpdateUserInfoReq) \
        -> UserinfoResp:
    user_id = body.user_id
    name = body.name
    unit = body.unit
    job = body.job
    lang = body.lang
    if not any([name, unit, job, lang]):
        # 缺少参数
        tip = load_login_tips("miss_param", lang)
        return success_res(code=RET.params_loss, msg=tip)

    if name:
        await update_userinfo_dao(user_id, 'real_name', name)
    elif unit:
        await update_userinfo_dao(user_id, 'unit', unit)
    elif lang:
        await update_userinfo_dao(user_id, "lang", lang)
    else:
        await update_userinfo_dao(user_id, 'job', job)
    data = await user_by_user_id(user_id)
    is_wechat = 1 if data["wechat_id"] else 0
    return UserinfoResp(user_id=data["user_id"], phone=data["phone_number"],
                        unit=data["unit"], job=data["job"], lang=data["lang"],
                        name=data["real_name"], is_wechat=is_wechat)


@summary("解除微信绑定")
async def post_delete_wechat(request, body: UserinfoReq) \
        -> UserinfoResp:
    user_id = body.user_id
    lang = await load_user_lang(user_id)
    await update_userinfo_dao(user_id, "wechat_id", None)

    # 解除微信绑定成功
    tip = load_login_tips("unbind_wechat_suc", lang)
    return success_res(msg=tip)


@summary("微信绑定")
async def post_update_wechat(request, body: UpdateWechatReq):
    user_id = body.user_id
    code = body.code
    lang = await load_user_lang(user_id)
    # 微信绑定失败
    bind_fail_tip = load_login_tips("wechat_bind_fail", lang)

    # 1.通过code换取网页授权access_token
    url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&" \
          "secret=%s&code=%s&grant_type=authorization_code" % \
          (SETTING.wechat_open_appid2, SETTING.wechat_open_secret_key2, code)
    log.info(f"post_update_wechat url:{url}")
    res_token, token_status = await AioHttpUtils().get(url)
    log.info(f"post_update_wechat res_token:{res_token}")
    if token_status != 200:
        log.error("post_update_wechat get access_token fail")
        # 微信绑定失败
        return success_res(code=RET.wechat_error, msg=bind_fail_tip)

    result = json.loads(res_token)
    # 2.获取用户信息
    if 'access_token' not in result:
        log.error("post_update_wechat access_token not found")
        # 微信绑定失败
        return success_res(code=RET.wechat_error, msg=bind_fail_tip)

    url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s" \
          % (result['access_token'], result['openid'])
    res, user_info_status = await AioHttpUtils().get(url)
    log.info(f"post_update_wechat user_info:{res}")
    if user_info_status != 200:
        log.error("post_update_wechat get user info fail")
        # 微信绑定失败
        return success_res(code=RET.wechat_error, msg=bind_fail_tip)

    user_info = json.loads(res)
    wechat_product_dict, old_zhiwei_u, old_role = {}, None, None
    if 'openid' in user_info and 'unionid' in user_info:
        unionid = user_info['unionid']
        data = await search_user_by_wechat_id_dao(unionid)
        wechat_product_auth, wechat_product_auth_dict = None, {}
        log.info(f"post_update_wechat{data}, unionid:{unionid}")
        if data:
            log.info(f"post_update_wechat{data}")
            user_id = data["user_id"]
            if data.get("phone_number"):
                # 该微信号已经被绑定
                tip = load_login_tips("wechat_bound", lang)
                return success_res(code=RET.wechat_repeat, msg=tip)

            else:
                old_zhiwei_u = data["zhiweiu_auth"]
                old_role = data["role"]
                r = '%04d' % random.randint(1, 9999)
                new_wechat_id = f"delete{r}{unionid}"
                # 删除老用户
                await update_user_is_delete(user_id, new_wechat_id)

                # # 添加微信到新用户
                # await update_not_phone(user_id, unionid)
                # 修改权限
                wechat_product_auth = await load_user_product_auth(user_id)

                wechat_product_auth_dict = \
                    {wechat_product["product"]: wechat_product["cid_ext"]
                     for wechat_product in wechat_product_auth} \
                        if wechat_product_auth else {}
                wechat_product_dict = \
                    {wechat_product["product"]: wechat_product["proxy"]
                     for wechat_product in wechat_product_auth} \
                        if wechat_product_auth else {}

        user_product_auth = await load_user_product_auth(user_id)
        user_product_auth_dict = \
            {user_product["product"]: user_product["cid_ext"]
             for user_product in user_product_auth} \
                if user_product_auth else {}
        user_product_dict = \
            {user_product["product"]: user_product["proxy"]
             for user_product in user_product_auth} \
                if user_product_auth else {}
        if all([wechat_product_auth, user_product_auth]):
            product_auth = wechat_product_auth_dict.update(
                user_product_auth_dict)
        elif wechat_product_auth:
            product_auth = wechat_product_auth_dict
        elif user_product_auth:
            product_auth = user_product_auth_dict
        else:
            product_auth = None
        # 绑定微信和智维u、role
        sql_list = [f"wechat_id='{unionid}'"]
        if old_zhiwei_u:
            sql_list.append(f"zhiweiu_auth={old_zhiwei_u}")
        if old_role:
            sql_list.append(f"role={old_role}")
        mid_sql = ",".join(sql_list)
        await update_user_auth_dao(user_id, mid_sql)
        # 修改权限
        if product_auth:
            for key, value in product_auth.items():
                proxy = user_product_dict.get(key) or wechat_product_dict.get(
                    key)
                is_auth = await search_product_auth_dao(user_id, key)
                if is_auth:
                    await update_product_auth_dao(key, value, user_id, proxy)
                else:
                    await insert_product_auth_dao(user_id, key, value, proxy)
        log.info(f"post_update_wechat product_auth:{product_auth}")
        # 微信绑定成功
        tip = load_login_tips("wechat_bind_suc", lang)
        return success_res(msg=tip)

    return success_res(code=RET.wechat_error, msg=bind_fail_tip)


@summary("手机号码是否被绑定")
async def post_phone_is_having(request, body: PhoneIsHavingReq):
    phone = body.phone
    user_id = jwt_user(request)
    lang = await load_user_lang(user_id)
    data = await phone_is_having_dao(phone)
    if data:
        # 该手机号码已经被绑定
        tip = load_login_tips("phone_bound", lang)
        return success_res(code=RET.phone_repeat, msg=tip)

    # 该手机号码可用
    tip = load_login_tips("phone_no_usable", lang)
    return success_res(msg=tip)


@summary("注销用户")
async def post_delete_user(request, body: UserinfoReq):
    user_id = body.user_id
    lang = await load_user_lang(user_id)
    data = await user_by_user_id(user_id)
    code = '%04d' % random.randint(1, 9999)
    new_wechat_id = f"delete{code}" + data["wechat_id"] \
        if data["wechat_id"] else None
    await update_user_is_delete(user_id, new_wechat_id)

    # 注销成功
    tip = load_login_tips("logout_suc", lang)
    return success_res(msg=tip)
