import logging

from django.shortcuts import redirect
from rest_framework.response import Response
from rest_framework.views import APIView

from api.models import UserInfo, ThirdWeChatUserInfo, WeChatInfo
from api.utils.modelutils import get_wx_nickname
from api.utils.response import BaseResponse
from api.utils.serializer import UserInfoSerializer, UserInfoWeiXinSerializer
from api.utils.utils import set_user_token
from common.base.baseutils import get_real_ip_address, WeixinLoginUid
from common.cache.storage import WxLoginBindCache
from common.core.auth import ExpiringTokenAuthentication
from common.core.sysconfig import Config
from common.core.throttle import VisitRegister1Throttle, VisitRegister2Throttle
from common.libs.mp.wechat import make_wx_login_qrcode, show_qrcode_url, WxWebLogin, WxTemplateMsg, WxAppletLogin
from common.utils.caches import set_wx_ticket_login_info_cache, get_wx_ticket_login_info_cache
from common.utils.pending import get_pending_result
from common.utils.token import verify_token, generate_alphanumeric_token_of_length

logger = logging.getLogger(__name__)


def wx_qr_code_response(ret, code, qr_info, ip_addr):
    if code:
        logger.info(f"微信登录码获取成功， {qr_info}")
        errmsg = qr_info.get('errmsg')
        if errmsg:
            ret.code = 1003
            ret.msg = "微信登录码获取失败，请稍后"
        else:
            ticket = qr_info.get('ticket')
            if ticket:
                set_wx_ticket_login_info_cache(ticket, {'ip_addr': ip_addr})
                ret.data = {'qr': show_qrcode_url(ticket), 'ticket': ticket}
    else:
        ret.code = 1003
        ret.msg = "微信登录码获取失败，请稍后"
    return Response(ret.dict)


class WeChatLoginView(APIView):
    throttle_classes = [VisitRegister1Throttle, VisitRegister2Throttle]

    def get(self, request):
        ret = BaseResponse()
        if Config.LOGIN.get("login_type").get('third', '').get('wxp'):
            unique_key = request.query_params.get('unique_key')
            if unique_key:
                cache_obj = WxLoginBindCache(unique_key)
                cache_data = cache_obj.get_storage_cache()
                if cache_data:
                    code, qr_info = cache_data
                else:
                    code, qr_info = cache_data = make_wx_login_qrcode()
                    cache_obj.set_storage_cache(cache_data, qr_info.get('expire_seconds', 600))
                return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request))
        return Response(ret.dict)

    def put(self, request):
        ret = BaseResponse()
        data = request.data
        token = data.get('token')
        uid = data.get('uid')
        wid = data.get('wid')
        if token and uid and wid:
            uid = WeixinLoginUid().get_decrypt_uid(uid)
            wid = WeixinLoginUid().get_decrypt_uid(wid)
            if verify_token(token, uid, True):
                user = UserInfo.objects.filter(uid=uid).first()

                if user and user.is_active:
                    wx_user_obj = ThirdWeChatUserInfo.objects.filter(weixin__openid=wid, enable_login=True,
                                                                     user_id=user).first()
                    if wx_user_obj:
                        key, user_info = set_user_token(user, request)
                        serializer = UserInfoSerializer(user_info)
                        data = serializer.data
                        ret.userinfo = data
                        ret.token = key
                        ret.msg = "验证成功!"
                        WxTemplateMsg(wid, get_wx_nickname(wid)).login_success_msg(user.first_name)
                    else:
                        ret.msg = "微信状态异常，请重新登录"
                        ret.code = 1006
                else:
                    ret.msg = "用户被禁用，请更换其他用户登录"
                    ret.code = 1005
            else:
                ret.msg = "校验失败，请重新登录"
                ret.code = 1006
        else:
            ret.msg = "参数异常，请重新登录"
            ret.code = 1006
        return Response(ret.dict)


class WeChatBindView(APIView):
    authentication_classes = [ExpiringTokenAuthentication, ]
    throttle_classes = [VisitRegister1Throttle, VisitRegister2Throttle]

    def post(self, request):
        ret = BaseResponse()
        uid = request.user.uid
        unique_key = request.data.get('unique_key')
        w_type = request.data.get('w_type', 'xxxx')
        if unique_key:
            cache_obj = WxLoginBindCache(unique_key)
            cache_data = cache_obj.get_storage_cache()
            if cache_data:
                code, qr_info = cache_data
            else:
                code, qr_info = cache_data = make_wx_login_qrcode(f"web.bind.{uid}.{w_type}")
                cache_obj.set_storage_cache(cache_data, qr_info.get('expire_seconds', 600))
            return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request))
        return Response(ret.dict)


def expect_func(result, *args, **kwargs):
    return result and result.get('pk')


