
package com.tmerclub.cloud.payment.manager;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.auth.constant.SysTypeEnum;
import com.tmerclub.cloud.api.feign.SearchOrderFeignClient;
import com.tmerclub.cloud.api.flow.feign.FlowFeignClient;
import com.tmerclub.cloud.api.multishop.feign.ShopWalletFeignClient;
import com.tmerclub.cloud.api.multishop.feign.ShopWalletLogFeignClient;
import com.tmerclub.cloud.api.order.constant.OrderStatus;
import com.tmerclub.cloud.api.order.feign.OrderFeignClient;
import com.tmerclub.cloud.api.order.feign.OrderSettlementFeignClient;
import com.tmerclub.cloud.api.order.vo.OrderSettlementPayVO;
import com.tmerclub.cloud.api.seckill.feign.SeckillFeignClient;
import com.tmerclub.cloud.api.user.feign.UserBalanceLogFeignClient;
import com.tmerclub.cloud.api.user.feign.UserLevelLogFeignClient;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.constant.PayType;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.order.bo.OrderStatusBO;
import com.tmerclub.cloud.common.order.bo.PayRefundBO;
import com.tmerclub.cloud.common.order.vo.ShopCartOrderMergerVO;
import com.tmerclub.cloud.common.product.constant.PreSaleType;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.payment.allinpay.order.resp.OrderResultNotice;
import com.tmerclub.cloud.payment.bo.PayInfoResultBO;
import com.tmerclub.cloud.payment.constant.DistributedIdKey;
import com.tmerclub.cloud.payment.constant.PayStatus;
import com.tmerclub.cloud.payment.model.PayInfo;
import com.tmerclub.cloud.payment.service.PayInfoService;
import com.tmerclub.cloud.payment.service.RefundInfoService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author tmerclub
 * @date 2021/5/11
 */
@Component
public class PayNoticeManager {

    private static final Logger logger = LoggerFactory.getLogger(PayNoticeManager.class);

    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private PayInfoService payInfoService;
    @Autowired
    private RefundInfoService refundInfoService;
    @DubboReference
    private OrderFeignClient orderFeignClient;
    @DubboReference
    private SeckillFeignClient seckillFeignClient;
    @DubboReference
    private UserBalanceLogFeignClient userBalanceLogFeignClient;
    @DubboReference
    private ShopWalletLogFeignClient shopWalletLogFeignClient;
    @DubboReference
    private UserLevelLogFeignClient userLevelLogFeignClient;
    @DubboReference
    private OrderSettlementFeignClient orderSettlementFeignClient;
    @DubboReference
    private SearchOrderFeignClient searchOrderFeignClient;
    @DubboReference
    private ShopWalletFeignClient shopWalletFeignClient;
    @DubboReference
    private FlowFeignClient flowFeignClient;

