#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import, print_function

import datetime
import json
import calendar
import math
from collections import defaultdict
from operator import itemgetter
from django.db.models import Q, F
from django.db import transaction

from gm_types.error import ERROR as CODES
from gm_types.point.error import ERROR as POINT_CODES
from gm_types.point.enum import SIGN_ACTIVITY_EXCHANGE_GOODS_TYPE
from gm_types.user_hierarchy import EventType
from gm_types.gaia import SIGN_TYPE, SIGN_USER_ACTION

from api.models.sign_activity import (
    SignActivity,
    SignAllRecord,
    UserLastSignRecord,
    SignPrize,
    ActivityPrize,
    UserReceive,
    SignConfig,
    UserLastSign,
    SignActivityUserStatus,
)
from api.models.types import DOCTOR_TYPE, USER_TYPE
from api.models.doctor import Doctor
from api.tool.datetime_tool import get_timestamp_or_none
from api.tool.datetime_tool import get_timestamp_epoch
from api.tool.user_tool import get_user_from_context
from mimas.tasks import send_event_task
from rpc.cache import sign_cache
from rpc.decorators import (
    bind,
    bind_context,
)
from rpc.exceptions import GaiaRPCFaultException
from rpc.tool.error_code import gen
from rpc.tool.log_tool import logging_exception


def get_today_datetime():
    """
    获取今天零点的datetime时间
    :return:
    """
    today = datetime.datetime.now()
    today_zero = datetime.datetime.combine(datetime.date.today(), datetime.time())
    return today, today_zero


def get_current_activity(date_time):
    """
    获取当前时间段的活动
    :param date_time:
    :return:
    """
    activity_query = Q(start_time__lte=date_time, end_time__gte=date_time, is_online=True)
    current_activity = SignActivity.objects.filter(activity_query).first()
    return current_activity


def get_user_check_status(user_id, activity_id, date_time):
    """
    获取当前用户的签到状态
    :param user_id:
    :param activity_id:
    :param date_time:
    :return:
    """
    # 当前活动，该用户是否签到
    user_check_query = Q(user_id=user_id, activity_id=activity_id, sign_time__gte=date_time)
    is_checked_today = SignAllRecord.objects.filter(user_check_query).exists()
    return is_checked_today


def try_get_activity_good(activity_id, prize_id):
    # 奖品记录
    temp_prize = ActivityPrize.objects.filter(activity_id=activity_id, prize_id=prize_id).first()
    if not temp_prize:  # 奖品不存在
        return gen(POINT_CODES.POINT_ACTIVITY_GOODS_NOT_EXIST)

    return temp_prize


def product_share_code(user_id, activity_id):
    #生成分享码
    share_code = '{month}{user_id}{activity_id}{day}'.format(month=datetime.datetime.now().month,
                                                             user_id=user_id, activity_id=activity_id,
                                                             day=datetime.datetime.now().day)
    return share_code


def get_activity_detail_info(activity_id, only_need_good_info=False):
    """
    通过活动id，获取活动的详细数据
    :param activity_id:
    :param only_need_good_info:仅获取奖品信息
    :return:
    """
    result = {}

    try:
        activity = SignActivity.objects.get(pk=activity_id)
    except SignActivity.DoesNotExist:
        return gen(CODES.ACTIVITY_NOT_EXIST)

    activity_goods_list = ActivityPrize.objects.filter(activity_id=activity_id).values("prize_id", "cost", "count")
    good_ids = [item["prize_id"] for item in activity_goods_list]
    # 获取这些商品
    goods_list = SignPrize.objects.filter(pk__in=good_ids).values("id", "name", "short_name", "img_url", "price")
    good_info_dict = {good["id"]: good for good in goods_list}

    goods_info_list = []
    for item in sorted(activity_goods_list, key=itemgetter("cost")):    # 按照兑换等级排序
        _data = dict(item)
        good_id = item.pop("prize_id", 0)
        _data.update(good_info_dict.get(good_id, {}))
        goods_info_list.append(_data)

    result["activity_prizes"] = goods_info_list

    if only_need_good_info:
        return result

    result.update({
        "activity_id": activity.id,
        "activity_name": activity.name,
        "activity_rule": activity.rule,
    })

    return result


