package com.cool.store.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.config.weixin.WxPayConfig;
import com.cool.store.dto.wx.PreOrderDTO;
import com.cool.store.service.WxPayService;
import com.cool.store.utils.HttpServletUtils;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
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.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;

/**
 * 微信支付相关
 * @author
 * @date 2024-03-13 14:28
 */
@Slf4j
@Service
public class WxPayServiceImpl implements WxPayService {

    // @Resource
    private RSAAutoCertificateConfig rsaAutoCertificateConfig;

    @Resource
    private WxPayConfig wxPayConfig;

    @Value("${weixin.appId}")
    private String wxAppId;

    /**
     * 微信支付
     */
    @Override
    @Transactional
    public PrepayWithRequestPaymentResponse jsApiOrder(PreOrderDTO order) {
        log.info("微信预支付下单：{}", JSONObject.toJSONString(order));
        //请求微信支付相关配置
        PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
        try {
            PrepayRequest request = new PrepayRequest();
            request.setAppid(wxAppId);
            request.setMchid(wxPayConfig.getMerchantId());
            request.setDescription(order.getDescription());
            request.setOutTradeNo(order.getOutTradeNo());
            request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
            Amount amount = new Amount();
            amount.setTotal(order.getTotal());
            amount.setCurrency("CNY");
            request.setAmount(amount);
            Payer payer = new Payer();
            payer.setOpenid(order.getWxOpenId());
            request.setPayer(payer);
            log.info("请求预支付下单，请求参数：{}", JSONObject.toJSONString(request));
            // 调用预下单接口
            response = getJsapiService().prepayWithRequestPayment(request);
            log.info("订单【{}】发起预支付成功，返回信息：{}", order.getOutTradeNo(), response);
        } catch (HttpException e) {
            // 发送HTTP请求失败
            log.error("微信下单发送HTTP请求失败，错误信息：{}", e.getHttpRequest());
        } catch (ServiceException e) {
            // 服务返回状态小于200或大于等于300，例如500
            log.error("微信下单服务状态错误，错误信息：{}", e.getErrorMessage());
        } catch (MalformedMessageException e) {
            // 服务返回成功，返回体类型不合法，或者解析返回体失败
            log.error("服务返回成功，返回体类型不合法，或者解析返回体失败，错误信息：{}", e.getMessage());
        }
        return response;
    }

