package com.ruoyi.ss.payBill.service.impl;

import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.domain.vo.LocalDateDecimalVO;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.pay.PayApi;
import com.ruoyi.common.pay.ali.service.AliPayService;
import com.ruoyi.common.pay.syb.enums.SybTrxStatus;
import com.ruoyi.common.pay.syb.service.SybPayService;
import com.ruoyi.common.pay.tm.TmPayService;
import com.ruoyi.common.pay.tm.enums.TmPayStatus;
import com.ruoyi.common.pay.tm.vo.TmTradeInfo;
import com.ruoyi.common.pay.wx.service.WxPayService;
import com.ruoyi.common.pay.wx.util.WxPayUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.SnowFlakeUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.ss.payBill.domain.PayBill;
import com.ruoyi.ss.payBill.domain.PayBillQuery;
import com.ruoyi.ss.payBill.domain.PayBillVO;
import com.ruoyi.ss.payBill.domain.bo.PayBillRefundBO;
import com.ruoyi.ss.payBill.domain.bo.RefundSuccessBO;
import com.ruoyi.ss.payBill.domain.dto.PayBillRefundDTO;
import com.ruoyi.ss.payBill.domain.enums.PayBillBstType;
import com.ruoyi.ss.payBill.domain.enums.PayBillStatus;
import com.ruoyi.ss.payBill.domain.vo.DoPayVO;
import com.ruoyi.ss.payBill.domain.vo.PayResultVO;
import com.ruoyi.ss.payBill.interfaces.AfterPay;
import com.ruoyi.ss.payBill.interfaces.AfterRefund;
import com.ruoyi.ss.payBill.mapper.PayBillMapper;
import com.ruoyi.ss.payBill.service.PayBillService;
import com.ruoyi.ss.refund.domain.RefundVO;
import com.ruoyi.ss.refund.service.RefundConverter;
import com.ruoyi.ss.refund.service.RefundService;
import com.ruoyi.ss.channel.domain.enums.ChannelEnum;
import com.wechat.pay.java.service.payments.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 支付订单Service业务层处理
 *
 * @author ruoyi
 * @date 2024-07-24
 */
@Service
@Slf4j
public class PayBillServiceImpl implements PayBillService
{
    @Autowired
    private PayBillMapper payBillMapper;

    @Autowired
    private RedisLock redisLock;

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    @Autowired
    private RefundConverter refundConverter;

    @Autowired
    private RefundService refundService;

    @Autowired
    private SybPayService sybPayService;

    @Autowired
    private TmPayService tmPayService;

    @Autowired
    private AliPayService aliPayService;

    /**
     * 查询支付订单
     *
     * @param payId 支付订单主键
     * @return 支付订单
     */
    @Override
    public PayBillVO selectPayBillByPayId(Long payId)
    {
        return payBillMapper.selectPayBillByPayId(payId);
    }

    /**
     * 查询支付订单列表
     *
     * @param payBill 支付订单
     * @return 支付订单
     */
    @Override
    public List<PayBillVO> selectPayBillList(PayBillQuery payBill)
    {
        return payBillMapper.selectPayBillList(payBill);
    }

    /**
     * 新增支付订单
     *
     * @param payBill 支付订单
     * @return 结果
     */
    @Override
    public int insertPayBill(PayBill payBill)
    {
        payBill.setPayNo(String.valueOf(SnowFlakeUtil.newId()));
        payBill.setCreateTime(DateUtils.getNowDate());
        return payBillMapper.insertPayBill(payBill);
    }

    /**
     * 修改支付订单
     *
     * @param payBill 支付订单
     * @return 结果
     */
    @Override
    public int updatePayBill(PayBill payBill)
    {
        return payBillMapper.updatePayBill(payBill);
    }

    /**
     * 批量删除支付订单
     *
     * @param payIds 需要删除的支付订单主键
     * @return 结果
     */
    @Override
    public int deletePayBillByPayIds(Long[] payIds)
    {
        return payBillMapper.deletePayBillByPayIds(payIds);
    }

    /**
     * 删除支付订单信息
     *
     * @param payId 支付订单主键
     * @return 结果
     */
    @Override
    public int deletePayBillByPayId(Long payId)
    {
        return payBillMapper.deletePayBillByPayId(payId);
    }