def try_exchange_activity_good(current_user, activity_id, prize_id, count):
    """
    尝试兑换活动商品
    :param current_user:
    :param activity_id:
    :param prize_id:
    :param count:
    :return:
    """
    result = {
        "goods_claim_result": 0,  # 商品兑换类型
    }

    temp_prize = try_get_activity_good(activity_id, prize_id)
    if not temp_prize.count:
        result["goods_claim_result"] = SIGN_ACTIVITY_EXCHANGE_GOODS_TYPE.CANNOT
        return result

    def _sign_activity_exception(code):
        raise GaiaRPCFaultException(
            error=code,
            message=SIGN_ACTIVITY_EXCHANGE_GOODS_TYPE.getDesc(code),
            data={}
        )

    try:
        with transaction.atomic():
            activity_prize = ActivityPrize.objects.select_for_update().get(activity_id=activity_id, prize_id=prize_id)
            current_prize_count = activity_prize.count  # 当前活动奖品库存
            current_prize_cost = activity_prize.cost  # 兑换条件

            # 用户记录
            try:
                user_sign_record = UserLastSign.objects.get(user_id=current_user.id,
                                                            activity_id=activity_id, available_days__gt=0)
            except UserLastSign.DoesNotExist:
                return _sign_activity_exception(SIGN_ACTIVITY_EXCHANGE_GOODS_TYPE.UN_STANDARD)
            # 用户可兑换判断  超过库存和余额不足这两种情况
            days = user_sign_record.available_days - current_prize_cost * count
            if days < 0:
                return _sign_activity_exception(SIGN_ACTIVITY_EXCHANGE_GOODS_TYPE.UN_STANDARD)
            if count > current_prize_count:
                return _sign_activity_exception(SIGN_ACTIVITY_EXCHANGE_GOODS_TYPE.OUT_NUMBER)
            user_sign_record.available_days -= current_prize_cost * count
            user_sign_record.save()
            # 准备减库存，更新用户连续签到记录及 记录用户领取信息
            activity_prize.count -= count
            activity_prize.save()

            UserReceive.objects.create(
                activity_id=activity_id,
                prize_id=prize_id,
                count=count,
                user_id=current_user.id,
                user_name=current_user.username,
                user_phone=current_user.person.phone,
                user_address=current_user.userextra.address,
            )
        result["goods_claim_result"] = SIGN_ACTIVITY_EXCHANGE_GOODS_TYPE.SUCCESS

    except Exception as e:
        logging_exception()
        if getattr(e, "error", 0) in SIGN_ACTIVITY_EXCHANGE_GOODS_TYPE:
            result["goods_claim_result"] = e.error
            return result
        return gen(CODES.ACTIVITY_EXCHANGE_FAIL)

    return result


@bind("api/user/check_config_switch")
def get_user_check_config():
    """
    仅用户获取签到入口的开关状态
    :return:
    """
    config = SignConfig.objects.last()
    return {
        "new_sign_switch": config.enter_switch if config else False,
        "new_sign_display_switch": config.float_window_switch if config else False,
    }


@bind("api/activity/detail")
def get_activity_detail(activity_id):
    """
    活动详情
    :param activity_id:
    :return:
    """
    return get_activity_detail_info(activity_id)


