# coding=utf-8
from __future__ import absolute_import

import random
import datetime
from time import sleep

import sys
from celery import shared_task
from django.db import transaction
from django.conf import settings
from django.utils import timezone

from gm_types.gaia import SERVICE_SELL_TYPE, PLATFORM_CHOICES
from gm_types.push import PUSH_INFO_TYPE
from gm_protocol import GmProtocol

from api.models import Doctor, DoctorCountRecord, Q, OrderUserInfo
from api.models.order import Order, ORDER_STATUS_USED
from api.models.user import User
from api.models.order import RefundOrder
from api.models.order import REFUND_STATUS
from api.models.types import ORDER_STATUS
from api.models.types import PAYMENT_TYPE
from api.models.types import ORDER_OPERATION_TYPE
from api.models.types import ORDER_OPERATION_ROLE
from api.tool.log_tool import logging_exception
from api.manager.refund_manager import increase_inventory, decrease_inventory

from statistic import const_strings
from rpc.all import get_rpc_remote_invoker
from rpc.cache import ViewRecord, doctor_discount_cache
from rpc.tool.error_code import CODES, gen

from services.talos_service import get_order_has_diary_and_topics
from talos.tools.push_tool import special_push_limit


@shared_task
def order_increase_event(order_id):
    try:
        order = Order.objects.get(pk=order_id)
        service = order.service
        view = ViewRecord(const_strings.SERVICE)
        show_volume = int(view[service.id] or '0')

        up = 10
        if service.get_service_type == const_strings.EXCHANGE_GIFT:
            up = 5

        if service.id in settings.GROUPON_SERVICE_IDS:
            up = 1

        add = random.randint(1, up)

        if service.service_type == SERVICE_SELL_TYPE.ONEPURCHASE:
            add = 0

        view[service.id] = show_volume + add

    except Order.DoesNotExist:
        logging_exception()


@shared_task
def order_event_for_apollo(event_message):
    rpc_invoker = get_rpc_remote_invoker()

    retry_count = 0

    while retry_count < 3:
        try:
            rpc_invoker['apollo/gaia/proccess_event'](
                event_message=event_message
            ).unwrap()

            break
        except Exception as e:
            retry_count += 1
            print(e)
            logging_exception()
            sleep(retry_count * 2)

# @shared_task
# def order_create_event(order_id, remind_before_expire=True, timeout_cancel=True):
#     try:
#         order = Order.objects.get(pk=order_id)
#
#         if timeout_cancel:
#             delta = datetime.timedelta(seconds=settings.ORDER_TIMEOUT)
#             order.expired_date = datetime.datetime.now() + delta
#             order.save()
#             order_timeout_event.apply_async((order_id,), countdown=settings.ORDER_TIMEOUT)
#
#         if remind_before_expire:
#             remind_countdown = settings.ORDER_TIMEOUT - settings.REMIND_TIME_BEFORE_ORDER_EXPIRED
#             order_expired_reminder.apply_async((order_id,), countdown=remind_countdown)
#
#         return True
#
#     except Order.DoesNotExist:
#         logging_exception()
#
#     return False


@shared_task
def set_order_is_read_by_order_ids(order_ids):
    """
        异步更新订单已读状态，避免订单表死锁
    :param order_id:
    :return:
    """

    # 这里是特意一行一行处理，减少不可预期的死锁状态
    for oid in order_ids:
        Order.objects.filter(id=oid).update(read=True)


@shared_task
def remind_user_write_diary_after_order_validate(order_id):
    """
    改为5小时推送
    :param order_id:
    :return:
    """
    push_remind_if_user_has_not_write_diary.apply_async(
        args=(order_id,),
        countdown=datetime.timedelta(hours=5).seconds
    )


@shared_task
def push_remind_if_user_has_not_write_diary(order_id):
    o = list(Order.objects.filter(id=order_id).values_list('id', 'user_id'))
    if len(o) == 1:
        order_id, user_id, = o[0]

        diary_has_topics = get_order_has_diary_and_topics(order_id)

        user_has_not_write_diary = not diary_has_topics

        if user_has_not_write_diary and special_push_limit(user_id, "verify_write_diary"):
            gm_protocol = GmProtocol()
            extra = {
                'type': PUSH_INFO_TYPE.GM_PROTOCOL,
                'pushUrl': gm_protocol.get_select_diary(),
            }

            content = (
                u'听说你变美啦！' +
                u'如果美人在社区发发照片分享术后心得就有机会赢得返现哦！' +
                u'具体详情可以私信返现小助手咨询哦！'
            )

            platform = [PLATFORM_CHOICES.ANDROID, PLATFORM_CHOICES.IPHONE]
            kwargs = {
                'platform': platform,
                'user_id': user_id,
                'extra': extra,
                'alert': content,
            }
            from api.tasks.push_task import allocate_push_task_one
            allocate_push_task_one(**kwargs)


@shared_task
def order_expired_reminder(order_id):
    try:
        order = Order.objects.get(pk=order_id)
        if order.service.payment_type == PAYMENT_TYPE.FREE_PAYMENT:
            return

        time_left = settings.REMIND_TIME_BEFORE_ORDER_EXPIRED
        if order.status == ORDER_STATUS.NOT_PAID:
            from api.tool.order_tool import send_order_expired_remind
            send_order_expired_remind(order.phone, order.id, order.service.name, order.user.id, time_left)
    except Order.DoesNotExist:
        logging_exception()


