package com.yiqi.payment;

import cn.hutool.json.JSONUtil;
import cn.hutool.setting.profile.Profile;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradePayModel;
import com.alipay.api.response.AlipayTradePayResponse;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.MicroPayModel;
import com.ijpay.wxpay.model.OrderQueryModel;
import com.ijpay.wxpay.model.RefundQueryModel;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import com.yiqi.common.config.RuoYiConfig;
import com.yiqi.common.exception.ServiceException;
import com.yiqi.common.utils.ip.IpUtils;
import com.yiqi.order.domain.dto.*;
import com.yiqi.order.domain.vo.weapp.AppPayRespVO;
import com.yiqi.system.service.ISysConfigRecordService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;


@Service
public class WechatPayService {

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

    /**
     * 应用ID
     */
    public static final String app_id = "wx10a9a1ca54e0aa75";

    /**
     * 商家ID
     */
    public static final String mch_id = "1557749741";

    /**
     * 支付密钥
     */
    public static final String pay_key = "c378426dba2f0fb797012f99119c57d6";

    /**
     * 证书
     */
    public static String pay_cert = "/www/server/yiqi_wash_v2/paycert/apiclient_key.pem";


    private static String notify_url = "";

    @Autowired
    ISysConfigRecordService sysConfigRecordService;
    @Autowired
    private RuoYiConfig ruoYiConfig;

    @PostConstruct
    public void init() {
        notify_url = sysConfigRecordService.getCacheConfigRecordByKey("pay_weixin_notify");
        initConfig();
    }

    private void initConfig() {
        WxPayApiConfig wxPayApiConfig = WxPayApiConfig.builder()
                .appId(app_id)
                .mchId(mch_id)
                .apiKey(pay_key)
                .certPath(pay_cert).build();
        WxPayApiConfigKit.setThreadLocalWxPayApiConfig(wxPayApiConfig);
    }

    /**
     * 当面付
     */
    public PayResponseDTO facePay(PayOrderFaceToFaceDTO payOrderFaceToFaceDTO) {

        try {
            //初始化
            String ip = IpUtils.getHostIp();
            Integer totalFee = payOrderFaceToFaceDTO.getPayAmount().multiply(new BigDecimal(100)).intValue();
            // 统一下单构建请求参数
            Map<String, String> params = MicroPayModel
                    .builder()
                    .appid(app_id)
                    .mch_id(mch_id)
                    .nonce_str(WxPayKit.generateStr())
                    .body(payOrderFaceToFaceDTO.getSubjectTitle())
                    .attach("")
                    .out_trade_no(payOrderFaceToFaceDTO.getRequestNo())
                    .total_fee(totalFee.toString())
                    .spbill_create_ip(ip)
                    .auth_code(payOrderFaceToFaceDTO.getAuthCode())
                    .build()
                    // 同时支持 SignType.MD5、SignType.HMACSHA256
                    .createSign(pay_key, SignType.MD5);
            String xmlResult = WxPayApi.microPay(false, params);
            Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
            String return_code = resultMap.get("return_code");
            String return_msg = resultMap.get("return_msg");
            if (!WxPayKit.codeIsOk(return_code)) {
                logger.error("微信支付异常:" + return_msg);
                return PayResponseDTO.payError(return_msg);
            }
            String result_code = resultMap.get("result_code");
            if (WxPayKit.codeIsOk(result_code)) {
                return PayResponseDTO.paySuccess(payOrderFaceToFaceDTO.getOrderNo(), resultMap.get("transaction_id"));
            } else {
                String errCode = resultMap.get("err_code");
                if ("USERPAYING".equals(errCode) || "SYSTEMERROR".equals(errCode) ||
                        "BANKERROR".equals(errCode)) {
                    return PayResponseDTO.payWait(payOrderFaceToFaceDTO.getOrderNo());
                } else {
                    return PayResponseDTO.payError(errCode == null ? return_msg : errCode);
                }
            }
        } catch (Exception e) {
            logger.error("微信支付异常", e);
            return PayResponseDTO.payError("微信支付异常");
        }
    }