@bind_context("api/user/can_take_part_in_activity", login_required=True)
def user_can_take_part_in_activity(ctx, activity_id):
    """
    判断用户是否可以参加活动
    :param ctx:
    :return:
    """
    current_user = get_user_from_context(ctx)
    if not current_user or not activity_id:
        return gen(CODES.LOGIN_REQUIRED)

    if Doctor.objects.filter(user_id=current_user.id):
        return gen(CODES.IS_DOCTOR)  #机构号

    activity = get_current_activity(datetime.datetime.today())
    if not activity:
        return gen(CODES.CAMPAIGNPAGE_NOT_FOUND)  #活动过期

    days = 1
    try:
        last_sign = UserLastSign.objects.get(user_id=current_user.id, activity_id=activity_id)
        days = (datetime.date.today() - last_sign.newest_time).days
    except UserLastSign.DoesNotExist:
        return {'status': 0}

    if not days:
        return gen(CODES.HAS_SIGN)  #已签到

    return {'status': 0}


@bind_context("api/user/has_sign", login_required=True)
def user_has_sign(ctx):
    """
    判断用户是否已签到
    :param ctx:
    :return:
    """
    result = {
        "is_checked_today": False,
    }

    current_user = get_user_from_context(ctx)
    if not current_user:
        return gen(CODES.LOGIN_REQUIRED)

    today, today_zero = get_today_datetime()
    current_activity = get_current_activity(today)
    if not current_activity:
        return gen(CODES.ACTIVITY_CLOSED)

    is_checked_today = get_user_check_status(current_user.id, current_activity.id, today_zero)
    result["is_checked_today"] = is_checked_today

    return result


@bind_context('api/user/sign', login_required=True)
def user_sign_in(ctx, activity_id, is_reset=False):
    """
    用户签到/重置签到======更新log表  更新sign_record记录表
    :param ctx:
    :return:
    """
    current_user = get_user_from_context(ctx)
    if not current_user:
        return gen(CODES.LOGIN_REQUIRED)

    sign_type = SIGN_USER_ACTION.RESET_SIGN if is_reset else SIGN_USER_ACTION.TO_SIGN
    try:
        with transaction.atomic():
            last_record = UserLastSign.objects.select_for_update().get(activity_id=activity_id, user_id=current_user.id)
    except UserLastSign.DoesNotExist:
        try:
            UserLastSign.objects.create(activity_id=activity_id, user_id=current_user.id,
                                        newest_time=datetime.date.today(),
                                        share_code=product_share_code(user_id=current_user.id, activity_id=activity_id))
            SignAllRecord.objects.create(activity_id=activity_id, user_id=current_user.id,
                                         sign_time=datetime.datetime.now(), sign_type=SIGN_USER_ACTION.TO_SIGN)
        except:
            logging_exception()
            return gen(CODES.HAS_SIGN)
    except:
        return gen(CODES.HAS_SIGN)
    else:
        if last_record.newest_time == datetime.date.today():
            return gen(CODES.HAS_SIGN)
        last_record.days_count = (last_record.days_count + 1) if not is_reset else 1
        last_record.available_days = (last_record.available_days + 1) if not is_reset else 1
        last_record.share_code = product_share_code(user_id=current_user.id, activity_id=activity_id)\
                                    if is_reset else last_record.share_code
        last_record.newest_time = datetime.date.today()
        last_record.add_sign_number = 0 if is_reset else last_record.add_sign_number
        last_record.save()

        SignAllRecord.objects.create(activity_id=activity_id, user_id=current_user.id,
                                     sign_time=datetime.datetime.now(), sign_type=sign_type)
    send_event_task(user_id=current_user.id, event_type=EventType.CHECKIN)
    return {}