    /**
     * 订单付款回调处理
     * 该方法中调用其他服务如果出现的异常，直接抛出，不要继续执行后续的步骤
     *
     * @param payInfoResultBO
     * @param payInfo
     * @param paySysType
     * @return
     */
    public ResponseEntity<String> noticeOrder(PayInfoResultBO payInfoResultBO, PayInfo payInfo, Integer paySysType) {

        // 获取订单之前是不是有进行过支付，如果有进行过支付，那么本次支付将直接退款
        String[] orderIdStrArr = payInfo.getOrderIds().split(StrUtil.COMMA);
        List<Long> orderIdList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (String s : orderIdStrArr) {
            orderIdList.add(Long.valueOf(s));
        }

        ServerResponseEntity<List<OrderStatusBO>> ordersStatusResponse = orderFeignClient.getOrdersStatus(orderIdList);

        if (!ordersStatusResponse.isSuccess()) {
            throw new LuckException(ordersStatusResponse.getMsg());
        }
        long actualTotal = 0L;
        List<OrderStatusBO> orderStatusList = ordersStatusResponse.getData();

        // 查询一下redis中是否有数据，有表示是秒杀订单，处理一下
        ShopCartOrderMergerVO shopCartOrderMergerVO = handleSeckillOrderPay(payInfo.getUserId(), orderIdList, orderStatusList);

        for (OrderStatusBO orderStatusBO : orderStatusList) {
            // 如果是定金预售，用定金or尾款钱进行判断
            if (Objects.equals(orderStatusBO.getPreSaleType(), PreSaleType.DEPOSIT.value())) {
                long balanceAmount = orderStatusBO.getBalanceAmount() + orderStatusBO.getFreightAmount() - orderStatusBO.getPlatformFreeFreightAmount();
                long preSaleAmount = Objects.equals(orderStatusBO.getStatus(), OrderStatus.UNPAY.value()) ? orderStatusBO.getDepositAmount() : balanceAmount;
                actualTotal = actualTotal + preSaleAmount;
            } else {
                actualTotal = actualTotal + orderStatusBO.getActualTotal();
            }
            // 有其中一个订单已经支付过了
            if (!Objects.equals(orderStatusBO.getStatus(), OrderStatus.UNPAY.value()) && !Objects.equals(orderStatusBO.getStatus(), OrderStatus.WAIT_BALANCE.value())
                    && !Objects.equals(orderStatusBO.getStatus(), OrderStatus.CLOSE.value())) {
                ServerResponseEntity<OrderSettlementPayVO> settlementResponse = orderSettlementFeignClient.getPayIdByOrderId(orderStatusBO.getOrderId());
                if (!settlementResponse.isSuccess()) {
                    throw new LuckException(settlementResponse.getMsg());
                }
                OrderSettlementPayVO settlementPayVO = settlementResponse.getData();
                // 相同的支付单号就不判断了
                if (Objects.nonNull(settlementPayVO.getPayId()) && Objects.equals(settlementPayVO.getPayId(), payInfo.getPayId())) {
                    continue;
                }
                PayRefundBO payRefundBO = new PayRefundBO();
                payRefundBO.setRefundId(this.getOrderRefundId(payInfo.getUserId()));
                payRefundBO.setRefundAmount(payInfo.getPayAmount());
                payRefundBO.setOrderId(orderStatusBO.getOrderId());
                payRefundBO.setShopId(orderStatusBO.getShopId());
                payRefundBO.setPayId(payInfo.getPayId());
                payRefundBO.setOnlyRefund(1);
                refundInfoService.doRefund(payRefundBO);
                // 标记为退款
                payInfoService.markerRefund(payInfo.getPayId());
                return ResponseEntity.ok(payInfoResultBO.getSuccessString());
            }
        }

        // 如果订单实付金额和支付金额不一致，则退款
        if (actualTotal != payInfo.getPayAmount()) {
            logger.info("用户支付金额不对，执行退款{}", payInfo);
            logger.info("用户支付金额不对，订单当前还需要支付{}", actualTotal);
            PayRefundBO payRefundBO = new PayRefundBO();
            payRefundBO.setRefundId(this.getOrderRefundId(payInfo.getUserId()));
            payRefundBO.setRefundAmount(payInfo.getPayAmount());
            payRefundBO.setPayId(payInfo.getPayId());
            payRefundBO.setOnlyRefund(1);
            payRefundBO.setShopId(orderStatusList.get(0).getShopId());
            refundInfoService.doRefundByAmountError(payInfo, payRefundBO);
            return ResponseEntity.ok(payInfoResultBO.getSuccessString());
        }
        logger.info("支付成功,执行下一步{}", payInfo);
        // 真正的支付成功
        payInfoService.paySuccess(payInfoResultBO, payInfo, orderIdList, paySysType,shopCartOrderMergerVO);
        // 记录支付成功日志
        flowFeignClient.paySuccessLog(payInfo.getSystemModel(), payInfo.getOrderIds(), payInfo.getUserId());
        return ResponseEntity.ok(payInfoResultBO.getSuccessString());
    }


    public ResponseEntity<String> noticeRecharge(PayInfoResultBO payInfoResultBO, PayInfo payInfo) {

        // 获取订单之前是不是有进行过支付，如果有进行过支付，那么本次支付将直接退款
        Long rechargeLogId = Long.valueOf(payInfo.getOrderIds());

        boolean isShopUser = Objects.equals(payInfo.getSysType(), SysTypeEnum.MULTISHOP.value());
        ServerResponseEntity<Integer> ordersStatusResponse;
        if (isShopUser) {
            ordersStatusResponse = shopWalletLogFeignClient.getIsPay(rechargeLogId);
        } else {
            ordersStatusResponse = userBalanceLogFeignClient.getIsPay(rechargeLogId);
        }

        if (!ordersStatusResponse.isSuccess()) {
            return ResponseEntity.ok(ResponseEnum.EXCEPTION.value());
        }

        Integer isPay = ordersStatusResponse.getData();
        // 有其中一个订单已经支付过了
        if (Objects.equals(isPay, 1)) {
            PayRefundBO payRefundBO = new PayRefundBO();
            payRefundBO.setOrderId(rechargeLogId);
            payRefundBO.setRefundId(this.getOrderRefundId(payInfo.getUserId()));
            payRefundBO.setRefundAmount(payInfo.getPayAmount());
            payRefundBO.setPayId(payInfoResultBO.getPayId());
            payRefundBO.setOnlyRefund(1);
            refundInfoService.doRefund(payRefundBO);
            // 标记为退款
            payInfoService.markerRefund(payInfo.getPayId());
            return ResponseEntity.ok(payInfoResultBO.getSuccessString());
        }

        // 支付成功
        payInfoService.rechargeSuccess(payInfoResultBO, PayType.instance(payInfo.getPayType()), rechargeLogId, payInfo.getSysType());
        return ResponseEntity.ok(payInfoResultBO.getSuccessString());
    }

