package com.ruoyi.newwxpay.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ValidatorUtils;
import com.ruoyi.newwxpay.config.NewWxPayProperties;
import com.ruoyi.newwxpay.exception.NewWxPayException;
import com.ruoyi.newwxpay.pay.PayNotifyResult;
import com.ruoyi.newwxpay.pay.WxMinPayRequest;
import com.ruoyi.newwxpay.refund.WxMinRefundRequest;
import com.ruoyi.newwxpay.transfer.*;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class NewWxPayService {

    @Resource(name = "rsaPublicKeyConfig")
    private RSAPublicKeyConfig rsaPublicKeyConfig;
    @Resource
    private NewWxPayProperties newWxPayProperties;


    /**
     * 小程序支付
     * @param param
     * @return
     */
    public Map<String, Object> wxMiniPay(WxMinPayRequest param) {
        ValidatorUtils.validate(param);

        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(rsaPublicKeyConfig).build();
        PrepayRequest request = new PrepayRequest();
        request.setAppid(newWxPayProperties.getAppId());
        request.setMchid(newWxPayProperties.getMchId());
        request.setNotifyUrl(newWxPayProperties.getPayNotifyUrl());

        Amount amount = new Amount();
        amount.setTotal(param.getMoney().multiply(BigDecimal.valueOf(100)).intValue());
        amount.setCurrency("CNY");
        request.setAmount(amount);

        request.setDescription(param.getDescription());
        request.setOutTradeNo(param.getOutTradeNo());
        request.setAttach("");
        request.setTimeExpire(param.getTimeExpireStr());
        Payer payer = new Payer();
        payer.setOpenid(param.getOpenId());
        request.setPayer(payer);
        //这里是设置是否需要分账，默认为true，表示需要分账
        SettleInfo settleInfo = new SettleInfo();
        settleInfo.setProfitSharing(param.getProfitSharing());
        request.setSettleInfo(settleInfo);
        // 调用下单方法，得到应答
        PrepayWithRequestPaymentResponse response  = service.prepayWithRequestPayment(request);
        Map<String, Object> packageParams = new HashMap<>();
        packageParams.put("appId", response.getAppId());
        packageParams.put("nonceStr", response.getNonceStr());
        packageParams.put("package",  response.getPackageVal());
        packageParams.put("timeStamp", response.getTimeStamp());
        packageParams.put("signType", response.getSignType());
        packageParams.put("paySign", response.getPaySign());
        //将组合后的商户订单号返回
        packageParams.put("out_trade_no", param.getOutTradeNo());
        packageParams.put("merchant_id", newWxPayProperties.getMchId());
        return packageParams;

    }


    /**
     * 支付回调
     * @param request
     * @throws Exception
     */
    public PayNotifyResult payNotify(HttpServletRequest request) throws Exception {
        log.info("进入微信支付回调");
        //读取请求体的信息
        ServletInputStream inputStream = request.getInputStream();
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s;
        //读取回调请求体
        while ((s = bufferedReader.readLine()) != null) {
            stringBuffer.append(s);
        }
        String s1 = stringBuffer.toString();
        String timestamp = request.getHeader(NewWxPayProperties.WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(NewWxPayProperties.WECHAT_PAY_NONCE);
        String signType = request.getHeader("Wechatpay-Signature-Type");
        String serialNo = request.getHeader(NewWxPayProperties.WECHAT_PAY_SERIAL);
        String signature = request.getHeader(NewWxPayProperties.WECHAT_PAY_SIGNATURE);

        NotificationParser parser = new NotificationParser(rsaPublicKeyConfig);
        RequestParam requestParam = new RequestParam.Builder()
            .serialNumber(serialNo)
            .nonce(nonce)
            .signature(signature)
            .timestamp(timestamp)
            .signType(signType)
            .body(s1)
            .build();
        Transaction parse = parser.parse(requestParam, Transaction.class);

        return PayNotifyResult.builder().outTradeNo(parse.getOutTradeNo()).transactionId(parse.getTransactionId()).tradeState(parse.getTradeState()).build();
    }


    /**
     * 小程序退款
     * @param param
     * @return
     */
    public Refund wxMiniRefund(WxMinRefundRequest param) {
        ValidatorUtils.validate(param);
        RefundService service = new RefundService.Builder().config(rsaPublicKeyConfig).build();
        CreateRequest createRequest = new CreateRequest();
        createRequest.setTransactionId(param.getTransactionId());
        createRequest.setOutRefundNo(param.getOutRefundNo());
        createRequest.setReason("退款");
        AmountReq amountReq = new AmountReq();
        amountReq.setTotal(param.getTotal().multiply(BigDecimal.valueOf(100)).longValue());
        amountReq.setRefund(param.getRefund().multiply(BigDecimal.valueOf(100)).longValue());
        amountReq.setCurrency("CNY");
        createRequest.setAmount(amountReq);
        createRequest.setNotifyUrl(newWxPayProperties.getRefundNotifyUrl());
        try {
            Refund refundRes = service.create(createRequest);
            if(Status.SUCCESS.equals(refundRes.getStatus()) || Status.PROCESSING.equals(refundRes.getStatus()))
            {
                return refundRes;
            }
            else if(Status.CLOSED.equals(refundRes.getStatus()))
            {
                throw new ServiceException("退款关闭");
            }
            else if(Status.ABNORMAL.equals(refundRes.getStatus()))
            {
                throw new ServiceException("退款异常");
            }
        }
        catch (com.wechat.pay.java.core.exception.ServiceException e)
        {
            log.error("微信退款失败",e);
            throw new ServiceException(String.format("微信退款失败,错识码:%s,描述：%s",e.getErrorCode(),e.getErrorMessage()));
        }
        catch (Exception ex)
        {
            log.error("微信退款失败",ex);
            throw new ServiceException(String.format("微信退款失败,描述：%s", ex.getMessage()));
        }
        return null;
    }

    /**
     * 退款回调
     * @param request
     * @throws Exception
     */
    public RefundNotification refundNotify(HttpServletRequest request) throws Exception {
        log.info("进入退款回调");
        //读取请求体的信息
        ServletInputStream inputStream = request.getInputStream();
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s;
        //读取回调请求体
        while ((s = bufferedReader.readLine()) != null) {
            stringBuffer.append(s);
        }
        String s1 = stringBuffer.toString();
        String timestamp = request.getHeader(NewWxPayProperties.WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(NewWxPayProperties.WECHAT_PAY_NONCE);
        String signType = request.getHeader("Wechatpay-Signature-Type");
        String serialNo = request.getHeader(NewWxPayProperties.WECHAT_PAY_SERIAL);
        String signature = request.getHeader(NewWxPayProperties.WECHAT_PAY_SIGNATURE);

        RequestParam requestParam = new RequestParam.Builder()
            .serialNumber(serialNo)
            .nonce(nonce)
            .signature(signature)
            .timestamp(timestamp)
            .signType(signType)
            .body(s1)
            .build();

        NotificationParser parser = new NotificationParser(rsaPublicKeyConfig);
        RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
        return parse;
    }

    /**
     * 转账回调
     * @param request
     * @return
     * @throws Exception
     */
    public TransferNotification transferNotify(HttpServletRequest request) throws Exception {
        log.info("进入退款回调");
        //读取请求体的信息
        ServletInputStream inputStream = request.getInputStream();
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s;
        //读取回调请求体
        while ((s = bufferedReader.readLine()) != null) {
            stringBuffer.append(s);
        }
        String s1 = stringBuffer.toString();
        String timestamp = request.getHeader(NewWxPayProperties.WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(NewWxPayProperties.WECHAT_PAY_NONCE);
        String signType = request.getHeader("Wechatpay-Signature-Type");
        String serialNo = request.getHeader(NewWxPayProperties.WECHAT_PAY_SERIAL);
        String signature = request.getHeader(NewWxPayProperties.WECHAT_PAY_SIGNATURE);

        RequestParam requestParam = new RequestParam.Builder()
            .serialNumber(serialNo)
            .nonce(nonce)
            .signature(signature)
            .timestamp(timestamp)
            .signType(signType)
            .body(s1)
            .build();

        NotificationParser parser = new NotificationParser(rsaPublicKeyConfig);
        TransferNotification parse = parser.parse(requestParam, TransferNotification.class);
        return parse;
    }


    /**
     * 单笔转账到零钱
     * @param request
     * @return
     */
    public InitiateSingleTransferResponse transferSingle(InitiateSingleTransferRequest request) {
        if(StringUtils.isBlank(request.getAppid()))
        {
            request.setAppid(newWxPayProperties.getAppId());
        }
        if(StringUtils.isBlank(request.getTransferSceneId()))
        {
            request.setTransferSceneId("1000");
        }

        if(StringUtils.isBlank(request.getTransferRemark()))
        {
            request.setTransferRemark("转账");
        }

        if(StringUtils.isBlank(request.getNotifyUrl()))
        {
            request.setNotifyUrl(newWxPayProperties.getTransferNotifyUrl());
        }

        ValidatorUtils.validate(request);


        TransferSingleService service = new TransferSingleService.Builder().config(rsaPublicKeyConfig).build();

        try {
            InitiateSingleTransferResponse initiateSingleTransferResponse = service.initiateSingleTransfer(request);
            log.error("转账信息,错误:{}", JSONUtil.toJsonStr(initiateSingleTransferResponse));
            TransferErrorCode transferErrorCode = TransferErrorCode.fromName(initiateSingleTransferResponse.getFailReason());
            TransferStatus transferStatus = TransferStatus.fromName(initiateSingleTransferResponse.getState());
            if (TransferStatus.SUCCESS.equals(transferStatus) ||
                TransferStatus.ACCEPTED.equals(transferStatus) ||
                TransferStatus.WAIT_USER_CONFIRM.equals(transferStatus) ||
                TransferStatus.TRANSFERING.equals(transferStatus)
            ){
                initiateSingleTransferResponse.setAppId(newWxPayProperties.getAppId());
                initiateSingleTransferResponse.setMchId(newWxPayProperties.getMchId());
                return initiateSingleTransferResponse;
            }
            else {
                if (ObjectUtil.isNotNull(transferErrorCode)) {
                    log.error("转账失败{},错误接款{}",transferErrorCode.name(),transferErrorCode.getDescription());
                    throw new ServiceException(transferErrorCode.getDescription());
                }

            }
            throw new ServiceException("转账失败");
        }
        catch (com.wechat.pay.java.core.exception.ServiceException e)
        {
            log.error("微信转账失败",e);
            throw new ServiceException(String.format("微信转账失败,错识码:%s,描述：%s",e.getErrorCode(),e.getErrorMessage()));
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("转账失败,错误{}",e.getMessage() ,e);
            throw new ServiceException(e.getMessage());
        }
    }


}
