# coding=utf-8

from __future__ import unicode_literals, absolute_import
import datetime

from django.contrib.auth.models import User
from django.db import transaction
from django.db.models import Q
from django.db import models

from gm_types.gaia import (
    POINTS_OPERATION,
    POINTS_TYPE,
    TASK_TYPE,
)

from rpc.tool.dict_mixin import to_dict
from talos.models.diary import Diary


class Points(models.Model):
    class Meta:
        verbose_name = u'积分'
        verbose_name_plural = u'积分'
        db_table = 'api_points'
        app_label = 'api'

    user = models.ForeignKey(User, verbose_name=u'用户', related_name='user_points', db_index=True)
    operation = models.CharField(max_length=10, choices=POINTS_OPERATION, null=False, default=POINTS_OPERATION.ADD,
                                 verbose_name=u'操作', db_index=True)
    reason = models.CharField(max_length=2, choices=POINTS_TYPE, null=False, default=POINTS_TYPE.INVITE,
                              verbose_name=u'原因', db_index=True)
    number = models.IntegerField(default=0, verbose_name=u'积分')
    link_topic_id = models.IntegerField(verbose_name=u'指定的话题', null=True, blank=True)
    link_reply_id = models.IntegerField(verbose_name=u'关联的回复', null=True, blank=True)
    link_user = models.ForeignKey(User, verbose_name=u'关联的用户', null=True, blank=True)
    link_order_id = models.IntegerField(verbose_name=u'关联的订单', null=True, blank=True)
    add_time = models.DateTimeField(verbose_name=u'时间', auto_now_add=True, db_index=True)

    @property
    def link_order(self):
        # TODO: delete this import, replaced with api call
        from api.models.order import Order
        return Order.objects.get(id=self.link_order_id)

    def to_dict(self):
        data = to_dict(
            self,
            fields=[
                'operation',
                'reason',
                'number',
                'link_topic',
                'link_reply',
                'link_user',
                'link_order_id',
                'add_time'
            ],
        )
        return data


class PointsTask(models.Model):
    class Meta:
        verbose_name = u'美分任务'
        verbose_name_plural = u'美分任务'
        db_table = 'api_points_task'
        app_label = 'api'

    point_type = models.CharField(max_length=1, choices=POINTS_TYPE, null=False,
                                  default=POINTS_TYPE.INVITE, verbose_name=u'美分类型', unique=True)
    number = models.IntegerField(default=0, verbose_name=u'积分')
    is_online = models.BooleanField(default=False, help_text=u"是否上线", verbose_name=u"上线")
    title = models.CharField(verbose_name=u'标题', max_length=128, default='')
    description = models.CharField(verbose_name=u'描述', max_length=128, default='')
    task_type = models.CharField(
        max_length=1, choices=POINTS_TYPE, null=False,
        default=TASK_TYPE.DEFAULT, verbose_name=u'任务类型'
    )
    ordering = models.IntegerField(default=20, verbose_name=u"展示顺序", help_text=u"小的排在前，大的排在后")
    url = models.CharField(max_length=128, verbose_name=u'跳转地址')
    times = models.IntegerField(default=0, verbose_name=u"完成次数")
    is_limited = models.BooleanField(default=True, help_text=u"是否受限", verbose_name=u"是否受限")

    def to_dict(self):
        data = to_dict(
            self,
            fields=['id', 'point_type', 'number',
                    'is_online', 'title', 'description',
                    'task_type', 'url', 'times',
                    'is_limited'],
        )
        return data


