# -*- coding: UTF-8 -*-

import json
from datetime import datetime, timedelta

from django.db.models import Q

from gm_dataquery.dataquery import DataBuilder, DataSQLQuery
from gm_dataquery.db import DB
from gm_dataquery.dict_mixin import to_dict

from gm_types.gaia import TICKETS_STATUS
from gm_types.gaia import PUNISHMENT_TARGET, PUNISHMENT_PV_LIMIT_DEMESION
from gm_types.gaia import PUNISHMENT_STATUS, PUNISHMENT_TYPE_FULL
from gm_types.error import ERROR

from api import Service
from rpc.exceptions import GaiaRPCFaultException

from api.models.order import Tickets, Doctor
from api.models.tickets import Punishment
from hippo.models.merchant import Merchant, MerchantRelevance

from rpc.cache import punishment_cache, punishment_diary_cache


class TicketsDB(DataBuilder):
    def getval_tickets_time(self, obj):
        return str(obj.tickets_time).split()[0] if obj.tickets_time else u''

    def getval_start_sink(self, obj):
        return str(obj.start_sink).split()[0] if obj.start_sink else u''

    def getval_end_sink(self, obj):
        return str(obj.end_sink).split()[0] if obj.start_sink else u''

    def getval_status(self, obj):
        if obj.status == TICKETS_STATUS.DEFAULT:
            return u''
        elif obj.status == TICKETS_STATUS.UNEXPIRED:
            return u'否'
        else:
            return u'是'


@DB
class TicketsDQ(DataSQLQuery):
    model = Tickets
    data_model = TicketsDB


class PunishmentDB(DataBuilder):

    def getval_days(self, item):
        t = item.end_time - item.start_time
        days = t.days
        if t.seconds:
            days += 1
        return days

    def getval_pv_limit_target(self, obj):
        return obj.target

    def getval_factor_range(self, obj):
        return obj.range

    def getval_factor_target(self, obj):
        return obj.target

    def getval_factor_target_ids(self, obj):
        return obj.target_ids



@DB
class PunishmentDQ(DataSQLQuery):
    model = Punishment
    data_model = PunishmentDB

    def filter_status(self, key, value, regex=False):
        now = datetime.now()
        status = PUNISHMENT_STATUS
        return {
            status.CANCEL: Q(enable=False),
            status.PRE: Q(enable=True, start_time__gt=now),
            status.ING: Q(enable=True, start_time__lt=now, end_time__gt=now),
            status.DONE: Q(enable=True, end_time__lt=now)
        }[value]

    def filter_is_adv_punishment(self, key, value, regex):
        q = Q(punish_type=PUNISHMENT_TYPE_FULL.ADVERTISE)
        if not int(value):
            q = ~q
        return q

    def update(self, updates, **kwargs):
        # TODO 这个样子很危险呐
        punishments = self.model.objects.filter(**kwargs)
        for x in punishments:
            if x.status not in (PUNISHMENT_STATUS.PRE, PUNISHMENT_STATUS.ING):
                raise GaiaRPCFaultException(ERROR.UNIVERSAL, u'状态错误', data=None)
        super(PunishmentDQ.sqlquery, self).update(updates, **kwargs)
        doctors = sum([x.doctor.get_merchant_doctors() for x in punishments], [])
        pks = [x['doctor_id'] for x in doctors]
        self.update_pv_limit(punishments)
        return

    def create(self, **kwargs):
        self.judge_condition(kwargs)
        punishment = Punishment.objects.create(**kwargs)
        doctor = Doctor.objects.get(id=kwargs['doctor_id'])
        doctors = doctor.get_merchant_doctors()
        pks = [x['doctor_id'] for x in doctors]
        self.update_pv_limit([punishment])
        return to_dict(punishment)

    def update_pv_limit(self, punishments):
        for punishment in punishments:
            if int(punishment.punish_type) != PUNISHMENT_TYPE_FULL.PV:
                continue
            punishment = Punishment.objects.get(id=punishment.id)
            end = punishment.end_time - timedelta(days=1)
            merchant = Merchant.objects.get(doctor=punishment.doctor)
            data = {
                'start_time': punishment.start_time.date().isoformat(),
                'end_time': end.date().isoformat(),
                'punish_object': punishment.target,
                'pv_limit': punishment.pv_limit
            }
            if punishment.target == PUNISHMENT_TARGET.SERVICE:
                punishment_cache.set(str(merchant.id), json.dumps(data))
            else:
                punishment_diary_cache.set(str(merchant.id), json.dumps(data))

    def judge_condition(self, data):
        """
          http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=9765603
        :param data:
        :return:
        """
        now = datetime.now()
        q = Q(
            doctor_id=data['doctor_id'],
            enable=True,
            end_time__gt=now
        )

        punishment_target = [data['target'], PUNISHMENT_TARGET.ALL]
        if data['punish_type'] == str(PUNISHMENT_TYPE_FULL.WEIGHT):
            punishment_target.append(PUNISHMENT_TARGET.SERVICE)

        if data['target'] != PUNISHMENT_TARGET.ALL:
            q &= Q(target__in=punishment_target)

        if self.model.objects.filter(q).exists():
            raise GaiaRPCFaultException(ERROR.UNIVERSAL, u'该商家已有未生效或生效中罚单', data=None)
        target = data['target']
        if target == PUNISHMENT_TARGET.SERVICE and int(data.get('range', -1)) == 0:
            service_ids = data['target_ids'].replace(' ', '').split(',')
            service_ids = map(int, service_ids)
            merchant_doctor_id = data['doctor_id']
            merchant = Merchant.objects.get(doctor_id=merchant_doctor_id)
            doctor_ids = MerchantRelevance.objects.filter(merchant_id=merchant.id).values_list('doctor_id')
            all_services = set(Service.objects.filter(doctor_id__in=doctor_ids, id__in=service_ids).values_list('id', flat=True))
            if len(service_ids) > len(all_services):
                other_service_ids = all_services ^ set(service_ids)
                raise GaiaRPCFaultException(ERROR.UNIVERSAL, u'存在不属于该商家的美购:{}'.format(list(other_service_ids)), data=None)



def create_by_advertise(doctor_id):
    now = datetime.now()
    end = now + timedelta(days=10*365)
    return PunishmentDQ.sqlquery().create(
        doctor_id=doctor_id,
        reason=u'广告系统惩罚',
        start_time=now,
        end_time=end,
        comment='',
        target=PUNISHMENT_TARGET.ALL,
        punish_type=PUNISHMENT_TYPE_FULL.ADVERTISE
    )


def cancel_by_advertise(doctor_id):
    updates = {'enable': False}
    now = datetime.now()
    return PunishmentDQ.sqlquery().update(
        punish_type=PUNISHMENT_TYPE_FULL.ADVERTISE,
        doctor_id=doctor_id,
        enable=True,
        end_time__gt=now,
        updates=updates
    )
