# coding:utf-8
"""名医大赏service"""

import StringIO
from datetime import datetime

from gm_types.error import ERROR as ERROR_CODES
from gm_types.gaia import FAMOUS_DOCTOR_JOIN_TYPE, DOCTOR_TYPE, POINTS_TYPE
from gm_upload import upload, IMG_TYPE
from rpc.tool.error_code import gen, CODES
from api.tool.user_tool import get_user_by_id
from point.models import PointsInfo

from api.models.activities import FamousDoctorRegisterItem
from api.models.activities import FamousDoctorsItemVote
from api.models.activities import FamousDoctorsRegistrationer
from api.models.activities import FamousDoctorsAppend
from api.models.activities import FamousDoctorsCanvass
from api.models.activities import FamousDoctorsReward
from api.tool.image_utils import get_full_path, get_w_path
from api.util.wechat_util import create_wx_qarcode
from hippo.models import Hospital, Doctor
from rpc.cache import famous_doctors_cache
from rpc.tool.error_code import gen, CODES
from rpc.tool.log_tool import logging_exception
from .famous_doctors_conf import list_items, get_items_dict_info, famous_doctors_conf_map


class FamousDoctorService(object):
    vote_sum = 20  # 一日投票总票数
    vote_item_sum = 5  # 单一项目投票票数
    vote_info_cache_key = "famous_doctors:uv:{day}:{is_app}:{user_id}"
    vote_item_info_cache_key = "famous_doctors:uv:{day}:{doc_type}:{doc_id}:{item_id}:{is_app}:{user_id}"
    reward_cache_key = "famous_doctors:reward:{version}"

    @classmethod
    def list_docs_info(cls, doc_type, doc_ids):

        docs_info = {}

        if doc_type == FAMOUS_DOCTOR_JOIN_TYPE.DOCTOR:
            doctors = Doctor.objects.filter(pk__in=doc_ids).select_related("hospital")
            for doctor in doctors:
                docs_info[doctor.id] = {
                    'id': doctor.id,
                    'name': doctor.name,
                    'portrait': doctor.portrait,
                    'hospital_id': doctor.hospital_id,
                    'hospital_name': doctor.hospital.name,
                    'doctor_type': doctor.doctor_type,
                    'hospital_portrait': doctor.hospital.portrait,
                    'good_at': doctor.good_at or "",
                    'title': doctor.title
                }

        elif doc_type == FAMOUS_DOCTOR_JOIN_TYPE.HOSPITAL:
            doctors = Doctor.objects.filter(doctor_type=DOCTOR_TYPE.OFFICER, hospital_id__in=doc_ids)

            docs_dict = {}
            for doctor in doctors:
                docs_dict[doctor.hospital_id] = {
                    'id': doctor.id,
                    'name': doctor.name,
                    'portrait': doctor.portrait,
                    'doctor_type': doctor.doctor_type,
                    'good_at': doctor.good_at or "",
                    'title': doctor.title,
                }

            hospitals = Hospital.objects.filter(pk__in=doc_ids)
            for hospital in hospitals:

                officer = hospital.officer
                if officer and officer.portrait:
                    portrait = get_full_path(officer.portrait)
                elif hospital.portrait:
                    portrait = get_full_path(hospital.portrait)
                else:
                    portrait = get_w_path(u'img%2Fyiyuan.png')
                docs_info[hospital.id] = {
                    'id': hospital.id,
                    'name': hospital.name,
                    'doctor_type': hospital.hospital_type,
                    'portrait': portrait,
                    'good_at': docs_dict.get(hospital.id, {}).get('good_at', "")
                }

        return docs_info

    @classmethod
    def is_append(cls, name):
        """判断医生是否入驻过"""
        return Doctor.objects.filter(name=name).exists()

    @classmethod
    def is_appendin(cls, name):
        """当医生通过报名入驻的时候，再次点击入驻，判断医生是否入驻"""
        return FamousDoctorsAppend.is_appendin(name)

    @classmethod
    def get_items_dict_info(cls):
        return get_items_dict_info()

    @classmethod
    def list_doctor_item_ids(cls, doc_type, doc_id, version):
        """获取用户参加的项目列表"""

        registrationer = cls.get_registrationer_by_doc_id(doc_type, doc_id, version)
        if not registrationer:
            return []
        item_ids = FamousDoctorRegisterItem.objects.filter(register_id=registrationer.id).values_list("item_id",
                                                                                                      flat=True)
        return list(item_ids)

    @classmethod
    def list_items(cls):
        return list_items()

    @classmethod
    def get_registrationer_by_doc_id(cls, doc_type, doc_id, version):
        f = FamousDoctorsRegistrationer.objects.filter(doctor_type=doc_type, doctor_id=doc_id, version=version)
        return f[0] if f else None

    @classmethod
    def get_registrationer_by_id(cls, reg_id):
        try:
            return FamousDoctorsRegistrationer.objects.get(id=reg_id)
        except FamousDoctorsRegistrationer.DoesNotExist:
            logging_exception()
            return None

    @classmethod
    def join(cls, doc_type, doc_id, item_ids, version=None):
        FamousDoctorsRegistrationer.join(doc_id, doc_type, item_ids, version)

    @classmethod
    def clear_record(cls, doc_type, doc_id):
        FamousDoctorsRegistrationer.remove_record(doc_type, doc_id)

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

        return FamousDoctorsRegistrationer.is_joined(doc_type, doc_id, version)

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

        FamousDoctorsRegistrationer.init_rank(doc_type, doc_id, item_ids)

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

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

        FamousDoctorsRegistrationer.init_rank_cache()

    @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票
        '''
        vote_cnt, rank, gap = FamousDoctorsRegistrationer.vote_rank_and_gap(doc_type, doc_id, item_id, version)

        return vote_cnt, rank, gap

    @classmethod
    def list_rank_info(cls, doc_type, item_ids=None, offset=None, limit=None):
        """
        获取排行信息
        return {'{item_id}': [doc_id1, doc_id2...]}
        """

        return FamousDoctorsRegistrationer.list_rank_info(doc_type, item_ids, offset, limit=limit)

    @classmethod
    def list_rank_info_by_item_id(cls, doc_type, item_id, version=None, offset=0, cnt=None):
        """获取排名医生或者医院 id 列表。
        @return: list: [{"doc_id": "121212", "vote_cnt": 1}, ...]
        """
        res = []
        if not item_id:
            if doc_type == '0':
                item_ids = ["4", "5", "6", "7", "8", "9"]
                for item_it in item_ids:
                    if cnt in [None, -1]:
                        limit = -1
                    else:
                        limit = offset + cnt
                    ret = FamousDoctorsRegistrationer.list_rank_info_by_item_id(doc_type, item_it, version, offset,
                                                                                limit=limit)
                    res += ret
                return res
            elif doc_type == '1':
                item_ids = ["1", "2", "3"]
                for item_it in item_ids:
                    if cnt in [None, -1]:
                        limit = -1
                    else:
                        limit = offset + cnt
                    ret = FamousDoctorsRegistrationer.list_rank_info_by_item_id(doc_type, item_it, version, offset,
                                                                                limit=limit)
                    res += ret
                return res
        else:
            if cnt in [None, -1]:
                limit = -1
            else:
                limit = offset + cnt
            return FamousDoctorsRegistrationer.list_rank_info_by_item_id(doc_type, item_id, version, offset,
                                                                         limit=limit)

    ##### 投票相关 #####

    @classmethod
    def vote(cls, user_id, item_id, reg_id, platform):
        """投票"""
        return FamousDoctorsItemVote.do_vote(user_id, item_id, reg_id, platform)

    @classmethod
    def Two_or_Five_vote(cls, item_id, reg_id, cnt):
        """每隔2分钟或者5分钟添加票数"""
        return FamousDoctorsItemVote.Two_or_Five_do_vote(item_id, reg_id, cnt)

    ##### 用户票数相关 #####

    @classmethod
    def user_vote_info(cls, user_id, doc_type, doc_id, item_id, is_app=True):
        """用户票数信息"""
        today = str(datetime.today().date())
        key = cls.vote_info_cache_key.format(day=today, is_app=int(is_app), user_id=user_id)
        item_key = cls.vote_item_info_cache_key.format(
            day=today, item_id=item_id, is_app=int(is_app),
            user_id=user_id, doc_type=doc_type, doc_id=doc_id,
        )

        used_vote_cnt = famous_doctors_cache.get(key)
        used_vote_cnt = int(used_vote_cnt) if used_vote_cnt else 0
        used_vote_item_cnt = famous_doctors_cache.get(item_key)
        used_vote_item_cnt = int(used_vote_item_cnt) if used_vote_item_cnt else 0
        return {
            "vote_sum": cls.vote_sum,
            "vote_item_sum": cls.vote_item_sum,
            "used_vote_cnt": used_vote_cnt,
            "used_vote_item_cnt": used_vote_item_cnt,
        }

    @classmethod
    def get_user_day_item_count(cls, doc_id, is_app, user_id, doc_type, version):
        """用来判断一天一个人只能给一个医生或者机构投5票"""
        reg_id = FamousDoctorsRegistrationer.get_reg_id_by_doc_id(doc_id=doc_id, version=version)

        item_result = FamousDoctorRegisterItem.get_items(reg_id=reg_id)
        today = str(datetime.today().date())
        sum = 0
        for item in item_result:
            item_key = cls.vote_item_info_cache_key.format(
                day=today, item_id=item.get('item_id'), is_app=int(is_app),
                user_id=user_id, doc_type=doc_type, doc_id=doc_id)
            if item_key:
                used_vote_item_cnt = famous_doctors_cache.get(item_key)
                used_vote_item_cnt = int(used_vote_item_cnt) if used_vote_item_cnt else 0
                sum = sum + used_vote_item_cnt

        return sum

    @classmethod
    def incr_used_vote_cnt(cls, doc_type, doc_id, user_id, item_id, is_app=True):

        today = str(datetime.today().date())
        key = cls.vote_info_cache_key.format(day=today, is_app=int(is_app), user_id=user_id)
        item_key = cls.vote_item_info_cache_key.format(
            day=today, doc_type=doc_type, doc_id=doc_id, item_id=item_id, is_app=int(is_app), user_id=user_id
        )

        cnt = famous_doctors_cache.incr(key)
        if cnt == 1:
            famous_doctors_cache.expire(key, 24 * 60 * 60)

        item_cnt = famous_doctors_cache.incr(item_key)
        if item_cnt == 1:
            famous_doctors_cache.expire(item_key, 24 * 60 * 60)

        return cnt, item_cnt

    @classmethod
    def add_reward(cls, user_id, reward_type, create_time, reward, version=None):
        """保存用户获奖记录"""
        FamousDoctorsReward.add_reward(user_id=user_id, reward_type=reward_type, create_time=create_time, reward=reward,
                                       version=version)

    @classmethod
    def find_reward(cls, user_id, create_time):
        """判断用户今天是否领过奖"""
        return FamousDoctorsReward.find_reward(user_id=user_id, create_time=create_time)

    @classmethod
    def count_type(cls, user_id):
        """获得用户连续多少天领奖"""
        count = FamousDoctorsReward.count_type(user_id=user_id)
        return count

    @classmethod
    def send_prize(cls, user_id, reason, prize):
        """用户投票获得美分"""
        if reason not in POINTS_TYPE:
            return gen(CODES.OPERATION_NOT_SUPPORTED)
        user = get_user_by_id(user_id)
        pi = PointsInfo(user)
        points = pi.points_operate_common(reason=reason, num=prize)
        return points

    @classmethod
    def init_reward(cls, version):
        """活动领奖美分初始化"""
        reward_key = cls.reward_cache_key.format(
            version=version
        )
        reward_cnt = famous_doctors_cache.get(reward_key)
        reward_cnt = int(reward_cnt) if reward_cnt else 0
        return {"reward_cnt": reward_cnt}

    @classmethod
    def incr_reward(cls, version, cnt):
        """活动结束领奖美分增加"""
        reward_key = cls.reward_cache_key.format(
            version=version
        )
        result = famous_doctors_cache.incr(reward_key, cnt)
        return result

    @classmethod
    def create_wx_qr(cls, doc_type, doc_id, reg_id):

        qr_key = "famous_doctor:qr_code:{doc_type}:{doc_id}".format(doc_id=doc_id, doc_type=doc_type)
        qr_url = famous_doctors_cache.get(qr_key)
        if qr_url:
            return qr_url

        path = "packageActivity/pages/canvassing/main"
        scene = "id={reg_id}&type={doc_type}".format(reg_id=reg_id, doc_type=doc_type)

        res = create_wx_qarcode(path, scene)
        if res["errcode"] != 0:
            return ""

        wx_qr = res["buffer"]
        save_name = "famous_doctors/" + "{doc_type}_{doc_id}_qr.png".format(doc_type=doc_type, doc_id=doc_id)
        img = StringIO.StringIO(wx_qr)
        try:
            url = upload(img, IMG_TYPE.NOWATERMARK, save_name=save_name)
            famous_doctors_cache.set(qr_key, url)
            return url
        except:
            logging_exception()
            return ""

    @classmethod
    def get_doc_id_by_res_id(cls, res_id, version=None):
        """根据res_id获取医生的doctor_id"""
        doc_id = FamousDoctorsRegistrationer.get_doc_id_by_res_id(res_id=res_id, version=version)
        return doc_id

    @classmethod
    def get_all_doctors_list(cls, version):
        """获取报名医生的全部doctor_ids"""
        doctor_ids_and_types = FamousDoctorsRegistrationer.get_all_doctors_list(version)
        return doctor_ids_and_types

    @classmethod
    def get_reg_id_by_doc_id(cls, doc_id, version):
        """通过doc_id获得reg_id"""
        reg_id = FamousDoctorsRegistrationer.get_reg_id_by_doc_id(doc_id=doc_id, version=version)
        return reg_id

    @classmethod
    def get_items(cls, reg_id):
        """
        后台系统获取项目id及票数
        """
        items_rsult = FamousDoctorRegisterItem.get_items(reg_id=reg_id)
        return items_rsult

    @classmethod
    def get_regids_by_user_id(cls, user_id):
        """根据user_id查询regids"""
        reg_ids = FamousDoctorsItemVote.get_regids_by_user_id(user_id=user_id)
        return reg_ids

    @classmethod
    def famous_doctors_append(cls, name, tel_phone, address, version):
        """机构入驻"""

        FamousDoctorsAppend.famous_doctors_append(name=name, tel_phone=tel_phone, address=address, version=version)

    @classmethod
    def famous_doctors_find(cls, version):
        famous_doctors_list = FamousDoctorsAppend.famous_doctors_find(version=version)
        return famous_doctors_list

    @classmethod
    def get_vote_count(cls, reg_id, item_id):
        """获取医生当前票数"""
        vote_count = FamousDoctorRegisterItem.get_vote_count(reg_id=reg_id, item_id=item_id)
        return vote_count

    @classmethod
    def update_vote_count(cls, reg_id, item_id, count):
        """改票"""
        FamousDoctorRegisterItem.update_vote_count(reg_id=reg_id, item_id=item_id, count=count)

    @classmethod
    def add_user(cls, user_id, reg_id, item_id, add_num):
        """记录谁为谁改了票"""
        FamousDoctorsCanvass.add_user(user_id=user_id, reg_id=reg_id, item_id=item_id, add_num=add_num)

    @classmethod
    def user_get_reward(cls, user_id, count, reason, version):
        """判断用户是否已经领过最后奖项"""
        reward_count = cls.init_reward(version=version)
        reward_count = reward_count.get('reward_cnt')
        cents = famous_doctors_conf_map.get('202010', None)
        cents = cents.get('cents')
        d_value = cents - reward_count
        award_time = datetime.strptime(famous_doctors_conf_map.get(version).get("award_time"),
                                       '%Y-%m-%d %H:%M:%S')
        c = "2099-09-09 00:00:00"
        end_time = famous_doctors_conf_map.get(version).get('award_end_time') if famous_doctors_conf_map.get(
            version).get('award_end_time') else c
        award_end_time = datetime.strptime(end_time,
                                           '%Y-%m-%d %H:%M:%S')
        is_get_reward = FamousDoctorsReward.user_get_reward(user_id=user_id, award_time=award_time,
                                                            award_end_time=award_end_time, version=version)
        if is_get_reward:
            gen(ERROR_CODES.REWARD_SAME)
        if d_value < 100:
            return gen(ERROR_CODES.REWARD_MONEY_OUT)
        elif count > d_value:
            count = d_value
            points = cls.get_cents(user_id, count, reason, version)
            return points
        else:
            points = cls.get_cents(user_id, count, reason, version)
            return points

    @classmethod
    def get_cents(cls, user_id, count, reason, version):
        """用户领奖获取美分"""
        create_time = datetime.now()
        cls.add_reward(user_id=user_id, reward_type=0, create_time=create_time, reward='入围医生领奖' + str(count) + '美分',
                       version=version)

        points = cls.send_prize(user_id, reason, count)
        cents = cls.incr_reward(version, count)
        return points

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


def version_control(version=None, key=None):
    """
    名医大赏版本控制器
    version: 版本
    key: 配置键
    return: 配置字典 或者 单一配置内容 可为NONE
    """
    conf = {}
    if not version:
        return None, conf
    else:
        conf = famous_doctors_conf_map.get(version)
        if not key:
            pass
        else:
            conf = conf.get(key) if conf else None
            version = version if conf else None
        return version, conf


def version_control_decorator(func):
    """
    名医大赏版本控制器装饰器
    """

    def wrapper(*arg, **kwargs):
        version = kwargs.get("version")  # version必为定值传参
        v, conf = version_control(version)
        if isinstance(conf, dict):
            pass
            return func(*arg, **kwargs)
        else:
            return gen(CODES.PARAMS_INVALID)

    return wrapper


def sign_valid_decorator(func):
    """
    名医大赏报名时间装饰器
    """

    def wrapper(*args, **kwargs):
        version = kwargs.get("version")  # version必为定值传参
        if not version:
            return func(*args, **kwargs)
        sign_start_time = datetime.strptime(famous_doctors_conf_map.get(version).get("sign_start_time"),
                                            '%Y-%m-%d %H:%M:%S')
        sign_end_time = datetime.strptime(famous_doctors_conf_map.get(version).get("sign_end_time"),
                                          '%Y-%m-%d %H:%M:%S')
        # 当前时间
        now_time = datetime.now()
        # 判断当前时间是否在范围时间内
        if now_time < sign_start_time:
            valid_flag = False
            valid_info = '还没到报名开始时间，报名时间为{}--{}'.format(famous_doctors_conf_map.get(version).get("sign_start_time"),
                                                        famous_doctors_conf_map.get(version).get("sign_end_time"))
            valid_tag = 1
            return gen(ERROR_CODES.BEFORE_VALID_SIGN_TIME)
        elif sign_start_time <= now_time <= sign_end_time:
            valid_flag = True
            valid_info = '可以报名啦!'
            valid_tag = 2
            return func(*args, **kwargs)
        else:
            valid_flag = False
            valid_info = '报名已结束，感谢您的关注。'
            valid_tag = 3
            return gen(ERROR_CODES.AFTER_VALID_SIGN_TIME)

    return wrapper


def vote_valid_decorator(func):
    """
    名医大赏投票时间装饰器
    """

    def wrapper(*args, **kwargs):
        version = kwargs.get("version")  # version必为定值传参
        if not version:
            return func(*args, **kwargs)
        vote_start_time = datetime.strptime(famous_doctors_conf_map.get(version).get("vote_start_time"),
                                            '%Y-%m-%d %H:%M:%S')
        vote_end_time = datetime.strptime(famous_doctors_conf_map.get(version).get("vote_end_time"),
                                          '%Y-%m-%d %H:%M:%S')
        # 当前时间
        now_time = datetime.now()
        # 判断当前时间是否在范围时间内
        if now_time < vote_start_time:
            valid_flag = False
            valid_info = '还没到投票开始时间，投票时间为{}--{}'.format(famous_doctors_conf_map.get(version).get("vote_start_time"),
                                                        famous_doctors_conf_map.get(version).get("vote_end_time"))
            valid_tag = 1
            return gen(ERROR_CODES.BEFORE_VALID_VOTE_TIME)
        elif vote_start_time <= now_time <= vote_end_time:
            valid_flag = True
            valid_info = '可以投票啦!'
            valid_tag = 2
            return func(*args, **kwargs)
        else:
            valid_flag = False
            valid_info = '投票已结束，感谢您的关注。'
            valid_tag = 3
            return gen(ERROR_CODES.AFTER_VALID_VOTE_TIME)

    return wrapper


def award_valid_decorator(func):
    """
    颁奖时间装饰器
    """

    def wrapper(*args, **kwargs):
        version = kwargs.get("version")  # version必为定值传参
        if not version:
            return func(*args, **kwargs)
        award_time = datetime.strptime(famous_doctors_conf_map.get(version).get("award_time"),
                                       '%Y-%m-%d %H:%M:%S')
        c = "2099-09-09 00:00:00"
        end_time = famous_doctors_conf_map.get(version).get('award_end_time') if famous_doctors_conf_map.get(
            version).get('award_end_time') else c
        award_end_time = datetime.strptime(end_time,
                                           '%Y-%m-%d %H:%M:%S')
        # 当前时间
        now_time = datetime.now()

        # 判断当前时间是否在范围时间内
        if now_time < award_time:
            valid_flag = False
            valid_info = '还没到颁奖开始时间，颁奖时间为{}'.format(famous_doctors_conf_map.get(version).get("award_time"))
            valid_tag = 1
            return gen(ERROR_CODES.BEFORE_VALID_AWARD_TIME)
        elif now_time > award_end_time:
            valid_flag = False
            valid_info = '颁奖时间已过'
            valid_tag = 2
            return gen(ERROR_CODES.REWARD_TIME_OUT)
        else:
            valid_flag = True
            valid_info = '可以领奖啦！'
            valid_tag = 4
            return func(*args, **kwargs)

    return wrapper