    public PayResponseDTO orderPayQuery(String orderSn) {

        try {
            //初始化
            String ip = IpUtils.getHostIp();
            // 统一下单构建请求参数
            Map<String, String> params = OrderQueryModel
                    .builder()
                    .appid(app_id)
                    .mch_id(mch_id)
                    .nonce_str(WxPayKit.generateStr())
                    .out_trade_no(orderSn)
                    .build()
                    // 同时支持 SignType.MD5、SignType.HMACSHA256
                    .createSign(pay_key, SignType.MD5);
            String xmlResult = WxPayApi.orderQuery(false, params);
            Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
            String return_code = resultMap.get("return_code");
            String return_msg = resultMap.get("return_msg");
            if (!WxPayKit.codeIsOk(return_code)) {
                logger.error("微信支付查询异常:" + return_msg);
                return PayResponseDTO.payWait(orderSn);
            }
            String result_code = resultMap.get("result_code");
            if (WxPayKit.codeIsOk(result_code)) {
                String tradeState = resultMap.get("trade_state");
                if ("SUCCESS".equals(tradeState)) {
                    return PayResponseDTO.paySuccess(orderSn, resultMap.get("transaction_id"));
                } else if ("USERPAYING".equals(tradeState) || "ACCEPT".equals(tradeState) || "NOTPAY".equals(tradeState)) {
                    return PayResponseDTO.payWait(orderSn);
                } else {
                    return PayResponseDTO.payError("交易失败");
                }
            } else {
                String errCode = resultMap.get("err_code");
                if ("SYSTEMERROR".equals(errCode)) {
                    return PayResponseDTO.payWait(orderSn);
                } else {
                    return PayResponseDTO.payError(errCode == null ? return_msg : errCode);
                }
            }
        } catch (Exception e) {
            logger.error("微信支付查询异常", e);
            return PayResponseDTO.payWait(orderSn);
        }
    }

    /**
     * 退款
     *
     * @param payRefundDTO
     * @return
     */
    public RefundResponseDTO orderRefund(PayRefundDTO payRefundDTO) {
        try {
            Map<String, String> params = new HashMap<String, String>();
            params.put("appid", app_id);
            params.put("mch_id", mch_id);
            params.put("nonce_str", System.currentTimeMillis() + "");
            params.put("transaction_id", payRefundDTO.getOutTradeNo());
            params.put("out_refund_no", payRefundDTO.getOutRequestNo());
            params.put("total_fee", payRefundDTO.getTotalAmount().multiply(new BigDecimal(100)).intValue() + "");
            params.put("refund_fee", payRefundDTO.getRefundAmount().multiply(new BigDecimal(100)).intValue() + "");
            params.put("sign", WxPayKit.createSign(params, pay_key));
            String refund = WxPayApi.orderRefund(false, params, pay_cert, mch_id);
            logger.info("微信退款:" + refund);
            Map<String, String> resultMap = WxPayKit.xmlToMap(refund);
            String return_code = resultMap.get("return_code");
            String return_msg = resultMap.get("return_msg");
            if (WxPayKit.codeIsOk(return_code)) {
                String result_code = resultMap.get("result_code");
                if (WxPayKit.codeIsOk(result_code)) {
                    return RefundResponseDTO.refundWait(return_msg);
                } else {
                    String err_code_des = resultMap.get("err_code_des");
                    if ("SYSTEMERROR".equals(err_code_des) || "BIZERR_NEED_RETRY".equals(err_code_des)) {
                        //需要重试
                        logger.error("退款失败，需要重试，重新发起退款。{},{}", payRefundDTO.getOrderNo(), err_code_des);
                        orderRefund(payRefundDTO);
                    } else {
                        return RefundResponseDTO.refundError(err_code_des);
                    }
                }
            } else {
                logger.error("微信退款异常:" + return_msg);
                return RefundResponseDTO.refundError(return_msg);
            }
        } catch (Exception e) {
            logger.error("微信退款异常", e);
        }
        return RefundResponseDTO.refundError("微信退款异常");
    }

