# -*- coding: UTF-8 -*-
from __future__ import absolute_import

import json
import datetime
from django.db import transaction

from django.db.models import Q
from django.conf import settings

from api.models import (
    Order, CashBackOrder, RefundOrder,
    REFUND_STATUS, ORDER_OPERATION_TYPE, ORDER_OPERATION_ROLE, ORDER_SOURCE,
    OrderOperation, Sum)
from api.models.service import ServiceItem
from hippo.models.config import DoctorWhiteList
from api.tool.user_tool import get_user_from_context
from api.tasks.export_excel_task import export_orders_excel, export_self_support_excel
from api.models.types import ORDER_STATUS, CASH_BACK_STATUS, PROBLEM_REVIEW_STATUS_CHOICES
from talos.models.diary import Diary
from hera.queries.orderoperation import OrderOperationDQ
from hera.queries.refundorder_error import RefundOrderErrorDQ
from hera.queries.stalerefundorder import StaleRefundOrderDQ
from rpc.decorators import bind_context
from rpc.exceptions import (RPCNotFoundException)
from rpc.tool.dict_mixin import to_dict
from rpc.cache import req_data_cache
from rpc.tool.log_tool import info_logger
from rpc.tool.error_code import CODES, gen
from rpc.all import get_rpc_remote_invoker
from rpc.tool.queryset_tool import queryset_dec
from rpc.cache import req_data_cache
from gm_types.gaia import REFUND_COMMENT, NEW_CASH_BACK_STATUS, SERVICE_SELL_TYPE
from gm_types.gaia import PAYMENT_CHANNEL
from gm_types.gaia import OPERATION_LOCATION_TYPE

from pay.models import ServiceSnapshot, PaymentOrder
from pay.models import WechatRefund
from pay.models import Installment
from utils.order import get_refund_no
from ..models import UserPerm
from ..datatables import CashBackOrderDT, RefundOrderDT
from ..actions.order import OrderAction
from ..utils import check_business, check_operate, operate_business, get_business_team
from ..queries.order import OrderDQ

uri_pre = 'hera/order'
uri_cashback = 'hera/order/cashback'
uri_refund = 'hera/order/refund'


@bind_context(uri_pre + '/choices')
def order_choices(ctx, q='', page=1, num=30, initial=None):
    page = int(page)
    num = int(num)

    if initial is not None:
        if isinstance(initial, (list, tuple)):
            qry = Q(id__in=initial)
        else:
            qry = Q(id=initial)
    else:
        qry = Q(id=q) | Q(name=q)
    query = Order.objects.filter(qry)
    # total_count = query.count()
    total_count = 0
    start_pos = (page - 1) * num
    start_pos = start_pos if start_pos >= 0 else 0
    results = [
        {
            'id': obj.id,
            'text': u'{}:{}'.format(obj.id, obj.service.name),
        } for obj in query[start_pos: start_pos + num]
        ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(uri_pre + '/get')
def order_detail(ctx, order_id, options=None):
    """TODO: 迁移PaymentOrder"""
    try:
        order = Order.objects.get(id=order_id)
    except:
        __import__('traceback').print_exc()
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': {
                'user': ['id', 'last_name'],
                'service': ['id', 'name'],
                'operate_user': ['id', 'last_name'],
            },
        }
    order_data = to_dict(order, **options)
    order_data['phone'] = order.phone_crypt
    
    serviceitem = None
    try:
        serviceitem = ServiceItem.objects.get(id=order.service_item_id)
        city_name = serviceitem.city.name
    except:
        city_name = None
    order_data['order_multiattribute'] = order.order_multiattribute if not city_name \
        else u'({}){}'.format(city_name, order.order_multiattribute)
    order_data['groupbuy_status'] = order.groupbuy_status
    order_data['hospital_payment'] = order.hospital_payment
    order_data['source'] = ORDER_SOURCE.getDesc(order.source)
    order_data['service_snapshot'] = json.loads(order.service_snapshot) \
        if order.service_snapshot else {}
    order_data['coupon_data'] = order.get_order_coupon_infos()
    if order.service.doctor:
        order_data['doctor'] = {
            'id': order.service.doctor.id,
            'name': order.service.doctor.name,
        }
    else:
        order_data['doctor'] = {'id': '', 'name': ''}
    if order.service.doctor.hospital:
        order_data['hospital'] = {
            'id': order.service.doctor.hospital.id,
            'name': order.service.doctor.hospital.name,
        }
    else:
        order_data['hospital'] = {'id': '', 'name': ''}

    if order.payment_channel in [PAYMENT_CHANNEL.WECHAT, PAYMENT_CHANNEL.ALIPAY, PAYMENT_CHANNEL.APPLEPAY]:
        payment_order = PaymentOrder.objects.get(orders=order_id)
        order_data['payment_order'] = to_dict(payment_order)
    # if order.payment_channel == PAYMENT_CHANNEL.WECHAT:
    #     wechat_order = WechatOrder.objects.get(order_no=order.id)
    #     order_data['wechat'] = to_dict(wechat_order)
    # if order.payment_channel == PAYMENT_CHANNEL.APPLEPAY:
    #     try:
    #         apple_pay = ApplePaySettlement.objects.get(settlement_id=order.settlement_id)
    #         order_data['apple_pay'] = to_dict(apple_pay)
    #     except:
    #         order_data['apple_pay'] = []
    #     apple_pay_refund = getattr(order, u'refund', None)
    #     order_data['apple_pay_refund'] = apple_pay_refund.id if apple_pay_refund else None

    if order.payment_channel == PAYMENT_CHANNEL.XIAOYING:
        try:
            xiaoying = Installment.objects.get(settlement_id=order.settlement_id)
            order_data['xiaoying'] = to_dict(xiaoying)
        except:
            order_data['xiaoying'] = []
    # if getattr(order, 'yinuo_order', False):
    #     yinuo = order.yinuo_order
    #     order_data['yinuo'] = {
    #         'id': yinuo.credential_id,
    #         'premium': yinuo.premium,
    #         'confirmed_at': str(yinuo.confirmed_at),
    #         'expiration': yinuo.expiration
    #     }

    order_data['insurance_info'] = order.insurance_info()

    order_data['operations'] = []
    for operation in order.orderoperation_set.all():
        op = {
            "role": ORDER_OPERATION_ROLE.getDesc(operation.role),
            "user": operation.operator.user.last_name or operation.operator.user.username,
            "operation": ORDER_OPERATION_TYPE.getDesc(operation.optype),
            "time": str(operation.operate_at),
        }
        order_data['operations'].append(op)
    order_data['is_seckill'] = order.is_seckill_order
    order_data['password'] = order.password or ''
    order_data['service__service_type_desc'] = SERVICE_SELL_TYPE.getDesc(order.service.service_type)
    order_data['operation_location'] = order.operation_hospital_id \
        if order.operation_location_type == OPERATION_LOCATION_TYPE.WHITE_LIST \
        else order.operation_location_type

    order_data['is_more_buy'] = True if serviceitem and serviceitem.parent_id else False

    return order_data