    public ResponseEntity<String> noticeBuyVip(PayInfoResultBO payInfoResultBO, PayInfo payInfo) {
        // 获取订单之前是不是有进行过支付，如果有进行过支付，那么本次支付将直接退款
        Long userLevelLogId = Long.valueOf(payInfo.getOrderIds());

        ServerResponseEntity<Integer> ordersStatusResponse = userLevelLogFeignClient.getIsPay(userLevelLogId);

        if (!ordersStatusResponse.isSuccess()) {
            return ResponseEntity.ok(ResponseEnum.EXCEPTION.value());
        }

        Integer isPay = ordersStatusResponse.getData();

        // 有其中一个订单已经支付过了
        if (Objects.equals(isPay, 1)) {
            PayRefundBO payRefundBO = new PayRefundBO();
            payRefundBO.setRefundId(this.getOrderRefundId(payInfo.getUserId()));
            payRefundBO.setRefundAmount(payInfo.getPayAmount());
            payRefundBO.setOrderId(userLevelLogId);
            payRefundBO.setPayId(payInfoResultBO.getPayId());
            payRefundBO.setOnlyRefund(1);
            refundInfoService.doRefund(payRefundBO);
            // 标记为退款
            payInfoService.markerRefund(payInfo.getPayId());
            return ResponseEntity.ok(payInfoResultBO.getSuccessString());
        }

        // 支付成功
        payInfoService.buyVipSuccess(payInfoResultBO, PayType.instance(payInfo.getPayType()), userLevelLogId);
        return ResponseEntity.ok(payInfoResultBO.getSuccessString());
    }

    public ResponseEntity<String> noticeBalancePay(PayInfoResultBO payInfoResultBO, PayInfo payInfo) {

        // 获取订单之前是不是有进行过采购，如果有进行过采购，那么本次支付将直接退款
        Long orderIds = Long.valueOf(payInfo.getOrderIds());

        ServerResponseEntity<Integer> ordersStatusResponse = shopWalletLogFeignClient.getShopBalancePayIsPay(orderIds);
        if (!ordersStatusResponse.isSuccess()) {
            return ResponseEntity.ok(ResponseEnum.EXCEPTION.value());
        }

        Integer isPay = ordersStatusResponse.getData();
        // 有其中一个订单已经支付过了
        if (Objects.equals(isPay, 1)) {
            PayRefundBO payRefundBO = new PayRefundBO();
            payRefundBO.setPayId(payInfoResultBO.getPayId());
            payRefundBO.setRefundId(this.getOrderRefundId(payInfo.getUserId()));
            payRefundBO.setRefundAmount(payInfo.getPayAmount());
            payRefundBO.setOrderId(orderIds);
            payRefundBO.setOnlyRefund(1);
            refundInfoService.doRefund(payRefundBO);
            // 标记为退款
            payInfoService.markerRefund(payInfo.getPayId());
            return ResponseEntity.ok(payInfoResultBO.getSuccessString());
        }

        // 支付成功
        payInfoService.payShopBalanceSuccess(payInfoResultBO, orderIds);
        return ResponseEntity.ok(payInfoResultBO.getSuccessString());
    }