    @Override
    public DoPayVO createPayBill(PayBill bill) {
        ServiceUtil.assertion(bill == null, "待新增的支付订单不存在");
        ServiceUtil.assertion(StringUtils.isBlank(bill.getAccount()), "用户支付账号不存在");
        // 锁
        String lockKey = bill.getBstType() + bill.getBstId();
        if (!redisLock.lock(RedisLockKey.PAY_BILL, lockKey)) {
            return null;
        };
        try {
            // 尝试关闭旧的支付订单
            boolean close = this.closeByBstId(bill.getBstId(), bill.getBstType());
            ServiceUtil.assertion(!close, "关闭旧支付订单失败");

            return transactionTemplate.execute(status -> {
                // 插入订单
                bill.setStatus(PayBillStatus.PAYING.getStatus());
                int insert = this.insertPayBill(bill);
                ServiceUtil.assertion(insert != 1, "插入支付订单失败");

                // 支付
                return this.doPay(this.selectByPayNo(bill.getPayNo()));
            });

        } finally {
            redisLock.unlock(RedisLockKey.PAY_BILL, lockKey);
        }

    }

    @Override
    public boolean closeByBstId(Long bstId, String bstType) {
        if (bstId == null || bstType == null ) {
            return false;
        }
        // 查询未支付、支付中的订单
        PayBillQuery query = new PayBillQuery();
        query.setBstId(bstId);
        query.setBstType(bstType);
        return this.closePayBill(query);
    }

    private boolean closePayBill(PayBillQuery query) {
        if (query == null) {
            query = new PayBillQuery();
        }
        query.setStatusList(PayBillStatus.getCanCancelList());
        List<PayBillVO> payingList = this.selectPayBillList(query);
        if (CollectionUtils.isEmpty(payingList)) {
            return true;
        }

        // 关闭正在支付中的订单
        transactionTemplate.execute(status -> {
            // 修改状态
            PayBill closeData = new PayBillVO();
            closeData.setStatus(PayBillStatus.CANCEL.getStatus());
            PayBillQuery updateQuery = new PayBillQuery();
            updateQuery.setStatusList(PayBillStatus.getCanCancelList());
            updateQuery.setPayIds(CollectionUtils.map(payingList, PayBillVO::getPayId));
            int update = this.updateByQuery(closeData, updateQuery);
            ServiceUtil.assertion(update != payingList.size(), "修改订单状态失败");

            // 关闭订单
            for (PayBillVO bill : payingList) {
                // 关闭支付中的订单
                if (PayBillStatus.PAYING.getStatus().equals(bill.getStatus())) {
                    // 获取支付API
                    PayApi payApi = ChannelEnum.getPayApi(bill.getChannelId());
                    if (payApi == null) {
                        continue;
                    }
                    // 调用关闭API
                    try {
                        payApi.closeByOutTradeNo(bill.getPayNo());
                    } catch (Exception e) {
                        // 关闭失败，尝试查询订单信息，判断是否已经支付成功
                        if (payApi.isPaySuccessByOutTradeNo(bill.getPayNo())) {
                            throw new ServiceException("当前交易已成功，无法关闭");
                        } else {
                            log.error("关闭支付订单失败: payNo = {} 原因：{}", bill.getPayNo(), e.getMessage());
                        }
                    }
                }
            }

            return update;
        });

        return true;
    }

    @Override
    public List<PayBillVO> selectByBstId(Long bstId, String bstType) {
        return payBillMapper.selectByBstId(bstId, bstType);
    }

    @Override
    public PayBillVO selectByPayNo(String payNo) {
        return payBillMapper.selectByPayNo(payNo);
    }

    @Override
    public int updateByQuery(PayBill data, PayBillQuery query) {
        if (data == null || query == null) {
            return 0;
        }
        return payBillMapper.updatePayQuery(data, query);
    }

    @Override
    public void handleSuccess(String payNo, LocalDateTime payTime) {
        if (payNo == null) {
            return;
        }
        PayBillVO payBill = this.selectByPayNo(payNo);
        this.handleSuccess(payBill, payTime);
    }