@bind_context(uri_pre + '/edit')
def order_edit(ctx, order_id, order_info=None):
    if order_info is None:
        return None

    try:
        order = Order.objects.get(id=order_id)
    except:
        info_logger.info(__import__('traceback').format_exc())
        raise RPCNotFoundException
    if order_info['operation']:
        order.operate(ctx.session.user.person, int(order_info['operation']),
                      ORDER_OPERATION_ROLE.STAFF, ORDER_SOURCE.BACKGROUND)
    user = get_user_from_context(ctx)
    now = datetime.datetime.now()
    comments = u'{}  备注人:{} 备注时间:{} 操作:{}\n'.format(
        order_info['comments'].replace('\n', ' '),
        user.last_name or user.username,
        now.strftime("%Y-%m-%d %H:%M:%S"),
        ORDER_OPERATION_TYPE.getDesc(int(order_info['operation'])),
    )
    order.comments = order.comments + comments
    order.save()
    return order.id


@bind_context(uri_pre + '/query', login_required=True)
def order_query(ctx, options):
    init_q = None
    _in, businessman_list, team_groups = get_business_team(ctx)
    if _in:
        init_q = Q(servicesnapshot__doctor__business_partener__in=businessman_list) | Q(servicesnapshot__doctor__business_group__in=team_groups)

    req_data_cache.set(ctx.session.session_key, json.dumps(options))
    dtobj = OrderDQ(init_q=init_q)
    return dtobj.process(**options)


@bind_context(uri_pre + '/status')
def change_orders_status(nos, dest_status):
    processed = []
    for order_no in nos:
        try:
            order = Order.objects.get(pk=order_no)
            order.status = dest_status
            order.save()
            processed.append(order_no)
        except Order.DoesNotExist:
            pass
    return {"processed": processed}


@bind_context(uri_pre + '/orders')
def order_orders(ctx, req_data, querytype, nos):
    user = ctx.session.user
    if check_business(ctx.session.groups):
        # 判断是商务并且非商务leader
        members = UserPerm.members(user)
        init_q = Q(service__doctor__business_partener__in=members)
    else:
        init_q = None

    dtobj = OrderDQ(Order, init_q=init_q)
    if nos != 'selectall':
        orders = Order.objects.filter(id__in=nos)
    else:
        req_data = json.loads(req_data_cache.get(ctx.session.session_key))
        if not req_data:
            return
        if req_data.get('paging'):
            req_data.pop('paging')
        if req_data.get('columns'):
            req_data.pop('columns')
        orders = dtobj.build_queryset(**req_data)
    if 'excel' in querytype:
        args = (orders, user.email)
    else:
        args = (orders,)
    return getattr(OrderAction, querytype)(*args)


