package com.gree.mall.manager.schedule;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.gree.mall.manager.constant.Constant;
import com.gree.mall.manager.enums.IsYesNoEnum;
import com.gree.mall.manager.enums.material.PartsRefTypeEnum;
import com.gree.mall.manager.enums.material.PartsRefundStateEnum;
import com.gree.mall.manager.enums.material.PayOrderTypeEnum;
import com.gree.mall.manager.enums.material.WebsitGoodsTypeEnum;
import com.gree.mall.manager.logic.common.AllInPayLogic;
import com.gree.mall.manager.plus.entity.WebsitPartsNewRefundManageRecord;
import com.gree.mall.manager.plus.entity.WebsitPartsOldRefundManageRecord;
import com.gree.mall.manager.plus.entity.WebsitPartsRefundRecord;
import com.gree.mall.manager.plus.entity.WebsitSalesPayOrder;
import com.gree.mall.manager.plus.service.WebsitPartsNewRefundManageRecordService;
import com.gree.mall.manager.plus.service.WebsitPartsOldRefundManageRecordService;
import com.gree.mall.manager.plus.service.WebsitPartsRefundRecordService;
import com.gree.mall.manager.plus.service.WebsitSalesPayOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

@ConditionalOnProperty(name = "schedule.enable", havingValue = "true", matchIfMissing = true)
@Component
@Slf4j
public class PartsRefundSchedule {

    @Resource
    WebsitSalesPayOrderService websitSalesPayOrderService;
    @Resource
    WebsitPartsRefundRecordService websitPartsRefundRecordService;
    @Resource
    RedisLockRegistry redisLockRegistry;
    @Resource
    AllInPayLogic allInPayLogic;
    @Resource
    WebsitPartsNewRefundManageRecordService websitPartsNewRefundManageRecordService;
    @Resource
    WebsitPartsOldRefundManageRecordService websitPartsOldRefundManageRecordService;

