# coding=utf-8
import datetime
from collections import defaultdict

from django.db import models, transaction
from django.db.models import F, Count
from gm_types.gaia import FAMOUS_DOCTOR_JOIN_TYPE, PLATFORM_CHOICES

from api.models.base_model import BaseModel
from rpc.cache import famous_doctors_cache

cache_key_prefix = 'famous_doctors:rank:{doc_type}:{item_id}'
cache_key_prefix_new = '{version}:famous_doctors:rank:{doc_type}:{item_id}'
_cache = famous_doctors_cache
ALL_item_idS = list(range(0, 18))
MAX_COUNT = 999999999


def get_rank_cache_key(doc_type, item_id, version=None):
    if version:
        return cache_key_prefix_new.format(version=version, doc_type=doc_type, item_id=item_id) + str(doc_type)
    else:
        return cache_key_prefix.format(doc_type=doc_type, item_id=item_id) + str(doc_type)


class FamousDoctorsRegistrationer(BaseModel):
    class Meta:
        verbose_name = "名医大赏报名表"
        db_table = "api_famous_doctors_registrationer"
        app_label = "api"

    doctor_id = models.CharField(max_length=200, verbose_name=u'医生或机构id', db_index=True)
    doctor_type = models.IntegerField(verbose_name=u'医生或机构')
    version = models.CharField(max_length=200, verbose_name=u'版本号(对应活动版本)', default='000000')

    @classmethod
    def join(cls, doc_id, doc_type, item_ids, version=None):
        """医生报名。"""
        if not version:

            return "此参数必须传"
        else:
            item = cls(doctor_id=doc_id, doctor_type=doc_type, version=version)
            item.save()
            for item_id in item_ids:
                register_item = FamousDoctorRegisterItem(register_id=item.id, item_id=item_id)
                register_item.save()

            cls.init_rank(doc_type, doc_id, item_ids, version)

            return True, ''

    @classmethod
    def is_joined(cls, doc_type, doc_id, version):
        """判断医生有没有报名。"""

        return cls.objects.filter(doctor_type=doc_type, doctor_id=doc_id, version=version).exists()

    @classmethod
    def remove_record(cls, doc_type, doc_id):
        """清除医生的报名记录。"""

        dw = cls.objects.filter(doctor_id=doc_id, doctor_type=doc_type).all()

        item_ids = []
        for d in dw:
            item_ids.append(d.item_id)
            d.delete()

        # 移除相关排名记录
        for aid in item_ids:
            rank_key = get_rank_cache_key(doc_type, aid)
            _cache.zrem(rank_key, doc_id)

    @classmethod
    def _set_cache_vote_count(cls, doc_type, doc_id, item_id, final_count, version=None):
        """设置缓存中的票数。"""

        cache_key = get_rank_cache_key(doc_type, item_id, version)
        _cache.zadd(cache_key, final_count, doc_id)

    @classmethod
    def init_rank(cls, doc_type, doc_id, item_ids, version=None):
        """医生报名后, 进入排行榜, 票数为0"""

        for item_id in item_ids:
            cls._set_cache_vote_count(doc_type, doc_id, item_id, 0, version)

    @classmethod
    def update_rank(cls, doc_type, doc_id, item_ids, version, cnt):
        """用户投票后, 更新排行榜, 票数自增"""
        for item_id in item_ids:
            cache_key = get_rank_cache_key(doc_type, item_id, version)
            _cache.zincrby(cache_key, doc_id, cnt)

    @classmethod
    def init_rank_cache(cls):
        """全量刷新缓存。"""

        rank_info = {
            FAMOUS_DOCTOR_JOIN_TYPE.DOCTOR: defaultdict(list),
            FAMOUS_DOCTOR_JOIN_TYPE.HOSPITAL: defaultdict(list),
        }
        for reg in cls.objects.filter(is_online=True):
            doc_id = reg.doctor_id
            doc_type = reg.doctor_type
            item_list = FamousDoctorRegisterItem.objects.filter(register_id=reg.id)
            for item in item_list:
                score = item.final_count
                rank_info[str(doc_type)][str(item.item_id)].extend([score, doc_id])

        for doc_type, rank_dict in rank_info.items():
            for item_id, scores in rank_dict.items():
                cache_key = get_rank_cache_key(doc_type, item_id)
                _cache.delete(cache_key)
                _cache.zadd(cache_key, *scores)

    @classmethod
    def vote_rank_and_gap(cls, doc_type, doc_id, item_id, version=None):
        """
        根据医生id, 及奖项id获取排名 及与上一名的差距
        return: tuple(rank, gap)
            eg: (999, 5, 100)   # 999票, 第五名, 距离上一名差距100票
        """

        cache_key = get_rank_cache_key(doc_type, item_id, version)
        d_vote_cnt = _cache.zscore(cache_key, doc_id) or 0

        # 比当前分数多的数量
        bigger_vote_count = _cache.zcount(cache_key, min=d_vote_cnt + 1, max=MAX_COUNT)
        rank = bigger_vote_count + 1

        gap = 0
        if bigger_vote_count > 0:
            # 上一名数据
            last_idx = bigger_vote_count - 1
            last_vote_tuples = _cache.zrevrange(cache_key, start=last_idx, end=last_idx, withscores=True,
                                                score_cast_func=lambda vote: int(vote))
            if last_vote_tuples:
                last_vote_tuple = last_vote_tuples[0]
                last_vote_cnt = last_vote_tuple[1]
                gap = last_vote_cnt - d_vote_cnt

        return d_vote_cnt, rank, gap

    @classmethod
    def list_rank_info(cls, doc_type, item_ids=None, offset=None, limit=None):
        """获取排行信息。

        return {'{item_id}': [{"doc_id": "121212", "vote_cnt": 1}, ...]}
        """

        result = {}
        for item_id in item_ids:
            result[str(item_id)] = cls.list_rank_by_item_id(item_id, offset, limit)

        return result

    @classmethod
    def list_rank_info_by_item_id(cls, doc_type, item_id, version, offset=0, limit=-1):
        """获取单个 item_id 下的排名信息"""
        offset = offset or 0
        limit = limit or -1
        cache_key = get_rank_cache_key(doc_type, item_id, version)
        rank_infos = _cache.zrevrange(cache_key, start=offset, end=limit, withscores=True)
        ret = []
        doc_ids = []
        for doc_id, vote_cnt in rank_infos:
            ret.append({"doc_id": doc_id, "vote_cnt": vote_cnt, "item_id": item_id})
            doc_ids.append(doc_id)

        res = cls.objects.filter(doctor_type=doc_type, doctor_id__in=doc_ids).values("id", "doctor_id")
        doc_reg_id_map = {item["doctor_id"]: item["id"] for item in res}
        for item in ret:
            try:
                item["reg_id"] = doc_reg_id_map[item["doc_id"]]
            except:
                item["reg_id"] = doc_reg_id_map[item["doc_id"].decode('utf8')]
        return ret

    @classmethod
    def get_doc_id_by_res_id(cls, res_id, version=None):
        """根据res_id获得医生的doctor_id"""
        doc_id = cls.objects.filter(pk=res_id, version=version).values("doctor_id")
        return doc_id

    @classmethod
    def get_all_doctors_list(cls, version):
        """获得报名医生的doctor_id和doctor_type"""
        doctor_ids_and_types = cls.objects.filter(version=version).order_by("-create_time").values("doctor_id", "doctor_type")
        return doctor_ids_and_types

    @classmethod
    def get_reg_id_by_doc_id(cls, doc_id, version):
        """根据doc_id获得reg_id"""
        reg_id = cls.objects.filter(doctor_id=doc_id, version=version).values("id")
        return reg_id

    @classmethod
    def get_doc_id_and_doc_type(cls, reg_id):
        """获得报名医生的doctor_id和doctor_type"""
        doctor_ids_and_types = cls.objects.filter(pk=reg_id).values("doctor_id", "doctor_type")
        return doctor_ids_and_types