# @shared_task
# def order_timeout_event(order_id):
#    try:
#        order = Order.objects.get(pk=order_id)
#        if order.status == ORDER_STATUS.NOT_PAID:
#            # 返还美券
#            from api.tool.coupon_tool import return_coupon
#
#            return_coupon(order.id)
#            order_cancel_event.delay(order.id)
#    except Order.DoesNotExist:
#        logging_exception()
# @shared_task
# def order_timeout_event(order_id):
#     try:
#         order = Order.objects.get(pk=order_id)
#         if order.status == ORDER_STATUS.NOT_PAID:
#             # 返还美券
#             from api.manager.coupon_manager import return_coupon
#
#             return_coupon(order.id)
#             order_cancel_event.delay(order.id)
#     except Order.DoesNotExist:
#         logging_exception()


@shared_task
def order_decrease_event(order_id):
    pass


@shared_task
def refund_cancle_event(order_id):
    try:
        with transaction.atomic():
            order = Order.objects.get(pk=order_id)
            decrease_inventory(order)

    except Order.DoesNotExist:
        logging_exception()


@shared_task
def doctor_refund_timeout():
    from api.tool.log_tool import doctor_unread_logger
    from datetime import datetime, timedelta
    from services.notify import notify
    time_threshold = datetime.now() - timedelta(minutes=settings.DOCTOR_REFUND_TIMEOUT_MINUTES)
    refund_orders = RefundOrder.objects.filter(
        status=REFUND_STATUS.PROCESSING,
        lastest_apply_refund__lt=time_threshold)
    system_user = User.objects.get(pk=settings.BOSS)
    operator = system_user.person
    from rpc.context import create_fake_context
    ctx = create_fake_context()
    for refund_order in refund_orders:
        check_status = RefundOrder.objects.filter(
            status=REFUND_STATUS.PROCESSING, order_id=refund_order.order_id,
        )
        if not check_status.exists():
            continue
        try:
            refund_order.order.operate(operator, ORDER_OPERATION_TYPE.REFUND_TIMEOUT, ORDER_OPERATION_ROLE.SYSTEM)
            ctx.gaia_local['pay/unified/refund'](order_id=refund_order.order.id).unwrap()
        except:
            logging_exception()
        # add 医生版实时小红点数量
        doctor_id = refund_order.order.service.doctor.id
        doctor_unread_logger.info("doctor_refund_timeout, doctor id:{}, refund_order_id:{}".format(
            doctor_id, refund_order.id))
        notify("refund/delete", doctor_id=doctor_id)


@shared_task
def speed_refund(refund):
    from rpc.context import create_fake_context
    ctx = create_fake_context()
    try:
        ctx.gaia_local['pay/unified/refund'](
            order_id=refund.order.id).unwrap()
    except:
        pass


@shared_task
def start_refund(refund_order_id):
    from rpc.context import create_fake_context
    ctx = create_fake_context()
    try:
        ctx.gaia_local['pay/unified/refund'](
            order_id=refund_order_id).unwrap()
    except:
        logging_exception()


# 只会执行一次 在command里面调用
@shared_task
def calc_doctor_discount_all():
    doctors = Doctor.objects.all()
    max_money = 0
    min_money = sys.maxsize
    for doctor in doctors:
        orders = Order.objects.filter(service__doctor=doctor, status__in=ORDER_STATUS_USED)
        all_money = 0
        for o in orders:
            all_money += o.discount
        try:
            d = DoctorCountRecord.objects.get_or_create(doctor=doctor)
            d[0].discount = all_money
            print (doctor.id + ' ' + str(all_money))
            d[0].save()
            if max_money < all_money:
                max_money = all_money
            if min_money > all_money:
                min_money = all_money
        except:
            logging_exception()

    # 防止最大值为0
    if max_money == 0:
        max_money = 1

    dcs = DoctorCountRecord.objects.all()
    for dc in dcs:
        doctor_discount_cache.set('doctor:' + dc.doctor_id, (dc.discount-min_money)*100/(max_money-min_money))


@shared_task
def calc_doctor_discount_one_day():
    doctors = Doctor.objects.all()
    now = timezone.now()
    t = now - datetime.timedelta(days=1)
    max_money = 0
    min_money = sys.maxsize
    one_day_before = datetime.datetime(
        year=t.year,
        month=t.month,
        day=t.day,
        hour=0,
        minute=0,
        second=0
    )
    now = datetime.datetime(
        year=now.year,
        month=now.month,
        day=now.day,
        hour=0,
        minute=0,
        second=0
    )
    for doctor in doctors:
        q = Q(last_modified__range=(one_day_before, now)) & Q(status__in=ORDER_STATUS_USED)
        q &= Q(service__doctor=doctor)
        orders = Order.objects.filter(q)
        all_money = 0
        for order in orders:
            all_money += order.discount
        try:
            d = DoctorCountRecord.objects.get_or_create(doctor=doctor)
            d[0].discount += all_money
            print (doctor.id + ' ' + str(all_money))
            d[0].save()
            if max_money < d[0].discount:
                max_money = d[0].discount
            if min_money > d[0].discount:
                min_money = d[0].discount
        except:
            logging_exception()

    # 防止最大值为0
    if max_money == 0:
        max_money = 1
    dcs = DoctorCountRecord.objects.all()
    for dc in dcs:
        doctor_discount_cache.set('doctor:' + dc.doctor_id, (dc.discount-min_money)*100/(max_money-min_money))


@shared_task
def record_user_information(orders, hospitalpay_id, device_id, ip, status):
    # 记录用户的设备标识,ip
    OrderUserInfo.objects.bulk_create([
        OrderUserInfo(
            order_id=order.id,
            hospitalpay_id=hospitalpay_id,
            status=status,
            device_id=device_id,
            ip=ip,
        )
        for order in orders
    ])