    /**
     * 微信回调
     *
     * @param request
     * @return
     * @throws IOException
     */
    @Transactional
    @Override
    public ResponseEntity payNotify(HttpServletRequest request) throws IOException {
        log.info("------收到支付通知------");
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(request.getHeader("Wechatpay-Serial"))
                .nonce(request.getHeader("Wechatpay-Nonce"))
                .signature(request.getHeader("Wechatpay-Signature"))
                .timestamp(request.getHeader("Wechatpay-Timestamp"))
                .signType(request.getHeader("Wechatpay-Signature-Type"))
                .body(HttpServletUtils.getRequestBody(request))
                .build();
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        // 以支付通知回调为例，验签、解密并转换成 Transaction
        log.info("验签参数：{}", requestParam);
        try {
            // 验签、解密并转换成 Transaction（返回参数对象）
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            log.info("验签成功！-支付回调结果：{}", JSON.toJSONString(transaction));
            // TODO 处理你的业务逻辑
            //修改订单前，建议主动请求微信查询订单是否支付成功，防止恶意post

            //修改订单信息
            // 处理成功，返回 200 OK 状态码
            return ResponseEntity.status(HttpStatus.OK).build();
        } catch (ValidationException e) {
            log.error("sign verification failed", e);
            log.error("微信支付回调v3java失败=" + e.getMessage(), e);
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }

    @Override
    public Transaction queryOrderByOutTradeNo(String outTradeNo) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(wxPayConfig.getMerchantId());
        request.setOutTradeNo(outTradeNo);
        Transaction result = getJsapiService().queryOrderByOutTradeNo(request);
        if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
            log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
        }
        // TODO 处理业务逻辑
        return result;
    }

    @Override
    public Transaction queryOrderByPaymentNo(String paymentNo) {
        QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        queryRequest.setTransactionId(paymentNo);
        Transaction result = getJsapiService().queryOrderById(queryRequest);
        if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
            log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
        }
        // TODO 处理业务逻辑
        return result;
    }

    /**
     * 申请退款
     * @param orderID
     * @param outRefundNo
     * @param backAmount
     */
    @Override
    public void applyRefund(String orderID, String outRefundNo, BigDecimal backAmount) {
        Transaction transaction = queryOrderByOutTradeNo(orderID);
        CreateRequest request = new CreateRequest();
        request.setTransactionId(transaction.getTransactionId());
        request.setNotifyUrl(wxPayConfig.getBackNotifyUrl());
        request.setOutTradeNo(transaction.getOutTradeNo());
        request.setOutRefundNo(outRefundNo);
        request.setReason("测试退款");
        AmountReq amountReq = new AmountReq();
        amountReq.setCurrency(transaction.getAmount().getCurrency());
        amountReq.setTotal(Long.parseLong((transaction.getAmount().getTotal().toString())));
        amountReq.setRefund((backAmount.multiply(new BigDecimal(100))).longValue());
        request.setAmount(amountReq);
        getRefundService().create(request);
    }

    @Override
    public ResponseEntity refundNotify(HttpServletRequest request) throws IOException {
        log.info("------收到退款回调通知------");
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(request.getHeader("Wechatpay-Serial"))
                .nonce(request.getHeader("Wechatpay-Nonce"))
                .signature(request.getHeader("Wechatpay-Signature"))
                .timestamp(request.getHeader("Wechatpay-Timestamp"))
                .signType(request.getHeader("Wechatpay-Signature-Type"))
                .body(HttpServletUtils.getRequestBody(request))
                .build();
        // 如果已经初始化了 RSAAutoCertificateConfig，可以直接使用  config
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        log.info("验签参数：{}", requestParam);
        try {
            // 验签、解密并转换成 Refund
            Refund refund = parser.parse(requestParam, Refund.class);
            log.info("验签成功！-退款回调结果：{}", JSON.toJSONString(refund));
            //记录日志信息
            Status state = refund.getStatus();
            String orderID = refund.getOutTradeNo();
            String backID = refund.getOutRefundNo();
            System.out.println("订单ID：" + orderID);
            System.out.println("退款ID：" + backID);
            if (state == Status.PROCESSING) {
                //TODO------
                //根据自己的需求处理相应的业务逻辑,异步

                //通知微信回调成功
                return ResponseEntity.status(HttpStatus.OK).build();
            } else if (state == Status.SUCCESS) {
                //TODO------
                //根据自己的需求处理相应的业务逻辑,异步

                //通知微信回调成功
                return ResponseEntity.status(HttpStatus.OK).build();
            } else {
                System.out.println("微信回调失败,JsapiPayController.Refund：" + state.toString());
                //通知微信回调失败
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
            }
        } catch (ValidationException e) {
            log.error("sign verification failed", e);
            log.error("微信支付回调v3java失败=" + e.getMessage(), e);
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }

    /**
     * 获取订单退款结果信息
     * @param outRefundNo
     * @return
     */
    @Override
    public Refund getRefundOrderInfo(String outRefundNo) {
        QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
        request.setOutRefundNo(outRefundNo);
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        return getRefundService().queryByOutRefundNo(request);
    }

    /**
     * 关闭微信支付
     */
    @Override
    public void closePay(String outTradeNo) {
        CloseOrderRequest closeRequest = new CloseOrderRequest();
        closeRequest.setMchid(wxPayConfig.getMerchantId());
        closeRequest.setOutTradeNo(outTradeNo);
        getJsapiService().closeOrder(closeRequest);
    }
    /**
     * 创建小程序支付服务
     * @return
     */
    protected JsapiServiceExtension getJsapiService() {
        JsapiServiceExtension jsapiServiceExtension =
                new JsapiServiceExtension.Builder()
                        .config(rsaAutoCertificateConfig)
                        .signType("RSA") // 不填默认为RSA
                        .build();
        return jsapiServiceExtension;
    }

    protected RefundService getRefundService() {
        RefundService refundService = new RefundService.Builder()
                        .config(rsaAutoCertificateConfig)
                        .build();
        return refundService;
    }
}