class FamousDoctorRegisterItem(BaseModel):
    class Mete:
        verbose_name = "名医大赏报名项目表"
        db_table = "api_famous_doctors_registeritem"
        app_label = "api"

    register_id = models.BigIntegerField(verbose_name=u'报名ID', db_index=True)
    item_id = models.BigIntegerField(verbose_name='项目ID', db_index=True)
    vote_count = models.IntegerField(verbose_name=u'票数统计', default=0)
    final_count = models.IntegerField(verbose_name=u'读取数量(真实数、灌水数量)', default=0)

    @classmethod
    def get_items(cls, reg_id):
        items_result = cls.objects.filter(register_id=reg_id).values("item_id", "create_time", "vote_count",
                                                                     "register_id")
        return items_result

    @classmethod
    def get_vote_count(cls, reg_id, item_id):
        """根据reg_id和item_id查询vote_count"""
        vote_count = cls.objects.filter(register_id=reg_id, item_id=item_id).values('vote_count')
        return vote_count

    @classmethod
    def update_vote_count(cls, reg_id, item_id, count):
        """改票"""
        cls.objects.filter(register_id=reg_id, item_id=item_id).update(vote_count=count, final_count=count)

    @classmethod
    def is_item(cls, reg_id, item_id):
        """判断报名的时候数据是否入库"""
        return cls.objects.filter(register_id=reg_id, item_id=item_id).exists()