    private void handleSuccess(PayBillVO payBill, LocalDateTime payTime) {
        ServiceUtil.assertion(payBill == null, "支付订单不存在");
        Long lockKey = payBill.getPayId();

//        ServiceUtil.assertion(redisLock.lock(RedisLockKey.PAY_BILL_SUCCESS, lockKey), "支付订单正在处理中:payId=" + payBill.getPayId());
        try {
            // 若不是支付成功的状态，则修改为支付成功
            if (!PayBillStatus.payedList().contains(payBill.getStatus())) {
                ServiceUtil.assertion(!PayBillStatus.PAYING.getStatus().equals(payBill.getStatus()), "该支付订单不是正在支付的支付订单");

                Integer result = transactionTemplate.execute(status -> {
                    // 修改支付订单状态
                    PayBill data = new PayBill();
                    data.setStatus(PayBillStatus.PAY_SUCCESS.getStatus());
                    data.setPayTime(payTime);
                    PayBillQuery query = new PayBillQuery();
                    query.setStatus(PayBillStatus.PAYING.getStatus());
                    query.setPayId(payBill.getPayId());
                    int update = this.updateByQuery(data, query);
                    ServiceUtil.assertion(update != 1, "支付订单状态已改变，请稍后再试");

                    return update;
                });
            }

            // 获取修改后的数据
            PayBillVO bill = this.selectPayBillByPayId(payBill.getPayId());
            // 若为成功，则处理业务
            if (PayBillStatus.PAY_SUCCESS.getStatus().equals(bill.getStatus())) {
                PayBillBstType bstType = PayBillBstType.parse(payBill.getBstType());
                if (bstType != null && bstType.getAfterPay() != null) {
                    PayBillVO newPayBill = selectPayBillByPayId(payBill.getPayId());
                    AfterPay afterPay = SpringUtils.getBean(bstType.getAfterPay());
                    int bstResult = afterPay.onPaySuccess(newPayBill);
                    ServiceUtil.assertion(bstResult == 0, "业务处理失败");
                };
            }
        } finally {
//            redisLock.unlock(RedisLockKey.PAY_BILL_SUCCESS, lockKey);
        }
    }

    @Override
    public DoPayVO doPay(PayBillVO bill) {
        DoPayVO vo = new DoPayVO();
        vo.setPayBill(bill);

        // 若支付金额为0，则不调起支付
        if (bill.getAmount().compareTo(BigDecimal.ZERO) == 0) {
            vo.setPayParams("支付金额为0，无需调起支付");
            // 异步处理支付成功
            scheduledExecutorService.schedule(() -> {
                this.handleSuccess(bill.getPayNo(), LocalDateTime.now());
            }, 1L, TimeUnit.SECONDS);
        } else {
            PayApi payApi = ChannelEnum.getPayApi(bill.getChannelId());
            ServiceUtil.assertion(payApi == null, "暂不支持该支付方式");
            vo.setPayParams(payApi.pay(bill));
            // 异步刷新支付结果
            scheduledExecutorService.schedule(() -> {
                this.refreshPayResultMaxCount(bill, 20L, TimeUnit.SECONDS, 10);
            }, 10, TimeUnit.SECONDS);
        }

        return vo;
    }

    @Override
    public int refreshPayResult(List<PayBillVO> payList) {
        if (CollectionUtils.isEmptyElement(payList)) {
            return 0;
        }
        for (PayBillVO bill : payList) {
            if (PayBillStatus.PAYING.getStatus().equals(bill.getStatus()) || PayBillStatus.WAIT_PAY.getStatus().equals(bill.getStatus())  ) {
                PayResultVO payResult = getPayResult(bill);
                if (payResult.getSuccess()) {
                    this.handleSuccess(bill, payResult.getPayTime());
                }
            }
        }
        return payList.size();
    }

    @Override
    public int refreshPayResult(String payNo) {
        PayBillVO payBill = this.selectByPayNo(payNo);
        return this.refreshPayResult(Collections.singletonList(payBill));
    }