    @Scheduled(initialDelay = 5000, fixedDelay = 5000)
    private void newPartsToSalesTasks() throws InterruptedException {
        //获取分布式锁
        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.PARTS_REFUND);
        if (!obtain.tryLock(10, TimeUnit.SECONDS)) {
            log.error("获取配件退款锁超时");
            return;
        }
        try {
            // 第一步 查询出未执行退款记录
            List<WebsitPartsRefundRecord> partsRefundRecords = websitPartsRefundRecordService.lambdaQuery()
                    .eq(WebsitPartsRefundRecord::getIsExec, false)
                    .eq(WebsitPartsRefundRecord::getRefundState, PartsRefundStateEnum.WAIT.getKey())
                    .orderByAsc(WebsitPartsRefundRecord::getCreateTime)
                    .list();

            // 第二步 按销售单号分组
            Map<String, List<WebsitPartsRefundRecord>> groupByIdMap = partsRefundRecords.stream()
                    .collect(Collectors.groupingBy(WebsitPartsRefundRecord::getSalesId));

            // 第三步 遍历销售单号分组
            for (List<WebsitPartsRefundRecord> partsRefundRecordList : groupByIdMap.values()) {
                try {
                    // 第四步 查询销售单号对应的支付记录
                    List<WebsitSalesPayOrder> payOrders = websitSalesPayOrderService.lambdaQuery()
                            .eq(WebsitSalesPayOrder::getGoodsType, WebsitGoodsTypeEnum.P.getKey())
                            .eq(WebsitSalesPayOrder::getWebsitId, partsRefundRecordList.get(0).getWebsitId())
                            .eq(WebsitSalesPayOrder::getOrderId, partsRefundRecordList.get(0).getSalesId())
                            .eq(WebsitSalesPayOrder::getPayFlag, IsYesNoEnum.YES.getKey())
                            .list();
                    if (payOrders.size() > 1) {
                        // 异常支付记录有多条
                        this.handleExceptionMethod(partsRefundRecordList, "异常支付记录有多条");
                        continue;
                    }
                    // 计算退款总额
                    BigDecimal totalRefundAmount = this.computeRefundAmount(partsRefundRecordList);
                    if (totalRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
                        this.handleExceptionMethod(partsRefundRecordList, "退款总额必须大于0");
                        continue;
                    }

                    WebsitSalesPayOrder payRecord = payOrders.get(0);

                    String refundId = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
                    // 调用通联退款接口
                    Map<String, String> resMap = allInPayLogic.serviceRefundOrder(payRecord, refundId, totalRefundAmount);

                    if ("0000".equals(resMap.get("trxstatus"))) {
                        // 更新支付记录的退款金额 如果退款金额等于支付金额支付记录的支付状态更新为退款
                        payRecord.setRetValue(payRecord.getRetValue().add(totalRefundAmount))
                                .updateById();
                        // 更新记录明细
                        String mchNo = resMap.get("cusid");
                        String refundSerialnumber = resMap.get("reqsn");
                        String refundChnltrxid = resMap.get("chnltrxid");
                        DateTime refundTime = DateUtil.parse(resMap.get("fintime"), "yyyyMMddHHmmss");
                        String refundAccountNo = payRecord.getOpenid();
                        WebsitSalesPayOrder refundOrder = new WebsitSalesPayOrder();

                        BeanUtils.copyProperties(payRecord, refundOrder);
                        refundOrder.setId(IdWorker.getIdStr())
                                .setOriginOrderId(payRecord.getOrderId())
                                .setOrderNo(refundId)
                                .setPayTime(refundTime)
                                .setPayValue(totalRefundAmount)
                                .setTransactionId(refundChnltrxid)
                                .setOrderType(PayOrderTypeEnum.R.getKey())
                                .setCreateTime(DateUtil.date())
                                .insert();


                        this.updateBillRecord(partsRefundRecordList, refundTime, mchNo, refundAccountNo, refundSerialnumber);
                    } else {
                        this.handleExceptionMethod(partsRefundRecordList, resMap.get("errmsg"));
                    }
                } catch (Exception e) {
                    this.handleExceptionMethod(partsRefundRecordList, e.getMessage());
                }
            }
        } finally {
            obtain.unlock();
        }
    }

    private void updateBillRecord(List<WebsitPartsRefundRecord> partsRefundRecordList, DateTime refundTime, String mchNo,
                                  String refundAccountNo, String refundSerialnumber) {
        for (WebsitPartsRefundRecord partsRefundRecord : partsRefundRecordList) {
            partsRefundRecord.setRefundState(PartsRefundStateEnum.OK.getKey())
                    .setIsExec(true)
                    .setInfo(refundSerialnumber);
            String refType = partsRefundRecord.getRefType();
            if (refType.equals(PartsRefTypeEnum.NEW_REFUND.getRemark())) {
                // 新件返还表
                websitPartsNewRefundManageRecordService.lambdaUpdate()
                        .set(WebsitPartsNewRefundManageRecord::getRefundTime, refundTime)
                        .set(WebsitPartsNewRefundManageRecord::getRefundState, PartsRefundStateEnum.OK.toString())
                        .set(WebsitPartsNewRefundManageRecord::getRefundMchNo, mchNo)
                        .set(WebsitPartsNewRefundManageRecord::getRefundAccountNo, refundAccountNo)
                        .set(WebsitPartsNewRefundManageRecord::getRefundSerialnumber, refundSerialnumber)
                        .eq(WebsitPartsNewRefundManageRecord::getId, partsRefundRecord.getRecordId())
                        .update();
            } else if (refType.equals(PartsRefTypeEnum.OLD_REFUND.getRemark()) || refType.equals(PartsRefTypeEnum.OUTSIDE_TO_SALES.getRemark())) {
                // 旧件返还表
                websitPartsOldRefundManageRecordService.lambdaUpdate()
                        .set(WebsitPartsOldRefundManageRecord::getRefundTime, refundTime)
                        .set(WebsitPartsOldRefundManageRecord::getRefundState, PartsRefundStateEnum.OK.toString())
                        .set(WebsitPartsOldRefundManageRecord::getRefundMchNo, mchNo)
                        .set(WebsitPartsOldRefundManageRecord::getRefundAccountNo, refundAccountNo)
                        .set(WebsitPartsOldRefundManageRecord::getRefundSerialnumber, refundSerialnumber)
                        .eq(WebsitPartsOldRefundManageRecord::getId, partsRefundRecord.getRecordId())
                        .update();
            }
        }
        websitPartsRefundRecordService.updateBatchById(partsRefundRecordList);
    }

    private BigDecimal computeRefundAmount(List<WebsitPartsRefundRecord> partsRefundRecordList) {
        BigDecimal refundAmount = BigDecimal.ZERO;
        for (WebsitPartsRefundRecord partsRefundRecord : partsRefundRecordList) {
            refundAmount = refundAmount.add(partsRefundRecord.getRefundAmount());
        }
        return refundAmount;
    }

    private void handleExceptionMethod(List<WebsitPartsRefundRecord> partsRefundRecordList, String info) {
        for (WebsitPartsRefundRecord partsRefundRecord : partsRefundRecordList) {
            partsRefundRecord.setRefundState(PartsRefundStateEnum.FAIL.toString())
                    .setIsExec(true)
                    .setInfo(info);
        }
        websitPartsRefundRecordService.updateBatchById(partsRefundRecordList);
    }
}