class FamousDoctorsItemVote(BaseModel):
    class Meta:
        verbose_name = "名医大赏投票记录表"
        db_table = "api_famous_doctors_vote"
        app_label = "api"

    item_id = models.BigIntegerField(verbose_name='项目ID', db_index=True)
    register_id = models.BigIntegerField(verbose_name=u'报名ID', db_index=True)
    user_id = models.BigIntegerField(verbose_name=u'投票用户 ID', db_index=True)
    platform = models.CharField(verbose_name=u"投票平台", max_length=3, choices=PLATFORM_CHOICES,
                                default=PLATFORM_CHOICES.IPHONE)

    @classmethod
    def do_vote(cls, user_id, item_id, reg_id, platform):
        with transaction.atomic():
            new_obj = cls(register_id=reg_id, user_id=user_id, item_id=item_id, platform=platform)
            new_obj.save()
            reg_obj = FamousDoctorRegisterItem.objects.get(register_id=reg_id, item_id=item_id)
            reg_obj.vote_count = F('vote_count') + 1
            reg_obj.final_count = F('final_count') + 1
            reg_obj.save()

        return reg_obj.final_count

    @classmethod
    def Two_or_Five_do_vote(cls, item_id, reg_id, cnt):
        with transaction.atomic():
            reg_obj = FamousDoctorRegisterItem.objects.get(register_id=reg_id, item_id=item_id)
            reg_obj.vote_count = F('vote_count') + cnt
            reg_obj.final_count = F('final_count') + cnt
            reg_obj.save()
        return reg_obj.final_count

    @classmethod
    def add_virtual_vote(cls, item_id, reg_id, cnt=1):
        reg_obj = FamousDoctorRegisterItem.objects.get(register_id=reg_id, item_id=item_id)
        reg_obj.final_count = F('final_count') + cnt
        reg_obj.save()

    @classmethod
    def get_regids_by_user_id(cls, user_id):
        """根据user_id查询reg_ids"""
        reg_ids = cls.objects.filter(user_id=user_id).values("register_id")
        return reg_ids