@bind_context(uri_pre + '/can_refund_directly')
def can_refund_directly(ctx, order_id):
    """
    是否可以直接退款
    :param order_id:
    :return: True or False
    """
    try:
        order = Order.objects.get(id=order_id)
    except Order.DoesNotExist:
        return gen(CODES.ORDER_NOT_FOUND)

    if order.status == ORDER_STATUS.REFUNDED:
        return False

    # 后台退款不需要判断白名单，只有等待退款的可以执行退款操作
    if order.status == ORDER_STATUS.WAIT_REFUNDED:
        return True
    return False

    doctor = order.service.doctor
    doctor_in_whitelist = DoctorWhiteList.objects.filter(doctor=doctor)
    if not doctor_in_whitelist:
        return False

    refund_reason = order.refund_comment
    if not refund_reason:
        return False

    # 只要退款理由中包含一个正当退款理由就可以
    is_valid_reason = False
    for item in REFUND_COMMENT.enum_list:
        v, desc = item
        if desc in refund_reason:
            is_valid_reason = True
            break

    if not is_valid_reason:
        return False

    return True


@bind_context(uri_cashback + '/list')
def cashback_order_datatable(ctx, req_data):
    if check_business(ctx.session.groups):
        # 判断是商务并且非商务leader
        user = ctx.session.user
        members = UserPerm.members(user)
        init_q = Q(servicesnapshot__doctor__business_partener__in=members)
    elif check_operate(ctx.session.user_id):
        business_ids = operate_business(ctx.session.user_id)
        init_q = Q(servicesnapshot__doctor__business_partener__id__in=business_ids)
    else:
        init_q = None

    req_data_cache.set(ctx.session.session_key + 'cashback', json.dumps(req_data))
    dtobj = CashBackOrderDT(CashBackOrder, init_q=init_q)
    return dtobj.process(req_data, [
        'order__service__doctor__name', 'order__service__doctor__hospital__name',
        'order__service__name', 'order__address', 'order__user__last_name',
        'order__phone', 'id', 'order__id', 'order__servicesnapshot__doctor__business_partener__username'
    ])


@bind_context(uri_refund + '/list')
def refund_order_datatable(ctx, req_data):
    if check_business(ctx.session.groups):
        # 判断是商务并且非商务leader
        user = ctx.session.user
        members = UserPerm.members(user)
        init_q = Q(order__service__doctor__business_partener__in=members)
    elif check_operate(ctx.session.user_id):
        business_ids = operate_business(ctx.session.user_id)
        init_q = Q(order__service__doctor__business_partener__id__in=business_ids)
    else:
        init_q = None

    req_data_cache.set(ctx.session.session_key + 'refund', json.dumps(req_data))
    dtobj = RefundOrderDT(RefundOrder, init_q=init_q)
    return dtobj.process(req_data, [
        'order__service__doctor__name', 'order__service__doctor__hospital__name',
        'order__service__name', 'order__address', 'order__user__last_name',
        'order__phone', 'id', 'order__id', 'order__servicesnapshot__doctor__business_partener__username'
    ])