    public ResponseEntity<String> noticeShopBalancePayRefundSpread(PayInfoResultBO payInfoResultBO, PayInfo payInfo) {

        // 获取订单之前是不是有进行过采购，如果有进行过自行处理费用，那么本次支付将直接退款
        Long orderIds = Long.valueOf(payInfo.getOrderIds());
        // TODO 如果管理员在操作自行处理退款的支付过程中，另一个管理员也进行了支付退款自行处理费用，需要退款这一笔
        ServerResponseEntity<Integer> ordersStatusResponse = shopWalletLogFeignClient.getRefundShopBalancePayIsPay(orderIds);
        if (!ordersStatusResponse.isSuccess()) {
            return ResponseEntity.ok(ResponseEnum.EXCEPTION.value());
        }

        Integer isPay = ordersStatusResponse.getData();
        // 有其中一个订单已经支付过了
        if (Objects.equals(isPay, 1)) {
            PayRefundBO payRefundBO = new PayRefundBO();
            payRefundBO.setOnlyRefund(1);
            payRefundBO.setRefundId(this.getOrderRefundId(payInfo.getUserId()));
            payRefundBO.setRefundAmount(payInfo.getPayAmount());
            payRefundBO.setOrderId(orderIds);
            payRefundBO.setPayId(payInfoResultBO.getPayId());
            refundInfoService.doRefund(payRefundBO);
            // 标记为退款
            payInfoService.markerRefund(payInfo.getPayId());
            return ResponseEntity.ok(payInfoResultBO.getSuccessString());
        }
        payInfoResultBO.setRefundId(payInfo.getRefundId());
        payInfoService.noticeShopBalancePayRefundSpread(payInfoResultBO, orderIds);
        return ResponseEntity.ok(payInfoResultBO.getSuccessString());
    }

    public ResponseEntity<String> settlementOrderByAllinpay(PayInfoResultBO payInfoResultBO, PayInfo payInfo) {
        payInfoService.noticeSettlementOrder(payInfoResultBO, payInfo);
        return ResponseEntity.ok(payInfoResultBO.getSuccessString());
    }

    @Transactional(rollbackFor = Exception.class)
    public void noticeAllinpayShopRecharge(OrderResultNotice orderResultNotice) {

        // 标记为支付成功状态
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(Long.valueOf(orderResultNotice.getBizOrderNo()));
        payInfo.setBizPayNo(orderResultNotice.getOrderNo());
        // 支付渠道交易单号
        payInfo.setCallbackContent(orderResultNotice.getChnltrxid());
        payInfo.setCallbackTime(new Date());
        payInfo.setPayStatus(PayStatus.PAYED.value());
        payInfoService.updateToSuccess(payInfo);

        // 充值订单只有支付成功才回调
        ServerResponseEntity<Void> response = shopWalletFeignClient.rechargeBack(orderResultNotice.getBizOrderNo(), orderResultNotice.getStatus(), orderResultNotice.getBuyerBizUserId());
        if (!response.isSuccess()) {
            logger.error("充值回调失败,{}", response.getMsg());
            throw new LuckException("充值回调失败");
        }
    }

    /**
     * 处理一下秒杀订单支付
     *
     * @param userId
     * @param orderIdList
     * @param orderStatusList
     */
    private ShopCartOrderMergerVO handleSeckillOrderPay(Long userId, List<Long> orderIdList, List<OrderStatusBO> orderStatusList) {
        Long orderId = orderIdList.get(0);
        ServerResponseEntity<ShopCartOrderMergerVO> shopCartOrderMergerResp = seckillFeignClient.getSeckillOrderByRedis(orderId, userId);
        if (!shopCartOrderMergerResp.isSuccess()) {
            throw new LuckException(shopCartOrderMergerResp.getMsg());
        }
        ShopCartOrderMergerVO shopCartOrderMergerVO = shopCartOrderMergerResp.getData();
        if (Objects.isNull(shopCartOrderMergerVO)) {
            return null;
        }
        if(CollectionUtil.isEmpty(orderStatusList)){
            // orderId，shopId,ActualTotal,status,preSaleType
            OrderStatusBO orderStatusBO = new OrderStatusBO();
            orderStatusBO.setActualTotal(shopCartOrderMergerVO.getActualTotal());
            orderStatusBO.setOrderId(orderIdList.get(0));
            orderStatusBO.setStatus(shopCartOrderMergerVO.getStatus());
            orderStatusBO.setShopId(shopCartOrderMergerVO.getShopCartOrders().get(0).getShopId());
            orderStatusBO.setPreSaleType(shopCartOrderMergerVO.getPreSaleType());
            orderStatusList.add(orderStatusBO);
        }
        return shopCartOrderMergerVO;
    }

    private Long getOrderRefundId(Long userId) {
        return segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_REFUND, userId);
    }
}
