# coding=utf-8
import datetime
from helios import create_default_invoker

from api.models.block_user.block_user import BlockRecord
from gm_dataquery.dataquery import DataBuilder, DataSQLQuery
from rpc.decorators import bind
from gm_types.gaia import (
    SEAL_ITEM_TYPE,
    SEAL_STATUS,
    SEAL_OPEARTION_TYPE,
    SEAL_TIME_TYPE,
    CANCEL_SEAL_TYPE)

from django.db.models import Q
from helios.rpc.exceptions import RPCSystemException


class SealRecordDataBuilder(DataBuilder):

    def getval_user_id(self, obj):
        return obj.user_id

    def getval_user_name(self, obj):
        return obj.user_name

    def getval_user_phone(self, obj):
        return obj.user_phone

    def getval_seal_time_start(self, obj):
        return str(obj.seal_time_start)

    def getval_seal_reason(self, obj):
        return obj.seal_reason

    def getval_item(self, obj):
        return SEAL_ITEM_TYPE.getDesc(int(obj.item))

    def getval_seal_type(self, obj):
        return SEAL_OPEARTION_TYPE.getDesc(int(obj.seal_type))

    def getval_seal_status(self, obj):
        return SEAL_STATUS.getDesc(int(obj.seal_status))

    def getval_seal_duration(self, obj):
        if int(obj.seal_time_type) == SEAL_TIME_TYPE.SEAL_TIME_FOREVER:
            return u'永久封禁'

        if int(obj.seal_time_type) == SEAL_TIME_TYPE.SEAL_TIME_TEMPORARY:
            return str(obj.seal_duration)+u'天'


class SealRecord(DataSQLQuery):
    model = BlockRecord
    data_model = SealRecordDataBuilder

    def filter_item(self, srch_key, srch_val, regex=False):
        q = Q()

        if int(srch_val) == SEAL_ITEM_TYPE.SEAL_ITEM_FACE_CONSULTATION:
            q.children.append(('item', SEAL_ITEM_TYPE.SEAL_ITEM_FACE_CONSULTATION))
            return q

        if int(srch_val) == SEAL_ITEM_TYPE.SEAL_ITEM_PRIVATE_MSG:
            q.children.append(('item', SEAL_ITEM_TYPE.SEAL_ITEM_PRIVATE_MSG))
            return q

        if int(srch_val) == SEAL_ITEM_TYPE.SEAL_ITEM_ALL:
            q.children.append(('item', SEAL_ITEM_TYPE.SEAL_ITEM_ALL))
            return ~q

    def filter_seal_type(self, srch_key, srch_val, regex=False):
        q = Q()

        if int(srch_val) == SEAL_OPEARTION_TYPE.SEAL_MANUAL:
            q.children.append(('seal_type', SEAL_OPEARTION_TYPE.SEAL_MANUAL))
            return q

        if int(srch_val) == SEAL_OPEARTION_TYPE.SEAL_SYSTEM_AUTO:
            q.children.append(('seal_type', SEAL_OPEARTION_TYPE.SEAL_SYSTEM_AUTO))
            return q

        if int(srch_val) == SEAL_OPEARTION_TYPE.SEAL_ALL:
            q.children.append(('seal_type', SEAL_OPEARTION_TYPE.SEAL_ALL))
            return ~q

    def filter_seal_status(self, srch_key, srch_val, regex=False):
        q = Q()

        if int(srch_val) == SEAL_STATUS.SEAL_STATUS_BAN:
            q.children.append(('seal_status', SEAL_STATUS.SEAL_STATUS_BAN))
            return q

        if int(srch_val) == SEAL_STATUS.SEAL_STATUS_UNBAN:
            q.children.append(('seal_status', SEAL_STATUS.SEAL_STATUS_UNBAN))
            return q

        if int(srch_val) == SEAL_STATUS.SEAL_STATUS_ALL:
            q.children.append(('seal_status', SEAL_STATUS.SEAL_STATUS_ALL))
            return ~q

    def filter_seal_time_start(self, srch_key, srch_val, regex=False):
        q = Q()

        if not isinstance(srch_val, list) or len(srch_val) != 2:
            return q

        start, end = srch_val[0], srch_val[1]
        if start and end:
            q.children.append(('seal_time_start__gt', start))
            q.children.append(('seal_time_start__lt', end))

            return q