class WeChatLoginCheckView(APIView):
    def post(self, request):
        ret = BaseResponse()
        if not Config.LOGIN.get("login_type").get('third', '').get('wxp'):
            return Response(ret.dict)
        ticket = request.data.get("ticket")
        unique_key = request.data.get("unique_key", ticket)
        if ticket and unique_key:
            status, result = get_pending_result(get_wx_ticket_login_info_cache, expect_func, ticket=ticket,
                                                locker_key=ticket, unique_key=unique_key, run_func_count=1)
            if status:
                if result.get('err_msg'):
                    ret.msg = result.get('err_msg')
                    ret.code = 1004
                    return Response(ret.dict)
                wx_ticket_data = result.get('data', {})
                if wx_ticket_data.get('pk', -1) == -1:
                    ret.msg = "还未绑定用户，请通过手机或者邮箱登录账户之后进行绑定"
                    ret.code = 1005
                else:
                    ids = wx_ticket_data.get('ids', [])
                    w_type = wx_ticket_data.get('w_type', '')
                    to_user = wx_ticket_data.get('to_user', '')
                    if ids:
                        user_queryset = UserInfo.objects.filter(pk__in=ids, is_active=True).all()
                        if user_queryset:
                            ret.data = UserInfoWeiXinSerializer(user_queryset, many=True,
                                                                context={'ticket': ticket}).data
                            ret.wid = WeixinLoginUid().get_encrypt_uid(to_user)
                            ret.code = 2000
                            return Response(ret.dict)

                    user = UserInfo.objects.filter(pk=wx_ticket_data['pk']).first()

                    if user.is_active:
                        if to_user and w_type:
                            ret.data = {'uid': user.uid, 'to_user': to_user, 'w_type': w_type}
                        else:
                            wx_obj = ThirdWeChatUserInfo.objects.filter(user_id=user, weixin__openid=to_user,
                                                                        enable_login=True).filter()
                            if not wx_obj:
                                ret.msg = "还未绑定用户，请通过手机或者邮箱登录账户之后进行绑定"
                                ret.code = 1005
                            else:
                                key, user_info = set_user_token(user, request)
                                serializer = UserInfoSerializer(user_info)
                                data = serializer.data
                                ret.userinfo = data
                                ret.token = key
                                ret.msg = "验证成功!"
                    else:
                        ret.msg = "用户被禁用"
                        ret.code = 1005
            else:
                ret.code = 1006
        else:
            ret.code = 1006
        return Response(ret.dict)


class WeChatWebLoginView(APIView):
    throttle_classes = [VisitRegister1Throttle, VisitRegister2Throttle]

    def get(self, request):
        wx_login_obj = WxWebLogin()
        ret = BaseResponse()
        code = request.query_params.get('code')
        if code:
            state = request.query_params.get('state', 'FLYAPPS')
            wx_login_obj.get_wx_token(code)
            wx_user_info = wx_login_obj.get_user_info()
            logger.info(f'{wx_user_info}')
            wx_user_info = {
                'openid': wx_user_info.get('openid'),
                'nickname': wx_user_info.get('nickname'),
                'sex': wx_user_info.get('sex'),
                'head_img_url': wx_user_info.get('headimgurl', ''),
                'address': f"{wx_user_info.get('country')}-{wx_user_info.get('province')}-{wx_user_info.get('city')}",
            }
            logger.info(f'{wx_user_info}')
            WeChatInfo.objects.filter(openid=wx_user_info.get('openid')).update(**wx_user_info)
            info = WxLoginBindCache(state).get_storage_cache()
            if info and isinstance(info, dict):
                info.update({'w_type': 'web', 'to_user': wx_user_info.get('openid')})
                set_wx_ticket_login_info_cache(state, info)
            return redirect(Config.WECHAT_WEB_SUCCESS_REDIRECT_URI)
        return Response(ret.dict)


class WeChatWebSyncView(APIView):
    authentication_classes = [ExpiringTokenAuthentication, ]
    throttle_classes = [VisitRegister1Throttle, VisitRegister2Throttle]

    def get(self, request):
        logger.error(request.query_params)
        wx_login_obj = WxWebLogin()
        ret = BaseResponse()
        ticket = generate_alphanumeric_token_of_length(32)
        WxLoginBindCache(ticket).set_storage_cache({'ip_addr': get_real_ip_address(request), 'pk': request.user.pk})
        ret.data = wx_login_obj.make_auth_uri(ticket)
        ret.ticket = ticket
        return Response(ret.dict)


class WeChatAppletView(APIView):
    throttle_classes = [VisitRegister1Throttle, VisitRegister2Throttle]

    def post(self, request):
        ret = BaseResponse()
        auth_code = request.data.get('auth_code')
        app_id = request.data.get('app_id')
        if auth_code and app_id:
            wx_login_obj = WxAppletLogin()
            if app_id == wx_login_obj.app_id:
                ret.data = wx_login_obj.get_session_from_code(auth_code)
        return Response(ret.dict)