class PointsInfo(object):

    cache_key = 'point_task_cache'
    user = None
    today_limit = 500
    points_task = []

    def __init__(self, user):
        if isinstance(user, (int, long)):
            user = User.objects.get(id=user)

        else:
            user= User.objects.get(id=user.id)

        if not isinstance(user, User):
            raise User.DoesNotExist

        self.user = user

    def _get_points_task(self):
        all_task = PointsTask.objects.filter(is_online=True).order_by('ordering')
        online_task = [points.to_dict() for points in all_task]
        return online_task

    def _get_usable_operation(self):
        sign_in = {
            'point_type': POINTS_TYPE.SIGN_IN,
            'number': 0,
            'task_type': '1',
            'times': 1,
            'is_limited': 1,
        }
        buy_service = {
            'point_type': POINTS_TYPE.BUY_SERVICE,
            'number': 0,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        cancel_order = {
            'point_type': POINTS_TYPE.CANCEL_ORDER,
            'number': 0,
            'task_type': '0',
            'times': 0,
            'is_limited': 0,
        }
        join_activity = {
            'point_type': POINTS_TYPE.JOIN_ACTIVITY,
            'number': 0,
            'task_type': '0',
            'times': 0,
            'is_limited': 0,
        }
        download_app = {
            'point_type': POINTS_TYPE.APP,
            'number': 200,
            'task_type': '0',
            'times': 0,
            'is_limited': 1,
        }
        pay = {
            'point_type': POINTS_TYPE.PAY,
            'number': 0,
            'task_type': '0',
            'times': 0,
            'is_limited': 0,
        }
        level_upper = {  # 等级升级，奖励
            'point_type': POINTS_TYPE.LEVEL_UPPER,
            'number': 200,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        reply_others = {  # replace POINTS_TYPE.REPLY_OTHER
            'point_type': POINTS_TYPE.REPLY_OTHERS,
            'number': 5,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        reply_others_multi = {
            'point_type': POINTS_TYPE.REPLY_OTHERS_MULTI_TIMES,
            'number': 30,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        answer_question = {
            'point_type': POINTS_TYPE.ANSWER_QUESTION,
            'number': 20,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        answer_question_multi = {
            'point_type': POINTS_TYPE.ANSWER_MULTI_TIMES,
            'number': 30,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        publish_topic = {
            'point_type': POINTS_TYPE.PUBLISH_TOPIC,
            'number': 30,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        vote_multi = {
            'point_type': POINTS_TYPE.VOTE_MULTI_TIMES,
            'number': 20,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        follow_multi = {
            'point_type': POINTS_TYPE.FOLLOW_MULTI_TIMES,
            'number': 30,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        recommend_problem = {
            'point_type': POINTS_TYPE.CHOSEN_DIARY,     # 日记被推荐
            'number': 50,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        recommend_answer = {
            'point_type': POINTS_TYPE.ANSWER_RECOMMEND,     # 回答被推荐
            'number': 50,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        registe_at_first = {
            'point_type': POINTS_TYPE.REGISTER,  # 7.6.20 首次绑定手机号，更改美分值为50，包括注册时
            'number': 50,
            'task_type': TASK_TYPE.ONCE,
            'times': 0,
            'is_limited': 0,
        }
        first_bind_phone = {
            'point_type': POINTS_TYPE.BIND_PHONE,  # 7.6.20 首次绑定手机号，更改美分值为50
            'number': 50,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        update_nickname = {
            'point_type': POINTS_TYPE.UPDATE_NICKNAME,
            'number': 25,
            'task_type': TASK_TYPE.DEFAULT,
            'times': 0,
            'is_limited': 0,
        }
        points_lottery = {
            'point_type': POINTS_TYPE.POINTS_LOTTERY,
            'number': 0,
            'task_type': '0',
            'times': 0,
            'is_limited': 0,
        }
        points_exchange = {
            'point_type': POINTS_TYPE.POINTS_EXCHANGE,
            'number': 0,
            'task_type': '0',
            'times': 0,
            'is_limited': 0,
        }
        get_points_from_lottery = {
            'point_type': POINTS_TYPE.GET_POINTS_FROM_LOTTERY,
            'number': 0,
            'task_type': '0',
            'times': 0,
            'is_limited': 0,
        }
        point_lipstick = {
            'point_type': POINTS_TYPE.LIPSTICK_POINTS,
            'number': 0,
            'task_type': '0',
            'times': 0,
            'is_limited': 0,
        }
        # other_operation 后面的合并，会覆盖数据库里同类型的值
        other_operation = [
            sign_in, buy_service, cancel_order, join_activity, download_app,
            pay, level_upper, reply_others, reply_others_multi, answer_question,
            answer_question_multi, publish_topic, vote_multi, follow_multi, recommend_problem,
            recommend_answer, first_bind_phone, update_nickname, registe_at_first,
            points_lottery, points_exchange, get_points_from_lottery, point_lipstick
        ]
        usable_operation = self._get_points_task() + other_operation
        return usable_operation

    def _get_user_points(self, point_type=None, date_time=None):
        points_history = self.user.user_points.all()
        if not point_type and not date_time:
            return []

        if point_type:
            points_history = points_history.filter(reason=point_type)
        if date_time:
            points_history = points_history.filter(add_time__gt=date_time)

        points = [point.to_dict() for point in points_history]
        return points

    def _get_task_points(self, task_info, set_number):
        num = 0
        today = datetime.date.today()
        if not task_info:
            return num
        if task_info['point_type'] == POINTS_TYPE.SIGN_IN:
            num = set_number

        elif task_info['task_type'] == TASK_TYPE.DAILY:
            date_time = today
            points = self._get_user_points(point_type=task_info['point_type'], date_time=date_time)
            if len(points) < task_info['times']:
                num = task_info['number']

        elif task_info['task_type'] == TASK_TYPE.ONCE:
            points = self._get_user_points(point_type=task_info['point_type'])
            if not points:
                num = task_info['number']

        elif not task_info['number'] and set_number:
            num = set_number
        else:
            num = task_info['number']
        return num

    def get_points_amount(self):
        return self.user.person.points

    def get_points_list(self, start_num=0, count=10):
        user = self.user
        points_history = user.user_points.all().order_by('-id')[start_num:start_num+count]
        points_list = [points.to_dict() for points in points_history]
        return points_list

    def get_task_list(self):
        points_task = self._get_points_task()
        daily_type_list = [task['point_type'] for task in points_task if task['task_type'] == TASK_TYPE.DAILY]
        once_type_list = [task['point_type'] for task in points_task if task['task_type'] == TASK_TYPE.ONCE]
        user = self.user
        daily_points_list = user.user_points.filter(reason__in=daily_type_list, add_time__gt=datetime.date.today())
        daily_points_task = {}
        for points in daily_points_list:
            if not daily_points_task.get(points.reason, 0):
                daily_points_task.update({points.reason: 1})
            else:
                daily_points_task[points.reason] += 1

        once_points_list = user.user_points.filter(reason__in=once_type_list)
        once_points_task = {points.reason: 1 for points in once_points_list}

        user_task = []
        finish_task = []
        for task in points_task:
            task['detail'] = u'做任务'
            task['status'] = 0
            if task['task_type'] == TASK_TYPE.DAILY:
                times = daily_points_task.get(task['point_type'], 0)
                if times >= task['times']:
                    task['detail'] = u'今日完成'
                    task['status'] = 1
                    finish_task.append(task)
                else:
                    user_task.append(task)
            elif task['task_type'] == TASK_TYPE.ONCE:
                if once_points_task.get(task['point_type'], 0):
                    task['detail'] = u'已完成'
                    task['status'] = 1
                    finish_task.append(task)
                elif task['point_type'] == POINTS_TYPE.EDIT_PROFILE and user.userextra.portrait:
                    task['detail'] = u'已完成'
                    task['status'] = 1
                    finish_task.append(task)
                elif task['point_type'] == POINTS_TYPE.REGISTER and user.person.phone:
                    task['detail'] = u'已完成'
                    task['status'] = 1
                    finish_task.append(task)
                else:
                    user_task.append(task)
            elif task['point_type'] == POINTS_TYPE.REPLY_DIARY:
                diary_set = Diary.objects.filter(user_id=user.id)
                diary_num = diary_set.count()
                need_comment_diary = 0
                if diary_num:
                    try:
                        comment_diaries = diary_set.filter(Q(comment='') & Q(rating=0.0)).order_by('-id')
                        for comment_diary in comment_diaries:
                            if comment_diary.topics.count():
                                need_comment_diary = comment_diary.id
                                break

                    except Diary.DoesNotExist:  # TODO zhangyunyu
                        need_comment_diary = 0
                if not diary_num:
                    task['detail'] = u'暂不可做'
                    task['status'] = 1
                    finish_task.append(task)
                elif diary_num and not need_comment_diary:
                    task['detail'] = u'已完成'
                    task['status'] = 1
                    finish_task.append(task)
                elif diary_num and need_comment_diary:
                    task['detail'] = u'做任务'
                    task['status'] = 0
                    task['url'] = "{}?diary_id={}".format(task['url'], need_comment_diary)
                    user_task.append(task)
            else:
                user_task.append(task)

        if finish_task:
            user_task += finish_task

        return user_task

    def points_operate(self, num=0, operation=POINTS_OPERATION.ADD, reason=POINTS_TYPE.INVITE, link_user=None,
                       link_topic=None, link_reply=None, link_order_id=None, is_new_sign_in=False):
        points_task = self._get_usable_operation()
        task_dict = {task['point_type']: task for task in points_task}
        task_info = task_dict.get(reason, {})
        if not task_info:
            return False

        today_points = self._get_user_points(date_time=datetime.date.today())
        today_amount = 0
        for today_point in today_points:
            points_info = task_dict.get(today_point['reason'], {})
            if points_info and points_info['is_limited']:
                today_amount += today_point['number']

        if task_info['is_limited'] and today_amount >= self.today_limit:
            return False
        if is_new_sign_in and num:
            number = num
        elif not is_new_sign_in:
            number = self._get_task_points(task_info, num)

        user = self.user
        if not number:
            return False

        with transaction.atomic():
            p = Points(
                user=user,
                operation=operation,
                reason=reason,
                number=number,
                link_user=link_user,
                link_topic_id=link_topic.id if link_topic else None,
                link_reply_id=link_reply.id if link_reply else None,
                link_order_id=link_order_id
            )
            p.save()

            if operation == POINTS_OPERATION.ADD:
                user.person.points += number
                # from api.models import Notification
                # from rpc.tool.protocol import gm_protocol
                # notify = Notification(user=user, title=u'美分返还', url=gm_protocol.my_points,
                #                       content='嗨~美人，您的美分奖励已到账，100美分可抵扣1元！点击查看“我的美分”')
                # notify.save()
            elif operation == POINTS_OPERATION.REMOVE:
                user.person.points -= number
            user.person.save()

            return p.number