class CancelSealRecordDataBuilder(DataBuilder):

    def getval_user_id(self, obj):
        return obj.user_id

    def getval_user_name(self, obj):
        return obj.user_name

    def getval_user_phone(self, obj):
        return obj.user_phone

    def getval_item(self, obj):
        return SEAL_ITEM_TYPE.getDesc(int(obj.item))

    def getval_cancel_seal_time(self, obj):
        return str(obj.cancel_seal_time)

    def getval_cancel_seal_type(self, obj):
        return CANCEL_SEAL_TYPE.getDesc(int(obj.cancel_seal_type))

    def getval_cancel_seal_reason(self, obj):
        return obj.cancel_seal_reason


class CancelSealRecord(DataSQLQuery):
    model = BlockRecord
    data_model = CancelSealRecordDataBuilder

    def filter_item(self, srch_key, srch_val, regex=False):
        q = Q()

        if int(srch_val) == SEAL_ITEM_TYPE.SEAL_ITEM_FACE_CONSULTATION:
            q.children.append(('item', SEAL_ITEM_TYPE.SEAL_ITEM_FACE_CONSULTATION))
            return q

        if int(srch_val) == SEAL_ITEM_TYPE.SEAL_ITEM_PRIVATE_MSG:
            q.children.append(('item', SEAL_ITEM_TYPE.SEAL_ITEM_PRIVATE_MSG))
            return q

        if int(srch_val) == SEAL_ITEM_TYPE.SEAL_ITEM_ALL:
            q.children.append(('item', SEAL_ITEM_TYPE.SEAL_ITEM_ALL))
            return ~q

    def filter_cancel_seal_type(self, srch_key, srch_val, regex=False):
        q = Q()

        if int(srch_val) == CANCEL_SEAL_TYPE.CANCEL_SEAL_MANUAL:
            q.children.append(('cancel_seal_type', CANCEL_SEAL_TYPE.CANCEL_SEAL_MANUAL))
            return q

        if int(srch_val) == CANCEL_SEAL_TYPE.CANCEL_SEAL_SYSTEM_AUTO:
            q.children.append(('cancel_seal_type', CANCEL_SEAL_TYPE.CANCEL_SEAL_SYSTEM_AUTO))
            return q

        if int(srch_val) == CANCEL_SEAL_TYPE.CANCEL_SEAL_ALL:
            q.children.append(('cancel_seal_type', CANCEL_SEAL_TYPE.CANCEL_SEAL_ALL))
            return ~q

    def filter_cancel_seal_time(self, srch_key, srch_val, regex=False):
        q = Q()

        if not isinstance(srch_val, list) or len(srch_val) != 2:
            return q

        start, end = srch_val[0], srch_val[1]

        if start and end:

            q.children.append(('cancel_seal_time__gt', start))
            q.children.append(('cancel_seal_time__lt', end))

            return q


@bind("api/block/querySeal")
def querySeal(options):

    filterLists = options[u"filters"]
    for index, filterLine in enumerate(filterLists):
        if filterLine.get(u"field") == u'seal_time_start':
            timeRange = filterLine.get(u'value')
            newTimeRange = [
                timeRange[0]+u" "+timeRange[1],
                timeRange[2]+u" "+timeRange[3]
            ]
            filterLine[u'value'] = newTimeRange
            filterLists[index] = filterLine

    options[u"filters"] = filterLists
    qObj = SealRecord()
    res = qObj.process(**options)

    return res