@bind_context(uri_refund + '/detail')
def refund_order_detail(ctx, refund_id):
    try:
        refund = RefundOrder.objects.get(id=refund_id)
    except:
        raise RPCNotFoundException
    service_snapshot = json.loads(refund.order.service_snapshot)
    try:
        points = refund.order.points_deduction
    except:
        points = ''
    data = {
        'gm_send_refund_no': get_refund_no(refund.order),
        'id': refund.id,
        'order_id': refund.order_id,
        'stale': refund.stale,
        'status': REFUND_STATUS.getDesc(refund.status),
        'created_at': str(refund.created_at),
        'fee': refund.fee or refund.estimated_fee,
        'user_reason': refund.user_reason,
        'doctor': refund.order.service.doctor.name,
        'update_at': str(refund.updated_at),
        'user_name': refund.order.name,
        'user_phone': refund.order.user.person.phone,
        'doctor_phone': refund.order.service.doctor.phone,
        "discount": refund.order.discount,
        "service_price": refund.order.service_price,
        "order_create_time": str(refund.order.created_time),
        "comments": refund.order.comments,
        "user": {
            "name": refund.order.user.last_name,
            "real_name": refund.order.name,
            "tel": refund.order.phone or refund.order.user.person.phone
        },
        "doctor": {
            "name": refund.order.service.doctor.name,
            "hospital": refund.order.service.doctor.hospital.name,
            "tel": service_snapshot['phone'] if service_snapshot['phone'] else refund.order.service.doctor.phone
        },
        "service": {
            "id": refund.order.service.id,
            "name": service_snapshot.get('name'),
            "item": service_snapshot.get("items", None),
            "ceiling_price": service_snapshot.get('ceiling_price'),
            "gengmei_price": service_snapshot.get('gengmei_price'),
            "pre_payment_price": service_snapshot.get('pre_payment_price'),
            "multiattr_items_name": service_snapshot.get('items'),
            "order_coupon_infos": {x['coupon_type']: x for x in refund.order.get_order_coupon_infos()},
            "points": refund.order.points or points,
            "points_deduction": refund.order.points_deduction,
        },
        "operations": [],
    }
    optypes = [
        ORDER_OPERATION_TYPE.CANCEL_REFUND,
        ORDER_OPERATION_TYPE.DOCTOR_REJECT,
        ORDER_OPERATION_TYPE.DOCTOR_APPROVE,
        ORDER_OPERATION_TYPE.REFUNDED,
        ORDER_OPERATION_TYPE.APPEAL_ARBIT,
        ORDER_OPERATION_TYPE.ARBIT_APPROVE,
        ORDER_OPERATION_TYPE.ARBIT_REJECT,
        ORDER_OPERATION_TYPE.REFUND_TIMEOUT,
        ORDER_OPERATION_TYPE.APPLY_REFUND,
        ORDER_OPERATION_TYPE.STALE_REFUND,
        ORDER_OPERATION_TYPE.TAG_STALE_REFUND,
    ]
    for operation in refund.order.orderoperation_set.filter(optype__in=optypes).order_by('-operate_at'):
        op = {
            "role": ORDER_OPERATION_ROLE.getDesc(operation.role),
            "operation": ORDER_OPERATION_TYPE.getDesc(operation.optype),
            "time": str(operation.operate_at),
            "reason": '',
        }
        if operation.optype == ORDER_OPERATION_TYPE.APPLY_REFUND:
            op["reason"] = refund.user_reason
        if operation.optype in [ORDER_OPERATION_TYPE.DOCTOR_REJECT, ORDER_OPERATION_TYPE.DOCTOR_APPROVE]:
            op["reason"] = refund.doctor_reason
        data["operations"].append(op)
    return data


@bind_context(uri_pre + '/refund_orders')
def refund_orders(ctx, querytype, nos):
    user = ctx.session.user
    req_data = json.loads(req_data_cache.get(ctx.session.session_key + 'refund'))
    if check_business(ctx.session.groups):
        # 判断是商务并且非商务leader
        members = UserPerm.members(user)
        init_q = Q(order__service__doctor__business_partener__in=members)
    else:
        init_q = None
    dtobj = StaleRefundOrderDQ(RefundOrder, init_q=init_q)
    if nos != 'selectall':
        refund_orders = RefundOrder.objects.filter(id__in=nos)
    else:
        if req_data.get('paging'):
            req_data.pop('paging')
        if req_data.get('columns'):
            req_data.pop('columns')
        refund_orders = dtobj.build_queryset(**req_data)
    if 'excel' in querytype:
        kwargs = {
            'refund_orders': refund_orders,
            'to_user_email': user.email,
        }
    else:
        kwargs = {
            'refund_orders': refund_orders,
        }
    return getattr(OrderAction, querytype)(**kwargs)


@bind_context(uri_cashback + '/edit')
def cashback_order_edit(ctx, cashback_order_id, cashback_order_info=None):
    if cashback_order_info is None:
        return None

    try:
        cashback_order = CashBackOrder.objects.get(id=cashback_order_id)
    except:
        info_logger.info(__import__('traceback').format_exc())
        raise RPCNotFoundException
    for k, v in cashback_order_info.iteritems():
        setattr(cashback_order, k, v)
    cashback_order.save()
    return {
        'cashback_id': cashback_order.id,
    }