    /**
     * 退款查询
     *
     * @param outRequestNo 外部退款交易号
     * @return
     */
    public RefundResponseDTO orderRefundQuery(String outRequestNo) {
        try {
            //初始化
            String ip = IpUtils.getHostIp();
            // 统一下单构建请求参数
            Map<String, String> params = RefundQueryModel
                    .builder()
                    .appid(app_id)
                    .mch_id(mch_id)
                    .nonce_str(WxPayKit.generateStr())
                    .out_refund_no(outRequestNo)
                    .build()
                    // 同时支持 SignType.MD5、SignType.HMACSHA256
                    .createSign(pay_key, SignType.MD5);
            String xmlResult = WxPayApi.orderRefundQuery(false, params);
            Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
            String return_code = resultMap.get("return_code");
            String return_msg = resultMap.get("return_msg");
            if (!WxPayKit.codeIsOk(return_code)) {
                logger.error("微信退款查询异常:" + return_msg);
                return RefundResponseDTO.refundWait(outRequestNo);
            }
            String result_code = resultMap.get("result_code");
            if (WxPayKit.codeIsOk(result_code)) {
                String refundStatus = resultMap.get("refund_status_0");
                if ("SUCCESS".equals(refundStatus)) {
                    return RefundResponseDTO.refundSuccess(outRequestNo, resultMap.get("refund_id_0"));
                } else if ("PROCESSING".equals(refundStatus)) {
                    return RefundResponseDTO.refundWait(outRequestNo);
                } else if ("CHANGE".equals(refundStatus)) {
                    logger.error("微信退款异常:" + return_msg);
                    return RefundResponseDTO.refundError("退款到用户的卡作废或者冻结");
                } else {
                    return RefundResponseDTO.refundError("交易失败");
                }
            } else {
                String errCode = resultMap.get("err_code");
                if ("SYSTEMERROR".equals(errCode)) {
                    return RefundResponseDTO.refundWait(outRequestNo);
                } else {
                    return RefundResponseDTO.refundError("交易退款失败");
                }
            }
        } catch (Exception e) {
            logger.error("微信支付查询异常", e);
            return RefundResponseDTO.refundWait(outRequestNo);
        }
    }

    /**
     * 小程序支付
     */
    public AppPayRespVO xcxPay(PayOrderAppDTO payOrderAppDTO) {

        try {
            //初始化
            String ip = IpUtils.getHostIp();
            Integer totalFee = payOrderAppDTO.getPayAmount().multiply(new BigDecimal(100)).intValue();
            // 统一下单构建请求参数
            Map<String, String> params = UnifiedOrderModel
                    .builder()
                    .appid(app_id)
                    .mch_id(mch_id)
                    .nonce_str(WxPayKit.generateStr())
                    .body(payOrderAppDTO.getSubjectTitle())
                    .out_trade_no(payOrderAppDTO.getRequestNo())
                    .total_fee(totalFee.toString())
                    .spbill_create_ip(ip)
                    .openid(payOrderAppDTO.getUserId())
                    .trade_type("JSAPI")
                    .notify_url(notify_url)
                    .build()
                    // 同时支持 SignType.MD5、SignType.HMACSHA256
                    .createSign(pay_key, SignType.MD5);
            String xmlResult = WxPayApi.pushOrder(false, params);
            Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
            String return_code = resultMap.get("return_code");
            String return_msg = resultMap.get("return_msg");
            if (!WxPayKit.codeIsOk(return_code)) {
                logger.error("微信小程序支付异常:" + return_msg);
                throw new ServiceException("微信下单失败，请重试：" + return_msg);
            }
            String result_code = resultMap.get("result_code");
            if (!WxPayKit.codeIsOk(result_code)) {
                String errCode = resultMap.get("err_code");
                logger.error("微信小程序支付异常:" + errCode);
                throw new ServiceException("微信下单失败，请重试：" + errCode);
            }
            // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
            String prepay_id = resultMap.get("prepay_id");
            Map<String, String> packageParams = WxPayKit.prepayIdCreateSign(prepay_id, app_id, pay_key, SignType.MD5);
            AppPayRespVO appPayRespVO = new AppPayRespVO();
            appPayRespVO.setAppid(packageParams.get("appId"));
            appPayRespVO.setNonceStr(packageParams.get("nonceStr"));
            appPayRespVO.setPackageVal(packageParams.get("package"));
            appPayRespVO.setPrepayId(prepay_id);
            appPayRespVO.setTimestamp(packageParams.get("timeStamp"));
            appPayRespVO.setSign(packageParams.get("paySign"));
            appPayRespVO.setPartnerId(mch_id);
            return appPayRespVO;
        } catch (Exception e) {
            logger.error("微信小程序支付异常:", e);
            throw new ServiceException("微信下单失败，请重试");
        }
    }