@bind('api/user/add_sign')
def user_add_sign(user_id=None, activity_id=None, share_code=None, need_invite=True):
    """用户补签"""
    if not activity_id:
        activity = SignActivity.objects.filter(is_online=True, start_time__lte=datetime.datetime.now(),
                                               end_time__gte=datetime.datetime.now()).first()
        activity_id = activity.id
        if not activity_id:
            return gen(CODES.CAMPAIGN_NOT_FOUND)

    query = {"activity_id": activity_id}
    if share_code:
        query.update({"share_code": share_code})

    if user_id:
        query.update({"user_id": user_id})

    try:
        last_sign = UserLastSign.objects.get(**query)
    except UserLastSign.DoesNotExist:
        return gen(CODES.ADD_SIGN_STATUS_ERROR)

    if (datetime.date.today() - last_sign.newest_time).days <= 1:
        return gen(CODES.ADD_SIGN_STATUS_ERROR)

    if not need_invite and last_sign.add_sign_number >= 2:
        return gen(CODES.SIGN_SUCCESS_AND_STATUS_ERROR)

    with transaction.atomic():
        last_sign.newest_time += datetime.timedelta(days=1)
        last_sign.available_days += 1
        last_sign.days_count += 1
        last_sign.add_sign_number += 1
        last_sign.save()

        sign_time = datetime.datetime(year=last_sign.newest_time.year, month=last_sign.newest_time.month, day=last_sign.newest_time.day)
        SignAllRecord.objects.create(activity_id=activity_id, user_id=user_id,
                                     sign_time=sign_time, sign_type=SIGN_USER_ACTION.ADD_SIGN)

    return {}


@bind('api/sign/detail')
def get_detail(user_id):
    activity = SignActivity.objects.filter(is_online=True, start_time__lte=datetime.datetime.now(),
                                           end_time__gte=datetime.datetime.now()).first()
    is_overdue = False   #用户没参加过的活动结束
    if not activity:
        activity = SignActivity.objects.filter(is_online=True, end_time__lte=datetime.datetime.now()).order_by(
            '-end_time').first()
        is_overdue = True
    if not activity:
        return gen(CODES.ACTIVITY_NOT_EXIST)

    activity_data = get_activity_detail_info(activity_id=activity.id)

    to_days = (datetime.datetime.date(activity.end_time) - datetime.date.today()).days
    if not user_id:
        """用户不登录也可以访问页面--只限签到详情页"""
        result = {
            "end_time": get_timestamp_epoch(activity.end_time),
            "start_time": get_timestamp_epoch(activity.start_time),
            "is_remind": False,
            "available_days": 0,
            "total_days": to_days,
            "space_days": 0,
            "add_sign_number": 0,
            "share_code": '',
        }
        result.update(activity_data)
        return result

    #是否签过到
    try:
        last_sign = UserLastSign.objects.get(activity_id=activity.id, user_id=user_id)
    except UserLastSign.DoesNotExist:
        if is_overdue:
            return gen(CODES.CAMPAIGNPAGE_NOT_FOUND)
        available_days, last_sign_time, share_code, add_sign_number =\
            0, datetime.date.today() - datetime.timedelta(days=1), '', 0
    else:
        if not last_sign.newest_time:
            last_sign.newest_time = datetime.date.today() - datetime.timedelta(days=1)  #老数据newest数据（时间）是空
            last_sign.save()
        if not last_sign.share_code:
            last_sign.share_code = product_share_code(activity_id=activity.id, user_id=user_id)  #针对轮次生成分享码
            last_sign.save()
        available_days, last_sign_time, share_code, add_sign_number =\
            last_sign.available_days, last_sign.newest_time, last_sign.share_code, last_sign.add_sign_number

    #用户签到状态
    try:
        user_status = SignActivityUserStatus.objects.get(activity_id=activity.id, user_id=user_id)
    except SignActivityUserStatus.DoesNotExist:
        SignActivityUserStatus.objects.create(activity_id=activity.id, user_id=user_id, add_sign_number=0)
        is_remind = False
    else:
        is_remind = user_status.is_remind

    total_days = available_days + (datetime.datetime.date(activity.end_time) - last_sign_time).days

    space_days = (datetime.date.today() - last_sign_time).days

    result = {
        "end_time": get_timestamp_epoch(activity.end_time),
        "start_time": get_timestamp_epoch(activity.start_time),
        "is_remind": is_remind,
        "available_days": available_days,
        "total_days": int(total_days),
        "space_days": space_days - 1,
        "add_sign_number": add_sign_number,
        "share_code": share_code,
    }
    result.update(activity_data)

    return result