@bind_context(uri_cashback + '/get')
def cashback_detail(ctx, cashback_id, options=None):
    try:
        cashback = CashBackOrder.objects.get(id=cashback_id)
        order = cashback.order
    except:
        __import__('traceback').print_exc()
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': {
                'user': ['id', 'last_name'],
                'service': ['id', 'name'],
                'operate_user': ['id', 'last_name'],
            },
        }
    order_data = to_dict(order, **options)
    options = {
        'fields': None,
        'excludes': None,
        'expands': None,
    }
    order_data['cashback'] = to_dict(cashback, **options)
    order_data['order_multiattribute'] = order.order_multiattribute
    order_data['can_cashback'] = can_cashback(cashback)
    order_data['source'] = ORDER_SOURCE.getDesc(order.source)
    order_data['service_snapshot'] = json.loads(order.service_snapshot) \
        if order.service_snapshot else {}
    order_data['order_coupon_info'] = {x['coupon_type']: x for x in order.get_order_coupon_infos()}
    if order.service.doctor:
        order_data['doctor'] = {
            'id': order.service.doctor.id,
            'name': order.service.doctor.name,
        }
    else:
        order_data['doctor'] = {'id': '', 'name': ''}
    if order.service.doctor.hospital:
        order_data['hospital'] = {
            'id': order.service.doctor.hospital.id,
            'name': order.service.doctor.hospital.name,
        }
    else:
        order_data['hospital'] = {'id': '', 'name': ''}
    order_data['operations'] = []
    for operation in order.orderoperation_set.all():
        op = {
            "role": ORDER_OPERATION_ROLE.getDesc(operation.role),
            "user": operation.operator.user.last_name or operation.operator.user.username,
            "operation": ORDER_OPERATION_TYPE.getDesc(operation.optype),
            "time": str(operation.operate_at),
        }
        order_data['operations'].append(op)
    order_data['fee'] = cashback.fee
    order_data['topic_cash_back_limit'] = cashback.topic_cash_back_limit
    order_data['gm_send_refund_no'] = get_refund_no(cashback.order)
    return order_data


@bind_context(uri_refund + '/stale_refund')
def stale_refund(ctx, refund_order_id):
    try:
        refund_order = RefundOrder.objects.get(pk=refund_order_id)
    except RefundOrder.DoesNotExist:
        raise gen(CODES.ORDER_REFUDN_NOT_FOUND)
    user = get_user_from_context(ctx)
    get_rpc_remote_invoker()['plutus/insurance/lose_efficacy'](order_id=refund_order.order_id).unwrap()
    refund_order.order.operate(user.person, ORDER_OPERATION_TYPE.STALE_REFUND, ORDER_OPERATION_ROLE.STAFF)


@bind_context(uri_cashback + '/stale_cashback')
def stale_cashback(ctx, cashback_order_id):
    try:
        cashback_order = CashBackOrder.objects.get(pk=cashback_order_id)
    except CashBackOrder.DoesNotExist:
        raise gen(CODES.ORDER_CASHBACK_NOT_FOUND)
    user = get_user_from_context(ctx)
    cashback_order.order.operate(user.person, ORDER_OPERATION_TYPE.STALE_CASHBACK, ORDER_OPERATION_ROLE.STAFF)


@bind_context(uri_cashback + '/can_cashback')
def cashback_can_cashback(ctx, cashback_order_id):
    try:
        cashback_order = CashBackOrder.objects.get(id=cashback_order_id)
    except:
        raise RPCNotFoundException

    return cashback_order.can_cash_back()
    # return can_cashback(cashback_order)


def can_cashback(cashback_order):
    order = cashback_order.order
    try:
        diary = Diary.objects.get(order_id=str(order.id))
    except Diary.DoesNotExist:
        return {
            'can_cash_back': False,
            'message': u"日记本不存在",
        }

    if order.cash_back_status == CASH_BACK_STATUS.SUCCESS:
        return {
            'can_cash_back': False,
            'message': u"改返现单已返现过",
        }

    has_comments = diary.comment or diary.rating > 0
    if not has_comments:
        return {
            'can_cash_back': False,
            'message': u"该返现单没有评价",
        }

    if order.cashback.fee:
        cash_back = order.cashback.fee
    else:
        return {
            'can_cash_back': False,
            'message': u"返现额为0",
        }

    topic_num_threshold = order.cashback.topic_cash_back_limit
    total_valid_topics = diary.topics.filter(review_status=PROBLEM_REVIEW_STATUS_CHOICES.OK, is_online=True).count()
    if total_valid_topics >= topic_num_threshold:
        return {
            'can_cash_back': True,
            'message': u"可以返现",
            'order_id': cashback_order.order.id,
        }
    return {
        'can_cash_back': False,
        'message': u"审核通过贴子数不足",
    }


@bind_context(uri_pre + '/comments')
def order_comments(ctx, order_id, comments):
    user = get_user_from_context(ctx)
    now = datetime.datetime.now()
    comments = u'{}  备注人:{} 备注时间:{}\n'.format(
        comments.replace('\n', ' '),
        user.last_name or user.username,
        now.strftime("%Y-%m-%d %H:%M:%S"),
    )
    try:
        order = Order.objects.get(id=order_id)
    except:
        return {
            'error': 1,
            'message': u'该订单不存在',
        }
    try:
        order.comments = order.comments + comments
        order.save()
    except:
        return {
            'error': 1,
            'message': u'保存出错',
        }
    return gen(CODES.SUCCESS)