    /**
     * 小程序支付
     */
    public AppPayRespVO appPay(PayOrderAppDTO payOrderAppDTO) {

        try {
            //初始化
            String ip = IpUtils.getHostIp();
            Integer totalFee = payOrderAppDTO.getPayAmount().multiply(new BigDecimal(100)).intValue();
            // 统一下单构建请求参数
            Map<String, String> params = UnifiedOrderModel
                    .builder()
                    .appid(app_id)
                    .mch_id(mch_id)
                    .nonce_str(WxPayKit.generateStr())
                    .body(payOrderAppDTO.getSubjectTitle())
                    .attach("")
                    .out_trade_no(payOrderAppDTO.getRequestNo())
                    .total_fee(totalFee.toString())
                    .spbill_create_ip(ip)
                    .trade_type("APP")
                    .notify_url(notify_url)
                    .build()
                    // 同时支持 SignType.MD5、SignType.HMACSHA256
                    .createSign(pay_key, SignType.MD5);
            String xmlResult = WxPayApi.pushOrder(false, params);
            Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
            String return_code = resultMap.get("return_code");
            String return_msg = resultMap.get("return_msg");
            if (!WxPayKit.codeIsOk(return_code)) {
                logger.error("微信小程序支付异常:" + return_msg);
                throw new ServiceException("微信下单失败，请重试：" + return_msg);
            }
            String result_code = resultMap.get("result_code");
            if (!WxPayKit.codeIsOk(result_code)) {
                String errCode = resultMap.get("err_code");
                logger.error("微信小程序支付异常:" + errCode);
                throw new ServiceException("微信下单失败，请重试：" + errCode);
            }
            // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
            String prepay_id = resultMap.get("prepay_id");
            Map<String, String> packageParams = WxPayKit.prepayIdCreateSign(prepay_id, app_id, pay_key, SignType.MD5);
            AppPayRespVO appPayRespVO = new AppPayRespVO();
            appPayRespVO.setAppid(packageParams.get("appId"));
            appPayRespVO.setNonceStr(packageParams.get("nonceStr"));
            appPayRespVO.setPackageVal(packageParams.get("package"));
            appPayRespVO.setPrepayId(prepay_id);
            appPayRespVO.setTimestamp(packageParams.get("timeStamp"));
            appPayRespVO.setSign(packageParams.get("paySign"));
            appPayRespVO.setPartnerId(mch_id);
            return appPayRespVO;
        } catch (Exception e) {
            logger.error("微信小程序支付异常:", e);
            throw new ServiceException("微信下单失败，请重试");
        }
    }

    /**
     * 获取微信扫码
     *
     * @param subject
     * @param orderSn
     * @param amount
     * @return
     */
    public String getScanCode(String orderSn, BigDecimal amount, String subject) {
        String qrCodeUrl = null;
        try {
            //初始化
            String ip = IpUtils.getHostIp();
            Integer totalFee = amount.multiply(new BigDecimal(100)).intValue();
            // 统一下单构建请求参数
            Map<String, String> params = UnifiedOrderModel
                    .builder()
                    .appid(app_id)
                    .mch_id(mch_id)
                    .nonce_str(WxPayKit.generateStr())
                    .body("IJPay 让支付触手可及")
                    .attach("Node.js 版:https://gitee.com/javen205/TNWX")
                    .out_trade_no(WxPayKit.generateStr())
                    .total_fee(totalFee.toString())
                    .spbill_create_ip(ip)
                    .notify_url(notify_url)
                    .trade_type(TradeType.JSAPI.getTradeType())
                    .build()
                    // 同时支持 SignType.MD5、SignType.HMACSHA256
                    .createSign(pay_key, SignType.HMACSHA256);
            String xmlResult = WxPayApi.pushOrder(false, params);
            Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
            String return_code = resultMap.get("return_code");
            String return_msg = resultMap.get("return_msg");
            if (!WxPayKit.codeIsOk(return_code)) {
                logger.error("获取支付二维码异常:" + return_msg);
                throw new ServiceException("获取支付二维码异常");
            }
            qrCodeUrl = resultMap.get("code_url");
        } catch (Exception e) {
            logger.error("获取微信支付二维码异常", e);
            throw new ServiceException("获取支付二维码异常");
        }
        return qrCodeUrl;
    }

}
