package com.jeequan.jeepay.pay.task;

import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.db.entity.OrderProfitSettRecord;
import com.jeequan.jeepay.db.entity.PayOrder;
import com.jeequan.jeepay.db.entity.RefundOrder;
import com.jeequan.jeepay.service.impl.OrderProfitSettRecordService;
import com.jeequan.jeepay.service.impl.PayOrderService;
import com.jeequan.jeepay.service.impl.RefundOrderService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutableTriple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/***
* 订单分润计算 定时任务
*
* @author terrfly
*
* @date 2022/3/23 14:12
*/
@Slf4j
@Component
public class OrderProfitSettTask {

    @Autowired private OrderProfitSettRecordService orderProfitSettRecordService;
    @Autowired private PayOrderService payOrderService;
    @Autowired private RefundOrderService refundOrderService;

    private static final int QUERY_PAGE_SIZE = 100; //每次查询数量


    @XxlJob("orderProfitSettTaskHandler")
    public void start() {

        log.info("开始【处理订单分润结算任务】 ");

        Date beginTime = new Date();

        Long allCount = 0L;

        // 错误订单集合， 避免分页死循环
        List<String> errorOrderIdList = new ArrayList<>();

        while(true){

            try {

                // 查询所有的待结算的订单
                // 此处的分页不是一般的分页， 由于任务的执行会导致订单状态的变化， 所以每次都查询第一页。
                IPage<String> resultPage = orderProfitSettRecordService.getBaseMapper()
                        .queryWaitSettPayOrderIdList(new Page(1, QUERY_PAGE_SIZE), errorOrderIdList.size() <= 0 ? null : errorOrderIdList);

                if(resultPage == null || resultPage.getRecords().isEmpty()){ //本次查询无结果, 不再继续查询;
                    break;
                }

                allCount += resultPage.getRecords().size();
                this.processQueryResult(resultPage.getRecords(), errorOrderIdList);

            } catch (Exception e) { //出现异常，直接退出，避免死循环。
                log.error("error", e);
                break;
            }
        }

        Long taskTime = (DateKit.currentTimeMillis() - beginTime.getTime()) / 1000;
        log.info("结束【处理订单分润结算任务】, 总条数： {}, 异常条数：{}, 处理时间：{}s ", allCount, errorOrderIdList.size(), taskTime );
    }


    @XxlJob("fixOrderProfitTaskHandler")
    public void fixOrderProfitTaskHandler() {

        log.info("开始【处理 修复订单分润数据 任务】 ");

        Date beginTime = new Date();

        // 查询今天之前的数据
        Date searchEndDate = DateUtil.beginOfDay(new Date());

        // 待处理订单
        List<PayOrder> allWaitProcessList = payOrderService.getBaseMapper().selectSuccessOrderWithProfitIsNull(searchEndDate);

        // 修复失败的订单 计算分润数据异常, al
        List<String> fixErrorList = new ArrayList<>();
        for (PayOrder payOrder : allWaitProcessList) {

            String payOrderId = payOrder.getPayOrderId();

            try {

                // 事务生效
                SpringUtil.getBean(OrderProfitSettTask.class).fixOrderProfitDate4Tx(payOrderId);

            }catch (Exception e){
                log.error("【处理 修复订单分润数据 任务】 修复失败， order={}", payOrderId, e);
                fixErrorList.add(payOrderId);
            }
        }

        Long taskTime = (DateKit.currentTimeMillis() - beginTime.getTime()) / 1000;
        log.info("结束【处理 修复订单分润数据 任务】, 总条数： {}, 修复失败条数：{}, 处理时间：{}s ", allWaitProcessList.size(), fixErrorList.size(), taskTime);
    }



    private void processQueryResult(List<String> result, List<String> errorOrderIdList){

        if(result != null){
            for (String payOrderId : result) {
                try {
                    orderProfitSettRecordService.profit2BalanceAmount(payOrderId, OrderProfitSettRecord.CAL_TYPE_PAY_ORDER);
                } catch (Exception e) {
                    log.error("处理订单结算异常！ payOrderId = {}",payOrderId, e);
                    errorOrderIdList.add(payOrderId);
                }
            }
        }
    }


    /** 同一个订单， 放置到 同一个事务中！  **/
    @Transactional
    public void fixOrderProfitDate4Tx(String payOrderId){

        boolean isRunTask = orderProfitSettRecordService.insertProfitByPayOrder(payOrderId, OrderProfitSettRecord.CAL_TYPE_PAY_ORDER);

        if(isRunTask){ // 实时结算
            orderProfitSettRecordService.profit2BalanceAmount(payOrderId, OrderProfitSettRecord.CAL_TYPE_PAY_ORDER);
        }

        // 查询退款订单数据
        List<RefundOrder> refundOrderList = refundOrderService.list(RefundOrder.gw().eq(RefundOrder::getPayOrderId, payOrderId)
                                    .eq(RefundOrder::getState, RefundOrder.STATE_SUCCESS));

        for (RefundOrder refundOrder : refundOrderList) {

            // 3. 退款退佣
            MutableTriple<String, Long, Long> calFeeAndSnapshot = orderProfitSettRecordService.insertProfitByRefundOrder(refundOrder.getRefundOrderId());
            if(calFeeAndSnapshot != null){


                // 更新到订单
                PayOrder updateRateRecord = new PayOrder().setPayOrderId(refundOrder.getPayOrderId()).setMchFeeAmount(calFeeAndSnapshot.middle).setMchFeeRate(calFeeAndSnapshot.left);
                payOrderService.updateById(updateRateRecord);

                // 更新到退款订单
                RefundOrder refundOrder2 = new RefundOrder().setRefundOrderId(refundOrder.getRefundOrderId()).setRefundFeeAmount(calFeeAndSnapshot.right);
                refundOrderService.updateById(refundOrder2);

            }
        }


    }


}