@bind_context(uri_pre + '/set_self_support')
def set_self_support(ctx, order_id):
    try:
        order = Order.objects.get(id=order_id)
        service_snapshot = ServiceSnapshot.objects.get(order_id=order.id)
    except:
        raise RPCNotFoundException
    # 如果订单下单时是自营订单,则可以做处理,下单时不是自营订单的不作处理 add by lipeng hera1.12 for hera_588
    if order.is_self_support:
        order.discount = service_snapshot.discount
        order.is_self_support = False
        order.save()
        return gen(CODES.SUCCESS)
    # 如果下单时不是自营订单,抛出业务异常(不支持的操作) add by lipeng hera1.12 for hera_588
    else:
        return gen(CODES.OPERATION_NOT_SUPPORTED)


@bind_context(uri_cashback + '/stale_datatable')
def stale_cashback_datatable(ctx, req_data):
    if check_business(ctx.session.groups):
        # 判断是商务并且非商务leader
        user = ctx.session.user
        members = UserPerm.members(user)
        init_q = Q(order__service__doctor__business_partener__in=members) & Q(stale=True)
    elif check_operate(ctx.session.user_id):
        business_ids = operate_business(ctx.session.user_id)
        init_q = Q(service__doctor__business_partener__id__in=business_ids) & Q(stale=True)
    else:
        init_q = Q(stale=True)

    req_data_cache.set(ctx.session.session_key + 'cashback', json.dumps(req_data))
    dtobj = CashBackOrderDT(CashBackOrder, init_q=init_q)
    return dtobj.process(req_data, [
        'order__service__doctor__name', 'order__service__doctor__hospital__name',
        'order__service__name', 'order__address', 'order__user__last_name',
        'order__phone', 'id', 'order__id',
    ]
                         )


@bind_context(uri_refund + '/stale_query')
def stale_refund_query(ctx, options):
    if check_business(ctx.session.groups):
        # 判断是商务并且非商务leader
        user = ctx.session.user
        members = UserPerm.members(user)
        init_q = Q(order__service__doctor__business_partener__in=members) & Q(stale=True)
    elif check_operate(ctx.session.user_id):
        business_ids = operate_business(ctx.session.user_id)
        init_q = Q(service__doctor__business_partener__id__in=business_ids) & Q(stale=True)
    else:
        init_q = Q(stale=True)
    req_data_cache.set(ctx.session.session_key + 'refund', json.dumps(options))
    dqobj = StaleRefundOrderDQ(init_q=init_q)
    return dqobj.process(**options)


@bind_context(uri_refund + '/stale_datatable')
def stale_refund_datatable(ctx, req_data):
    if check_business(ctx.session.groups):
        # 判断是商务并且非商务leader
        user = ctx.session.user
        members = UserPerm.members(user)
        init_q = Q(order__service__doctor__business_partener__in=members) & Q(stale=True)
    elif check_operate(ctx.session.user_id):
        business_ids = operate_business(ctx.session.user_id)
        init_q = Q(service__doctor__business_partener__id__in=business_ids) & Q(stale=True)
    else:
        init_q = Q(stale=True)

    req_data_cache.set(ctx.session.session_key + 'refund', json.dumps(req_data))
    dtobj = RefundOrderDT(RefundOrder, init_q=init_q)
    return dtobj.process(req_data, [
        'order__service__doctor__name', 'order__service__doctor__hospital__name',
        'order__service__name', 'order__address', 'order__user__last_name',
        'order__phone', 'id', 'order__id',
    ]
                         )


@bind_context(uri_cashback + '/getbyorderid')
def cashback_detail(ctx, order_id, options=None):
    try:
        cashback = CashBackOrder.objects.get(order_id=order_id)
    except:
        __import__('traceback').print_exc()
        raise RPCNotFoundException
    return cashback.id


@bind_context(uri_refund + '/getbyorderid')
def cashback_detail(ctx, order_id, options=None):
    try:
        refundorder = RefundOrder.objects.get(order_id=order_id)
    except:
        __import__('traceback').print_exc()
        raise RPCNotFoundException
    return refundorder.id


@bind_context(uri_pre + '/getstatus')
def order_getstatus(ctx, order_id):
    """
    根据订单id获取订单的状态
    :param order_id: 订单ID
    """
    try:
        order = Order.objects.get(pk=order_id)
    except:
        __import__('traceback').print_exc()
        raise RPCNotFoundException

    return order.status


@bind_context(uri_pre + '/getcashbackstatusby_orderid')
def cashback_getstatus_by_orderid(ctx, order_id):
    """
    根据订单获取返现单状态
    :param ctx:
    :param order_id: 订单ID
    """
    try:
        cashback = CashBackOrder.objects.get(order__id=order_id)
    except:
        __import__('traceback').print_exc()
        raise RPCNotFoundException
    return cashback.status