@bind('api/block/queryCancelSeal')
def queryCancelSeal(options):

    filterLists = options[u"filters"]
    for index, filterLine in enumerate(filterLists):
        if filterLine.get(u'field') == u'cancel_seal_time':
            timeRange = filterLine.get(u'value')
            newTimeRange = [
                timeRange[0]+u' '+timeRange[1],
                timeRange[2]+u' '+timeRange[3]
            ]
            filterLine[u'value'] = newTimeRange
            filterLists[index] = filterLine

    options[u'filters'] = filterLists

    qObj = CancelSealRecord()
    res = qObj.process(**options)

    return res


# isUserExist check is userID in user_seal_log table
#
# userID int
#
# return bool
#     true: exist
#     false: no exist
def isUserExist(userID):
    num = len(BlockRecord.objects.filter(user_id=userID))
    if num == 0:
        return False

    return True


# querySealRecord query record of user_id is userID, item is item and seal_type is sealing
#
# userID int
# item int
#
# return: a new QuerySet instance
def querySealRecord(userID, item):
    return BlockRecord.objects.filter(
        user_id=userID,
        item=item,
        seal_status=SEAL_STATUS.SEAL_STATUS_BAN
    )


# isUserSealNow check whether item is sealing at now for userID
#
# queryResNum int
#
# return bool
#     true: sealing
#     false: no seal at now
def isUserSealNow(queryResNum):
    if queryResNum == 0:
        return False

    return True


# isSealForever check if the user is sealed forever or temporarily
#
# sealTimeType int
#
# return bool
#     true: sealed forever
#     false: sealed temp
def isSealForever(sealTimeType):
    if sealTimeType == SEAL_TIME_TYPE.SEAL_TIME_FOREVER:
        return True

    return False


# isSealExpired check if the user's item seal is expired
#
# endDateTime datetime
#
# return bool
#     true: expired
#     false: unexpired
def isSealExpired(endDateTime):
    now = datetime.datetime.now()

    if now > endDateTime:
        return True

    return False


# When the seal expires, the system will automatically cancel seal
#
# queryID int
# cancelSealTime datetime
#
# return bool
#     true: succeed to cancel seal
#     false: failed to cancel seal
def systemCancelSeal(queryID, cancelSealTime):
    BlockRecord.objects.filter(id=queryID).update(
        seal_status=SEAL_STATUS.SEAL_STATUS_UNBAN,
        cancel_seal_reason=u"封禁到期，系统自动解除",
        cancel_seal_type=CANCEL_SEAL_TYPE.CANCEL_SEAL_SYSTEM_AUTO,
        cancel_seal_time=cancelSealTime
    )

    return True


# isSeal check whether item is seal for userID
#
# userInfo: dict{}
# example:
#         {
#             "user_id": 2323423432,
#             "item": 1
#         }
# return: dict{}
# example:
#         {
#             "msg_req": {
#                 "user_id": 2332423434,
#                 "item": 1
#             },
#             "msg_resp": {
#                 "is_seal": 0,
#                 "time_start": "",
#                 "time_end": "",
#                 "time_dur": 3
#             },
#             "msg_code": 1,
#             "msg_error": ""
#         }
@bind("api/block/isSeal")
def checkIsSeal(userInfo):
    return isSeal(userInfo)


