# coding=utf-8

import traceback
import random
from collections import defaultdict

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

from api.models.base_model import BaseModel
from hippo.models import Doctor, Hospital
from rpc.cache import famous_doctors_cache


cache_key_prefix = '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):
    return cache_key_prefix.format(doc_type=doc_type, item_id=item_id) + 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'医生或机构')

    @classmethod
    def join(cls, doc_id, doc_type, item_ids):
        """医生报名。"""

        item = cls(doctor_id=doc_id, doctor_type=doc_type)
        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)

        return True, ''

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

        return cls.objects.filter(doctor_type=doc_type, doctor_id=doc_id).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, doctor_id)

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

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

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

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

    @classmethod
    def update_rank(cls, doc_type, doc_id, item_ids):
        """用户投票后, 更新排行榜, 票数自增"""

        for item_id in item_ids:
            cache_key = get_rank_cache_key(doc_type, item_id)
            _cache.zincrby(cache_key, doc_id)

    @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):
        """
        根据医生id, 及奖项id获取排名 及与上一名的差距
        return: tuple(rank, gap)
            eg: (999, 5, 100)   # 999票, 第五名, 距离上一名差距100票
        """

        cache_key = get_rank_cache_key(doc_type, item_id)
        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, offset=0, limit=-1):
        """获取单个 item_id 下的排名信息"""

        offset = offset or 0
        limit = limit or -1
        cache_key = get_rank_cache_key(doc_type, item_id)
        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})
            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


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)


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 add_virtual_vote(cls, item_id, reg_id):
        reg_obj = FamousDoctorRegisterItem.objects.get(register_id=reg_id, item_id=item_id)
        reg_obj.final_count = F('final_count') + 1
        reg_obj.save()