@bind_context(uri_pre + '/mark_refund')
def refund(ctx, order_id):
    """
    标记退款
    :param order_id:订单ID
    """
    try:
        order = Order.objects.get(pk=order_id)
    except Order.DoesNotExist:
        raise RPCNotFoundException
    try:
        refund_order = order.refund
    except RefundOrder.DoesNotExist:
        raise RPCNotFoundException
    if order.status != ORDER_STATUS.WAIT_REFUNDED:
        raise gen(CODES.ORDER_WRONG_STATUS_CODE)
    if refund_order.status not in (REFUND_STATUS.DOCTOR_APPROVE, REFUND_STATUS.REFUND_APPLY_SELLER_TIMEOUT):
        raise gen(CODES.ORDER_REFUDN_STATUS_ERROR)
    if refund_order.error is False:
        raise gen(CODES.NO_PERMISSION)
    if refund_order.order.payment_channel == PAYMENT_CHANNEL.WECHAT:
        wechat_refund = WechatRefund.objects.filter(order_no=refund_order.order_id).last()
        if wechat_refund.err_code != 'SYSTEMERROR':
            raise gen(CODES.NO_PERMISSION)
    with transaction.atomic():
        now = datetime.datetime.now()
        order.status = ORDER_STATUS.REFUNDED
        order.refund_time = now
        order.save()

        refund_order.status = REFUND_STATUS.REFUNDED
        refund_order.refunded_at = now
        refund_order.save()
        OrderOperation.objects.create(order=order,
                                      operator=ctx.session.user.person,
                                      optype=ORDER_OPERATION_TYPE.REFUNDED,
                                      role=ORDER_OPERATION_ROLE.STAFF,
                                      source=ORDER_SOURCE.BACKGROUND,
                                      operate_at=datetime.datetime.now())


@bind_context(uri_pre + '/mark_cashback')
def cashback(ctx, order_id):
    """
    标记返现
    :param order_id: 订单ID
    """
    try:
        order = Order.objects.get(pk=order_id)
    except Order.DoesNotExist:
        raise RPCNotFoundException
    try:
        cashback_order = order.cashback
    except CashBackOrder.DoesNotExist:
        raise RPCNotFoundException
    if cashback_order.error is False:
        raise gen(CODES.NO_PERMISSION)
    if order.status not in (ORDER_STATUS.USED, ORDER_STATUS.SETTLING, ORDER_STATUS.SETTLED):
        raise gen(CODES.ORDER_WRONG_STATUS_CODE)

    if cashback_order.status != NEW_CASH_BACK_STATUS.WAIT:
        raise gen(CODES.ORDER_WRONG_STATUS_CODE)
    if cashback_order.order.payment_channel == PAYMENT_CHANNEL.WECHAT:
        wechat_refund = WechatRefund.objects.filter(order_no=cashback_order.order_id).last()
        if wechat_refund.err_code != 'SYSTEMERROR':
            raise gen(CODES.NO_PERMISSION)

    with transaction.atomic():
        now = datetime.datetime.now()
        cashback_order.status = NEW_CASH_BACK_STATUS.SUCCESS
        cashback_order.cashbacked_at = now
        cashback_order.save()

        order.cash_back_time = now
        order.cash_back_status = CASH_BACK_STATUS.SUCCESS
        OrderOperation.objects.create(order=order,
                                      operator=ctx.session.user.person,
                                      optype=ORDER_OPERATION_TYPE.CASHBACKED,
                                      role=ORDER_OPERATION_ROLE.STAFF,
                                      source=ORDER_SOURCE.BACKGROUND,
                                      operate_at=datetime.datetime.now())


@bind_context(uri_pre + '/operate_query')
def operate_log_query(ctx, options):
    dqobj = OrderOperationDQ()
    # q = Q(optype__in=(ORDER_OPERATION_TYPE.CASHBACKED,ORDER_OPERATION_TYPE.REFUNDED))
    return dqobj.process(**options)


@bind_context(uri_refund + '/error_query')
def refunder_error_query(ctx, options):
    dqobj = RefundOrderErrorDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/get_orders')
def artemis_getorders(ctx, users, created_time):
    count = 0
    content = []
    for order in Order.objects.filter(user_id__in=users, validated=True, is_ordertoservice=False, created_time__gte=created_time):
        count = count + int(order.settle_data()['discount'])
        data = {}
        data['order_id'] = order.id
        data['discount'] = order.settle_data()['discount']
        data['validated'] = order.validated
        content.append(data)
    return {'amount': count, 'content': content}


@bind_context(uri_pre + '/change_ordertoservice')
def artemis_changeordertoservice(ctx, data):
    for content in data['content']:
        order = Order.objects.get(pk=content['order_id'])
        order.is_ordertoservice = True
        order.save()
    return {'message': 'success'}