@bind_context('api/sign/exchange', login_required=True)
def exchanged_prize(ctx, activity_id, prize_id, count):

    current_user = get_user_from_context(ctx)
    if not current_user:
        return gen(CODES.LOGIN_REQUIRED)

    try:
        _ = SignActivity.objects.get(pk=activity_id)
    except SignActivity.DoesNotExist:
        return gen(CODES.ACTIVITY_NOT_EXIST)

    goods_claim_result = try_exchange_activity_good(current_user, activity_id, prize_id, count)

    return goods_claim_result


@bind('api/sign/get_guide_window')
def get_sign_guide(user_id):
    #has_window-----和活动无关
    sign_status = SignActivityUserStatus.objects.filter(user_id=user_id, has_window=False)
    has_window = False if sign_status else True
    activity = SignActivity.objects.first()
    if activity and not sign_status:
        SignActivityUserStatus.objects.update_or_create(activity_id=activity.id, user_id=user_id,
                                                        defaults={"has_window": False})

    return {"has_window": has_window}


@bind('api/sign/update_guide_window')
def update_sign_guide(user_id):
    #更新has_window的状态-----和活动无关
    SignActivityUserStatus.objects.filter(user_id=user_id).update(has_window=False)


@bind('api/sign/update_user_status')
def update_user_status(user_id, activity_id, updates):
    #更新签到活动的status
    try:
        SignActivityUserStatus.objects.update_or_create(user_id=user_id, activity_id=activity_id,
                                                        defaults=updates)
    except:
        logging_exception()


@bind('api/sign/get_icon')
def get_check_icon(user_id):
    # 根据用户状态展示不同的icon
    if not user_id:
        return {'sign_icon': ''}

    config = SignConfig.objects.first()

    activity = SignActivity.objects.filter(is_online=True, start_time__lte=datetime.datetime.now(),
                                           end_time__gte=datetime.datetime.now()).first()
    if not activity:
        return {'sign_icon': config.routine_icon or ''}   #签到icon

    try:
        last_sign = UserLastSign.objects.get(activity_id=activity.id, user_id=user_id)
    except UserLastSign.DoesNotExist:
        return {'sign_icon': config.routine_icon or ''}   # 签到icon

    if last_sign.newest_time:
        days = (datetime.date.today() - last_sign.newest_time).days
        if days >= 2:
            return {'sign_icon': config.space_icon or ''}                #断签icon

    return {'sign_icon': config.routine_icon or ''}       #签到icon


@bind('api/sign/cash_record')
def get_cash_record(user_id):
    if not user_id:
        return {'records': []}

    receives = UserReceive.objects.filter(user_id=user_id).order_by('-created_time').values('express_no', 'prize_id', 'created_time')

    if not receives:
        return {'records': []}

    prize_ids, records = [], []
    for receives_record in receives:
        prize_ids.append(receives_record.get('prize_id'))
        records.append({
            'express_no': receives_record.get('express_no') if receives_record.get('express_no') else '暂未发货',
            'exchanged_time': get_timestamp_epoch(receives_record.get('created_time')) * 1000,
            'prize_id': receives_record.get('prize_id'),
        })

    prize_ids, prize_detail = list(set(prize_ids)), {}
    prizes = SignPrize.objects.filter(id__in=prize_ids).values('id', 'short_name', 'img_url')
    for prize in prizes:
        prize_detail[prize.get('id')] = {
            'name': prize.get('short_name'),
            'img_url': prize.get('img_url'),
        }

    for record in records:
        prize_info = prize_detail.get(record.get('prize_id'))
        record.update(prize_info)

    return {'records': records}