class FamousDoctorsAppend(models.Model):
    class Meta:
        verbose_name = "名医大赏机构入驻表"
        db_table = "api_famous_doctors_append"
        app_label = "api"

    name = models.CharField(verbose_name=u'机构名称', max_length=128, db_index=True)
    tel_phone = models.CharField(verbose_name=u'机构联系电话', max_length=128, db_index=True)
    address = models.CharField(verbose_name=u'机构地址', max_length=255, db_index=True)
    version = models.CharField(verbose_name=u'2020名医大赏版本', max_length=128, default='000000')
    created_time = models.DateTimeField(verbose_name=u'创建时间', auto_now_add=True)

    @classmethod
    def is_appendin(cls, name):
        return cls.objects.filter(name=name).exists()

    @classmethod
    def famous_doctors_append(cls, name, tel_phone, address, version):
        """名医大赏机构入驻"""
        famous_doctors = FamousDoctorsAppend(name=name, tel_phone=tel_phone, address=address, version=version)
        famous_doctors.save()

    @classmethod
    def famous_doctors_find(cls, version):
        """根据version查询新入驻的机构列表"""
        famous_doctors_list = cls.objects.filter(version=version).order_by("-created_time").values('id', 'name', 'address', 'tel_phone',
                                                                         'created_time')
        return famous_doctors_list


class FamousDoctorsCanvass(models.Model):
    class Meta:
        verbose_name = "名医大赏改票记录表"
        db_table = "api_famous_doctors_canvass"
        app_label = "api"

    admin_user_id = models.IntegerField(verbose_name=u'登录系统用户id', db_index=True)
    register_id = models.IntegerField(verbose_name=u'医生注册id', db_index=True)
    item_id = models.IntegerField(verbose_name=u'项目id', db_index=True)
    add_num = models.IntegerField(verbose_name=u'灌水票数', db_index=True)
    create_time = models.DateTimeField(verbose_name=u'创建时间', auto_now_add=True)

    @classmethod
    def add_user(cls, user_id, reg_id, item_id, add_num):
        """记录谁为哪个医生改了票"""
        user = FamousDoctorsCanvass(admin_user_id=user_id, register_id=reg_id, item_id=item_id, add_num=add_num)
        user.save()


class FamousDoctorsReward(models.Model):
    class Meta:
        verbose_name = "名字大赏领奖记录表"
        db_table = "api_famous_doctors_reward"
        app_label = "api"

    user_id = models.IntegerField(verbose_name=u"投票用户id", db_index=True)
    reward_type = models.IntegerField(verbose_name=u"奖励类型", db_index=True)
    create_time = models.DateTimeField(verbose_name=u"创建时间", db_index=True)
    reward = models.CharField(verbose_name=u"奖励描述", max_length=128, db_index=True)
    version = models.CharField(verbose_name=u"版本控制", max_length=128, db_index=True)

    @classmethod
    def add_reward(cls, user_id, reward_type, create_time, reward, version=None):
        """添加领奖记录"""
        user = FamousDoctorsReward(user_id=user_id, reward_type=reward_type, create_time=create_time, reward=reward,
                                   version=version)
        user.save()

    @classmethod
    def find_reward(cls, user_id, create_time):
        """查询用户今天是否领过奖"""
        now = datetime.datetime.now()
        # 获取今天零点
        zeroToday = now - datetime.timedelta(hours=now.hour, minutes=now.minute, seconds=now.second,
                                             microseconds=now.microsecond)
        # 获取23:59:59
        lastToday = zeroToday + datetime.timedelta(hours=23, minutes=59, seconds=59)
        return cls.objects.filter(user_id=user_id, create_time__range=[zeroToday, lastToday]).exists()

    @classmethod
    def count_type(cls, user_id):
        """查询用户获得美分的数量"""
        count = cls.objects.filter(reward_type=1, user_id=user_id).aggregate(c=Count('user_id'))
        return count

    @classmethod
    def user_get_reward(cls, user_id, award_time, award_end_time, version):
        """判断用户是否已经领过奖"""
        return cls.objects.filter(reward_type=0,
                                  user_id=user_id,
                                  version=version).exists()