    @Override
    public int refund(PayBillRefundDTO dto) {
        // 校验订单
        PayBillVO bill = this.selectPayBillByPayId(dto.getPayId());
        if (bill == null) {
            return 0;
        }

        // 构造BO
        PayBillRefundBO bo = new PayBillRefundBO();
        bo.setDto(dto);
        bo.setPayBill(bill);

        BigDecimal totalRefundAmount = dto.getRefundAmount().add(bill.getRefundAmount()).add(bill.getRefundingAmount());
        ServiceUtil.assertion(totalRefundAmount.compareTo(bill.getAmount()) > 0, "总退款金额不允许大于订单金额");
        ServiceUtil.assertion(!PayBillStatus.PAY_SUCCESS.getStatus().equals(bill.getStatus()), "当前状态不允许退款");

        Integer result = transactionTemplate.execute(status -> {
            // 修改订单状态
            PayBill data = new PayBill();
            data.setStatus(PayBillStatus.REFUNDING.getStatus());
            PayBillQuery billQuery = new PayBillQuery();
            billQuery.setPayId(dto.getPayId());
            billQuery.setStatus(PayBillStatus.PAY_SUCCESS.getStatus());
            int updateBill = this.updateByQuery(data, billQuery);
            ServiceUtil.assertion(updateBill != 1, "修改支付订单状态失败");

            // 增加退款中金额
            int addRefunding = payBillMapper.addRefundingAmount(dto.getPayId(), dto.getRefundAmount());
            ServiceUtil.assertion(addRefunding != 1, "记录退款中的金额失败");

            // 创建退款订单
            int refund = refundService.createRefund(refundConverter.toPo(bo));
            ServiceUtil.assertion(refund != 1, "申请退款失败");

            return updateBill;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int handleRefundSuccess(RefundVO refund) {
        if (refund == null) {
            return 0;
        }

        Integer result = transactionTemplate.execute(status -> {
            // 修改支付订单状态
            PayBill data = new PayBillVO();
            data.setStatus(PayBillStatus.REFUNDED.getStatus());
            PayBillQuery query = new PayBillQuery();
            query.setPayId(refund.getBillId());
            query.setStatus(PayBillStatus.REFUNDING.getStatus());
            int update = this.updateByQuery(data, query);
            ServiceUtil.assertion(update != 1, "支付订单状态已发生变化，请刷新后重试");

            // 记录已退款金额，并修改状态
            int recordAmount = payBillMapper.recordRefundAmount(refund.getBillId(), refund.getAmount());
            ServiceUtil.assertion(recordAmount != 1, "修改退款金额失败");

            // 修改业务
            PayBillVO pay = this.selectPayBillByPayId(refund.getBillId());
            PayBillBstType bstType = PayBillBstType.parse(pay.getBstType());
            if (bstType != null && bstType.getAfterRefund() != null) {
                AfterRefund handler = SpringUtils.getBean(bstType.getAfterRefund());
                RefundSuccessBO bo = new RefundSuccessBO();
                bo.setRefund(refund);
                bo.setPay(pay);
                int bstResult = handler.onRefundSuccess(bo);
                ServiceUtil.assertion(bstResult != 1, "退款业务修改失败");
            }

            return update;
        });

        return result == null ? 0 : result;
    }

    @Override
    public void refreshPayResultMaxCount(PayBillVO pay, long delay, TimeUnit timeUnit, int count) {
        if (pay == null) {
            return;
        }

        if (count <= 0) {
            this.closeById(pay.getPayId());
            return;
        }

        // 刷新支付结果
        refreshPayResult(Collections.singletonList(pay));

        // 获取刷新后的订单
        PayBillVO newPay = this.selectPayBillByPayId(pay.getPayId());
        if (newPay == null) {
            log.error("获取订单{}为空", pay.getPayNo());
            return;
        }

        // 未支付成功
        if (PayBillStatus.PAYING.getStatus().equals(newPay.getStatus())) {
            log.info("{}未支付成功，继续延迟刷新, 剩余次数:{}", newPay.getPayNo(), count - 1);
            scheduledExecutorService.schedule(() -> {
                this.refreshPayResultMaxCount(newPay, delay , timeUnit, count - 1);
            }, delay, timeUnit);
        }
    }

    @Override
    public PayResultVO getPayResult(String payNo) {
        PayBillVO bill = selectByPayNo(payNo);
        return getPayResult(bill);
    }

    @Override
    public List<LocalDateDecimalVO> selectDailyAmount(PayBillQuery query) {
        return payBillMapper.selectDailyAmount(query);
    }

    private boolean closeById(Long payId) {
        if (payId == null) {
            return false;
        }
        // 查询未支付、支付中的订单
        PayBillQuery query = new PayBillQuery();
        query.setPayId(payId);
        return this.closePayBill(query);
    }

    /**
     * 获取支付结果
     */
    private PayResultVO getPayResult(PayBillVO bill) {
        if (bill == null) {
            return PayResultVO.fail("支付订单不存在");
        }

        // 若支付金额为0，则直接返回支付成功
        if (bill.getAmount().compareTo(BigDecimal.ZERO) == 0) {
            return PayResultVO.success(LocalDateTime.now());
        }

        // 获取支付API
        PayApi payApi = ChannelEnum.getPayApi(bill.getChannelId());
        if (payApi == null) {
            return PayResultVO.fail("暂不支持该支付方式");
        }

        // 获取支付结果
        Object result = payApi.queryByOutTradeNo(bill.getPayNo());
        if (payApi.isPaySuccess(result)) {
            return PayResultVO.success(payApi.getPayTime(result), result);
        } else {
            return PayResultVO.fail("暂未支付成功");
        }
    }
}