@bind_context(uri_pre + '/operation_location')
def operationn_location(ctx, order_id, operation_location_type, operation_hospital_id):
    try:
        order = Order.objects.get(id=order_id)
    except:
        raise RPCNotFoundException
    order.operation_location_type = operation_location_type
    order.operation_hospital_id = operation_hospital_id
    order.save(update_fields=['operation_location_type', 'operation_hospital_id'])
    return {
        'message': 'success',
    }


@bind_context(uri_pre + '/today_sum_service_price_by_doctor_id')
def sum_service_price_by_doctor_id(ctx, doctor_ids, time_type):
    today = datetime.date.today()
    query = {
        'created_time': Q(created_time__gte=today),
        'pay_time': Q(pay_time__gte=today),
        'validate_time': Q(validate_time__gte=today),
    }
    q = query[time_type]
    queryset = Order.objects.using(settings.HERA_READ_DB or None)
    return queryset.filter(q, service__doctor_id__in=doctor_ids).aggregate(
        sum_service_price=Sum('service_price'),
        sum_discount=Sum('discount')
    )


@bind_context(uri_pre + '/order_export_excel')
def order_export_excel(ctx, self_order=False):
    """
    订单导出/自营订单导出
    :param ctx:
    :return:
    """
    init_q = Q()
    _in, businessman_list, team_groups = get_business_team(ctx)
    if _in:
        init_q = Q(servicesnapshot__doctor__business_partener__in=businessman_list) | \
                 Q(servicesnapshot__doctor__business_group__in=team_groups)
    if self_order:
        init_q &= Q(operation_location_type__in=[OPERATION_LOCATION_TYPE.OTHER, OPERATION_LOCATION_TYPE.WHITE_LIST])
    dqobj = OrderDQ(init_q=init_q)
    options = json.loads(req_data_cache.get(ctx.session.session_key))
    options.pop('paging')
    options.pop('columns')
    orders = dqobj.build_queryset(**options)
    if self_order:
        export_self_support_excel.delay(queryset_dec(orders, need_instant_data=True), ctx.session.user)
    else:
        export_orders_excel.delay(queryset_dec(orders, need_instant_data=True), ctx.session.user)


@bind_context(uri_pre + '/recently_orders')
def recently_orders(ctx, phone=None, from_date=None, end_date=None, offset=None, size=11, sorted=None):
    """
    GET orders by phone and use order's timesteamp and size to paginate
    query using created_time index
    :param ctx:
    :param phone:
    :param from_date:
    :param end_date:
    :param order_id:
    :param size:
    :return:
    """
    result = list()
    q_list = Q()

    if from_date:
        q_list.add(Q(created_time__gte=from_date), Q.AND)

    if end_date:
        q_list.add(Q(created_time__lte=end_date), Q.AND)

    if phone:
        q_list.add(Q(phone=phone), Q.AND)

    q_list.add(Q(status=ORDER_STATUS.USED), Q.AND)

    data = Order.objects. \
               values('id', 'service_id', 'service__name',
                      'service__doctor__hospital__city__name',
                      'service__doctor__hospital__city__id',
                      'status', 'created_time', 'validate_time',
                      'service_price', 'user_id', 'user__last_name'). \
               filter(q_list).all()[:size]
    for item in data:
        result.append({
            'id': item.get('id'),
            'service_id': item.get('service_id'),
            'service_name': item.get('service__name'),
            'status': item.get('status'),
            'status_name': ORDER_STATUS.getDesc(item.get('status'), '-1'),
            'created_time': datetime.datetime.strftime(item.get('created_time'),
                                                       '%Y-%m-%d %H:%M:%S'),
            'validate_time': datetime.datetime.strftime(item.get('validate_time'), '%Y-%m-%d %H:%M:%S') if item.get('validate_time', '') else '',
            'service_price': item.get('service_price'),
            'user_id': item.get('user_id'),
            'user_name': item.get('user__last_name'),
            'city_id': item.get('service__doctor__hospital__city__id'),
            'city_name': item.get('service__doctor__hospital__city__name')
        })
    return result


@bind_context(uri_pre+'/get_hospital_orders')
def get_hospital_orders(ctx, hospital_id=None):
    """
    Query orders by doctor id who is a company manager
    :param ctx:
    :param hospital_id:
    :return:
    """
    data = list()
    if hospital_id is None:
        return data
    from hippo.models import Merchant, MerchantRelevance
    doctor_ids = []
    merchant = Merchant.objects.filter(doctor_id=hospital_id).first()
    if merchant:
        doctor_ids = MerchantRelevance.objects.filter(merchant_id=merchant.id).values_list('doctor_id', flat=True)
    query_res = ServiceSnapshot.objects.\
        values('order_id', 'service_id', 'name', 'gengmei_price', 'total_price', 'discount').\
        filter(order__status=ORDER_STATUS.USED, doctor_id__in=doctor_ids)
    data = list(query_res)
    return data