def isSeal(userInfo):
    resp = {
        "msg_req": userInfo,
        "msg_resp": {},
        "msg_code": 1,
        "msg_error": ""
    }

    msg_resp_unSeal = {
        "is_seal": 0,
        "time_start": "",
        "time_end": "",
        "time_dur": 0
    }

    msg_resp_seal = {
        "is_seal": 1,
        "time_start": "",
        "time_end": "",
        "time_dur": 0
    }

    userID = userInfo.get("user_id")
    item = userInfo.get("item")
    if userID is None or item is None:
        resp["msg_code"] = 0
        resp["msg_error"] = "bad user info of request"
        return resp

    isUserExistSealTable = isUserExist(userID=userID)

    if not isUserExistSealTable:
        resp["msg_resp"] = msg_resp_unSeal
        return resp

    sealRecordNow = querySealRecord(userID=userID, item=item)

    recordNum = len(sealRecordNow)
    isUserSealing = isUserSealNow(queryResNum=recordNum)
    if not isUserSealing:
        resp["msg_resp"] = msg_resp_unSeal
        return resp

    sealTimeInfo = sealRecordNow.values_list("seal_time_start", "seal_time_end", "seal_duration")[0]
    sealTimeStart = str(sealTimeInfo[0])
    sealTimeEnd = str(sealTimeInfo[1])
    sealTimeDur = sealTimeInfo[2]

    msg_resp_seal["time_start"] = sealTimeStart
    msg_resp_seal["time_end"] = sealTimeEnd
    msg_resp_seal["time_dur"] = sealTimeDur

    sealTimeType = sealRecordNow.values_list("seal_time_type", flat=True)[0]
    isUserSealForever = isSealForever(sealTimeType)
    if isUserSealForever:
        resp["msg_resp"] = msg_resp_seal
        return resp

    sealEndDateTime = sealRecordNow.values_list("seal_time_end", flat=True)[0]
    isUserSealExpired = isSealExpired(sealEndDateTime)
    if not isUserSealExpired:
        resp["msg_resp"] = msg_resp_seal
        return resp

    recordID = sealRecordNow.values_list("id", flat=True)[0]
    resCancelSeal = systemCancelSeal(recordID, sealEndDateTime)
    if resCancelSeal:
        resp["msg_resp"] = msg_resp_unSeal
        return resp

    resp["msg_resp"] = msg_resp_seal
    return resp


# insertSealRecords bulk insert seal records into seal table
#
# sealUserList dict{}
# example:
#     {
#         "seal_item": 0,
#         "seal_time_type": 0,
#         "seal_duration": 3,
#         "seal_reason": "骚扰",
#         "seal_user_list": [2223131, 213213123, 213213213]
#     }
#
# return dict{}
# example:
#     {
#         "req": {
#             "seal_item": 0,
#             "seal_time_type": 0,
#             "seal_duration": 3,
#             "seal_reason": "骚扰",
#             "seal_user_list": [2223131, 213213123, 2132131231]
#         },
#         "resp": {
#             "seal_total": 3,
#             "seal_failed": 2,
#             "seal_failed_list": [32423432, 32423423]
#         },
#         "msg_code": 1,
#         "msg_error": ""
#
#     }
@bind("api/block/bulkSeal")
def bulkSeal(sealUserList):
    recordList = []
    sealResp = {
        "msg_req": sealUserList,
        "msg_resp": {},
        "msg_code": 1,
        "msg_error": ""
    }
    failedSealList = []

    sealUserNum = len(sealUserList.get("seal_user_list"))

    # should add check func to check k/v
    item = sealUserList.get("seal_item")
    sealTimeType = sealUserList.get("seal_time_type")
    duration = sealUserList.get("seal_duration")
    reason = sealUserList.get("seal_reason")
    userList = sealUserList.get("seal_user_list")
    timeStart = datetime.datetime.now()
    timeEnd = timeStart + datetime.timedelta(days=duration)

    for userID in userList:

        # check user seal status, if no seal, will seal him
        res = isSeal({"user_id": userID, "item": item})
        isUserSeal = res["msg_resp"]["is_seal"]
        if isUserSeal == 1:
            failedSealList.append(userID)
            continue

        invoker = create_default_invoker(debug=True)
        try:
            userDetail = invoker()['hera/user/detail'](user_id=userID).unwrap()
        except RPCSystemException:
            failedSealList.append(userID)
            continue

        userName = userDetail.get("first_name")
        if userName is None:
            userName = ""

        # hera/user/getinfoby_userid
        userInfo = invoker()['hera/user/getinfoby_userid'](user_id=userID).unwrap()

        userPhone = userInfo.get("phone")
        if userPhone is None:
            userPhone = ""

        record = BlockRecord(
            user_id=userID,
            user_name=userName,
            user_phone=userPhone,
            item=item,
            seal_time_type=sealTimeType,
            seal_time_start=timeStart,
            seal_time_end=timeEnd,
            seal_duration=duration,
            seal_type=SEAL_OPEARTION_TYPE.SEAL_MANUAL,
            seal_reason=reason,
            seal_status=SEAL_STATUS.SEAL_STATUS_BAN
        )

        recordList.append(record)

    BlockRecord.objects.bulk_create(recordList)

    resp = {
        "seal_total": sealUserNum,
        "seal_failed": len(failedSealList),
        "seal_failed_list": failedSealList
    }
    sealResp["msg_resp"] = resp

    return sealResp


