package cn.iocoder.yudao.module.payment.service.impl;

import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.inspection.service.AppInspectionPartnerService;
import cn.iocoder.yudao.module.payment.entity.OrderInfo;
import cn.iocoder.yudao.module.payment.service.IFzRecordService;
import cn.iocoder.yudao.module.payment.service.IOrderInfoDetailService;
import cn.iocoder.yudao.module.payment.service.OrderInfoService;
import cn.iocoder.yudao.module.payment.service.WxPayService;
import cn.iocoder.yudao.module.shop.service.IShopConfigService;
import cn.iocoder.yudao.module.shop.service.IShopCouponService;
import cn.iocoder.yudao.module.shop.service.IUserBalanceService;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.util.StringUtils;
import cn.iocoder.yudao.util.WechatPayConfig;
import cn.iocoder.yudao.util.WechatPayRequest;
import cn.iocoder.yudao.util.WechatPayUrlEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import static cn.hutool.crypto.SignUtil.sign;
import static jodd.exception.ExceptionUtil.buildMessage;

@Service
public class WxPayServiceImpl implements WxPayService {
    @Resource
    private WechatPayConfig wechatPayConfig;
    @Resource
    private IShopCouponService shopCouponService;
    @Resource
    private WechatPayRequest wechatPayRequest;
    @Autowired
    private OrderInfoService orderInfoService;
    @Autowired
    private com.ruoyi.partner.service.IPartnerBankInfoService partnerBankInfoService;
    @Autowired
    private AdminUserService adminUserService;
    @Autowired
    private IShopConfigService configService;
    @Autowired
    private IOrderInfoDetailService orderInfoDetailService;
    @Autowired
    private IUserBalanceService userBalanceService;
    @Autowired
    private AppInspectionPartnerService partnerService;
    @Autowired
    private IFzRecordService fzRecordService;
    private final ReentrantLock lock = new ReentrantLock();
    /**
     * @param type
     * @param orderId
     * @return
     * @throws SignatureException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws IOException
     */
    @Override
    public Map<String,Object> transactions(String type,Long orderId) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException, IOException {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        AdminUserDO user = adminUserService.getUser(loginUser.getId());
        OrderInfo orderInfo = orderInfoService.getById(orderId);
        // 统一参数封装
        Map<String, Object> params = new HashMap<>(8);
        params.put("appid", wechatPayConfig.getJcAppId());
        params.put("mchid", wechatPayConfig.getMchId());
        params.put("description", orderInfo.getGoodsTitle());
        params.put("out_trade_no", orderInfo.getOrderNo());
        params.put("notify_url", wechatPayConfig.getJcNotifyUrl());
        Map<String, Object> amountMap = new HashMap<>(4);
        // 金额单位为分
        amountMap.put("total", orderInfo.getPayMoney());
        //人民币
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);

        // 场景信息
        Map<String, Object> sceneInfoMap = new HashMap<>(4);
        // 客户端IP
        sceneInfoMap.put("payer_client_ip", "127.0.0.1");
        // 商户端设备号（门店号或收银设备ID）
        sceneInfoMap.put("device_id", "127.0.0.1");
        // 除H5与JSAPI有特殊参数外，其他的支付方式都一样
        if (type.equals(WechatPayUrlEnum.H5.getType())) {
            Map<String, Object> h5InfoMap = new HashMap<>(4);
            // 场景类型:iOS, Android, Wap
            h5InfoMap.put("type", "IOS");
            sceneInfoMap.put("h5_info", h5InfoMap);
        } else if (type.equals(WechatPayUrlEnum.JSAPI.getType()) || type.equals(WechatPayUrlEnum.SUB_JSAPI.getType())) {
            Map<String, Object> payerMap = new HashMap<>(4);
            payerMap.put("openid", user.getOpenId());
            params.put("payer", payerMap);
        }
        params.put("scene_info", sceneInfoMap);
        String paramsStr = JSON.toJSONString(params);

        String resStr = wechatPayRequest.wechatHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",paramsStr);
        Map<String, Object> resMap = JSONObject.parseObject(resStr);
        Map<String, Object> signMap = paySignMsg(resMap.get("prepay_id").toString(), wechatPayConfig.getAppId(),null);
        return signMap;
    }

    /**
     * 扫码支付
     *
     * @param orderId
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> nativeTransaction(Long orderId) throws Exception {
        OrderInfo orderInfo = orderInfoService.getById(orderId);

        // 统一参数
        Map<String, Object> params = new HashMap<>();
        params.put("appid", wechatPayConfig.getJcAppId());
        params.put("mchid", wechatPayConfig.getMchId());
        params.put("description", orderInfo.getGoodsTitle());
        // 截取订单好前几个字符
        // 如果始终是固定格式，比如总是有个“线下订单-”，可以直接：
        String pureOrderNo = orderInfo.getOrderNo().replaceFirst("^线下订单-", "");
        params.put("out_trade_no", pureOrderNo);
        params.put("notify_url", wechatPayConfig.getJcNativeNotifyUrl());

        Map<String, Object> amountMap = new HashMap<>();
        amountMap.put("total", orderInfo.getGoodsPrice()); // 单位：分
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);

        String paramsStr = JSON.toJSONString(params);

        // 调用微信 Native 支付接口
        String resStr = wechatPayRequest.wechatHttpPost(
                "https://api.mch.weixin.qq.com/v3/pay/transactions/native", paramsStr);

        Map<String, Object> resMap = JSONObject.parseObject(resStr);
        return resMap;
    }


    private  Map<String, Object>  paySignMsg(String prepayId,String appId,String privateKeyStr) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        long timeMillis = System.currentTimeMillis();
        String timeStamp = timeMillis/1000+"";
        String nonceStr = timeMillis+"";
        String packageStr = "prepay_id="+prepayId;
        // 公共参数
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("nonceStr",nonceStr);
        resMap.put("timeStamp",timeStamp);
        resMap.put("appId",appId);
        resMap.put("package", packageStr);
        // 使用字段appId、timeStamp、nonceStr、package进行签名
        //从下往上依次生成
        String message = buildMessage(appId, timeStamp, nonceStr, packageStr);
        //签名
        String paySign = sign(message.getBytes("utf-8"), privateKeyStr);
        resMap.put("paySign", paySign);
        resMap.put("signType", "RSA");
        return resMap;
    }

    String sign(byte[] message,String privateKeyStr) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
        //签名方式
        Signature sign = Signature.getInstance("SHA256withRSA");
        //私钥，通过MyPrivateKey来获取，这是个静态类可以接调用方法 ，需要的是_key.pem文件的绝对路径配上文件名
        PrivateKey privateKey =null;
        if (StringUtils.isNotEmpty(privateKeyStr)){
            privateKey = PemUtil.loadPrivateKey(privateKeyStr);
        }else {
            privateKey =  wechatPayConfig.getPrivateKey(wechatPayConfig.getKeyPemPath());
        }

        sign.initSign(privateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     *  按照前端签名文档规范进行排序，\n是换行
     * @param timestamp
     * @param nonceStr
     * @param prepay_id
     * @return
     */
    String buildMessage(String appId, String timestamp,String nonceStr,String prepay_id) {

        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + prepay_id + "\n";
    }
}
