package com.ruoyi.common.pay.wx.service;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.pay.PayApi;
import com.ruoyi.common.pay.Payable;
import com.ruoyi.common.pay.Refundable;
import com.ruoyi.common.pay.wx.config.WxPayConfig;
import com.ruoyi.common.pay.wx.util.WxPayUtil;
import com.ruoyi.common.utils.DateUtils;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 微信支付服务
 * @author 辉
 * 2024/3/11
 */
@Service
@Slf4j
public class WxPayService implements PayApi {

    private static final String CNY = "CNY";

    public PrepayWithRequestPaymentResponse pay(Payable payable, Object config) {
        WxPayConfig wxPayConfig = (WxPayConfig) config;
        return this.prepayWithRequestPayment(payable.payableOutTradeNo(), payable.payableFen(), payable.payableDescription(), payable.payableOpenId(), payable.payableAttach(), wxPayConfig);
    }

    public PrepayWithRequestPaymentResponse prepayWithRequestPayment(String billNo, BigDecimal money, String description, String wxOpenId, String attach, WxPayConfig wxPayConfig) {
        // 获取JSAPI所需参数
        PrepayRequest request = new PrepayRequest();
        request.setAmount(getAmount(money));
        request.setOutTradeNo(billNo);
        request.setAppid(wxPayConfig.getAppId());
        request.setMchid(wxPayConfig.getMerchantId());
        request.setDescription(description);
        request.setNotifyUrl(wxPayConfig.getNotifyUrl());
        request.setPayer(getPayer(wxOpenId));
        request.setAttach(attach);
        return wxPayConfig.jsapiServiceExtension().prepayWithRequestPayment(request);
    }

    /**
     * 关闭支付订单
     *
     * @param billNo 平台订单编号
     * @param config
     */
    public void closeByOutTradeNo(String billNo, Object config) {
        // 初始化配置
        WxPayConfig wxPayConfig = (WxPayConfig) config;

        CloseOrderRequest request = new CloseOrderRequest();
        request.setMchid(wxPayConfig.getMerchantId());
        request.setOutTradeNo(billNo);
        wxPayConfig.jsapiService().closeOrder(request);
    }

    public Transaction queryByOutTradeNo(String billNo, LocalDate orderDate, Object config) {
        // 初始化配置
        WxPayConfig wxPayConfig = (WxPayConfig) config;

        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(wxPayConfig.getMerchantId());
        request.setOutTradeNo(billNo);
        return wxPayConfig.jsapiService().queryOrderByOutTradeNo(request);
    }

    /**
     * 发起退款
     *
     * @param refund
     * @param config
     */
    public Refund refund(Refundable refund, Object config) {
        // 初始化配置
        WxPayConfig wxPayConfig = (WxPayConfig) config;

        CreateRequest request = new CreateRequest();
        request.setOutTradeNo(refund.refundOutTradeNo());
        request.setOutRefundNo(refund.refundOutRefundNo());
        request.setReason(refund.refundReason());
        request.setAmount(refund.refundAmount());
        request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
        log.info("【退款】请求微信参数: {}", JSON.toJSONString(request));
        Refund res = wxPayConfig.refundService().create(request);
        log.info("【退款】微信返回结果:【{}】",JSON.toJSONString(refund));
        return res;
    }

    @Override
    public boolean isPaySuccessByOutTradeNo(String outTradeNo, LocalDate orderDate, Object config) {
        return isPaySuccess(this.queryByOutTradeNo(outTradeNo, orderDate, config));
    }

    @Override
    public boolean isPaySuccess(Object result) {
        Transaction transaction = (Transaction) result;
        return WxPayUtil.isSuccess(transaction);
    }

    @Override
    public LocalDateTime getPayTime(Object result) {
        Transaction transaction = (Transaction) result;
        return DateUtils.toLocalDateTimeByISO(transaction.getSuccessTime());
    }

    private Payer getPayer(String openId) {
        Payer payer = new Payer();
        payer.setOpenid(openId);
        return payer;
    }

    private Amount getAmount(BigDecimal money) {
        Amount amount = new Amount();
        amount.setTotal(money.intValue());
        amount.setCurrency(CNY);
        return amount;
    }

}