# bulkCancelSeal bulk cancel seal of item for multiple user,
# update records that meet condition
#
# cancelSealUserList dict{}
# example:
#     {
#         "item": 1,
#         "cancel_seal_reason": "表现良好",
#         "cancel_seal_user_list": [323232323, 2332423232]
#     }
#
# return dict{}
# example:
#     {
#         "req": {
#             "item": 1,
#             "cancel_seal_reason": "表现良好",
#             "cancel_seal_user_list": [{"user_id": 231, "sealTimeType": 0},{"user_id": 343423, "sealTimeType": 1}]
#         },
#         "resp": {
#             "cancel_seal_total": 3,
#             "cancel_seal_failed": 2,
#             "cancel_seal_failed_list": [
#                 3223423432,
#                 4234232334
#             ]
#         },
#         "msg_code": 1,
#         "msg_error": ""
#     }
@bind("api/block/bulkCancelSeal")
def bulkCancelSeal(cancelSealUserList):
    sealResp = {
        "msg_req": cancelSealUserList,
        "msg_resp": {},
        "msg_code": 1,
        "msg_error": ""
    }
    recordList = cancelSealUserList.get("cancel_seal_record_list")
    cancelUserNum = len(recordList)
    failedUpdateList = []

    reason = cancelSealUserList.get("cancel_seal_reason")
    cancelSealTime = datetime.datetime.now()

    for recordID in recordList:

        recordContent = BlockRecord.objects.get(id=recordID)
        recordSealTimeType = recordContent.seal_time_type
        recordSealStatus = recordContent.seal_status
        recordUserID = str(recordContent.user_id)

        # forever seal log will change seal_time_type to temporary and change seal_status to unbind
        if recordSealTimeType == SEAL_TIME_TYPE.SEAL_TIME_FOREVER:
            rowNum = BlockRecord.objects.filter(id=recordID).update(
                seal_status=SEAL_STATUS.SEAL_STATUS_UNBAN,
                seal_time_type=SEAL_TIME_TYPE.SEAL_TIME_TEMPORARY,
                cancel_seal_reason=reason,
                cancel_seal_type=CANCEL_SEAL_TYPE.CANCEL_SEAL_MANUAL,
                cancel_seal_time=cancelSealTime
            )
            if rowNum == 0:
                failedUpdateList.append(recordUserID)

        # temporary seal log only change seal_status to unbind
        if recordSealTimeType == SEAL_TIME_TYPE.SEAL_TIME_TEMPORARY and recordSealStatus == SEAL_STATUS.SEAL_STATUS_BAN:
            rowNum = BlockRecord.objects.filter(id=recordID).update(
                seal_status=SEAL_STATUS.SEAL_STATUS_UNBAN,
                cancel_seal_reason=reason,
                cancel_seal_type=CANCEL_SEAL_TYPE.CANCEL_SEAL_MANUAL,
                cancel_seal_time=cancelSealTime
            )
            if rowNum == 0:
                failedUpdateList.append(recordUserID)

        if recordSealTimeType == SEAL_TIME_TYPE.SEAL_TIME_TEMPORARY and recordSealStatus == SEAL_STATUS.SEAL_STATUS_UNBAN:
            failedUpdateList.append(recordUserID)

    resp = {
        "cancel_seal_total": cancelUserNum,
        "cancel_seal_failed": len(failedUpdateList),
        "cancel_seal_failed_list": failedUpdateList
    }

    sealResp["msg_resp"] = resp

    return sealResp


