package com.jeequan.jeepay.thirdparty.channel.ryxpay;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SignUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.ruipay.RyxpayIsvParams;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.thirdparty.channel.ryxpay.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;

import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * TODO
 *
 * @author crystal
 * @date 2024/2/27 11:01
 */
@Slf4j
public class RyxpayKit {

    public static final String AUDIT_SUCCESS_CODE = "0000";

    /**
     * 请求成功code
     */
    public static final String SUCCESS_CODE = "000000";

    /**
     * 扩展成功状态码
     */
    public static final String EXT_SUCCESS_CODE = "200002";

    /**
     * 业务响应成功码
     */
    public static final String RESP_BIZ_SUCCESS = "S";

    /**
     * 业务响应失败码
     */
    public static final String RESP_BIZ_FAIL = "E";

    /**
     * 业务响应失败码 （不确定 处理中）
     */
    public static final String RESP_BIZ_ING = "R";

    public static final String IV_PARAM = "ivParam";
    public static final String AES_KEY = "aesKey";

    public static RyxAccessModel accessCombineEncrypt(Map<String, Object> paramMap, RyxpayIsvParams isvParams) {
        log.info("瑞银信-请求业务参数：{}", JSONObject.toJSONString(paramMap));
        RyxAccessModel ryxAccessModel = new RyxAccessModel();
        ryxAccessModel.setAccessId(isvParams.getAccessId());
        // 接入方订单号
        ryxAccessModel.setAccessMerchId(UUID.randomUUID().toString());
        // 发起请求的时间
        ryxAccessModel.setReqTime(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));

        // 保存AES密钥
        String ivParam = RandomUtil.randomString(16);
        byte[] aesKey;

        RSA rsa = SecureUtil.rsa(isvParams.getPrivateKey(), isvParams.getPublicKey());
        Sign sign = SignUtil.sign(SignAlgorithm.SHA256withRSA, isvParams.getPrivateKey(), isvParams.getPublicKey());
        AES aes = new AES(Mode.CBC, Padding.PKCS5Padding);
        aes.setIv(ivParam.getBytes(StandardCharsets.UTF_8));
        try {
            aesKey = aes.getSecretKey().getEncoded();
            Map<String, String> keyMap = new HashMap<>();
            // aesKey使用Base64编码
            keyMap.put(AES_KEY, Base64.encode(aesKey));
            keyMap.put(IV_PARAM, ivParam);
            String encryptKey = rsa.encryptBase64(JSON.toJSONString(keyMap), KeyType.PublicKey);
            ryxAccessModel.setEncryptKey(encryptKey);
        } catch (Exception e) {
            log.info("异常信息: ", e);
            throw new BizException("瑞银信-AES密钥加密异常");
        }

        // 使用AES加密普通参数
        try {
            String info = aes.encryptBase64(JSON.toJSONString(paramMap).getBytes(StandardCharsets.UTF_8));
            ryxAccessModel.setInfo(info);
        } catch (Exception e) {
            log.info("瑞银信-请求参数加密异常: ", e);
            throw new BizException("瑞银信-请求参数加密异常");
        }

        // 参数加签
        Object[] sortedKeys = paramMap.keySet().toArray();
        Arrays.sort(sortedKeys);

        StringBuilder builder = new StringBuilder();
        builder.append("accessId=").append(ryxAccessModel.getAccessId())
                .append("&reqTime=").append(ryxAccessModel.getReqTime())
                .append("&ivParam=").append(ivParam);
        for (Object key : sortedKeys) {
            Object o = paramMap.get(key);
            if (o != null) {
                builder.append("&").append(key).append("=").append(o);
            }
        }

        log.info("签名原串：{}", builder);
        // 客户端使用私钥签名
        try {
            String signStr = cn.hutool.core.codec.Base64.encode(sign.sign(builder.toString()));
            ryxAccessModel.setSign(signStr);
        } catch (Exception e) {
            log.info("瑞银信-签名异常: ", e);
            throw new BizException("瑞银信-签名异常");
        }

        log.info("瑞银信-加密后的请求参数：{}", JSONObject.toJSONString(ryxAccessModel));

        return ryxAccessModel;
    }

    public static void main(String[] args) {
        String respData = "{\"accessId\":\"000000000000038\",\"sign\":\"b653kuEY8GBQU983OAcPil0VOiOtK4mHtknhGujaCPdmrzhXkmfbn7LzERveU4XUjWqz8QyH24OMaSWuF2n2UuKPmKzFRD0ssJ6lWWKoo4c2mAv0nJ2WqD4M775J2zgQt4Wld4LxuAExpJDAVpKe5dcRsg9RTOOTjeqjBdfaPAO8kh3jvJxRHTXcAkPztl5CVYMWsd1u5NumVl12MvtKpWZl56frOe1ZzxuxuMGrlHnSJlNqsygpDk2b3zffOdLFHQV2d++ERfqV3K2oLUDAgwB38ehdYX2SFUeddY6BMNnWCEgKsDlciTJgtdlqTpMLliu8iYmSFfhCcIjzqCuLeg==\",\"reqTime\":\"20240307140757\",\"encryptKey\":\"u3SIim4XPuY6VEgj31ee4kQUf6R3oRaIAq7CGWLOWA8dk3zeuBwrSe803yah6YMm/bLJfKzslQuY0e3XjO/I9BKzVFtaiUjTfnkbHQouj7pe1Nauj3eWDu8d9qR4I9R+R04tdboXQRvRbGwt+cIkr+2MCMONr6Ut23CyDq9CED2ob4K6EjcmfbxeGQqP355EtqqbhcKwdlAJHWuz5w2un2pIG4biRKAQkcuIpA7Vxwh9YahrvGo3U+l6L9C8BX+xMmwNYFznOe79lJEHahR13b1csHEQrkraxtYQjbZpUm1d5/66tjG5BuT+TsqKJVJarXKrpeKzVu5P8zUxJsH24w==\",\"info\":\"EW1GNSpmAMX2uJQ9qpylV+iFjpr330XhNIlRSVOth8V+cjikqOvJ4GnZJ5EAbBy82gppCXIwrN4oP2h5Lpgb1Wyaw/xZiLA57ZCralZLUV589Y0HdvuUYH4WpZsvXsL6j+E3ZmoDZLiBY1XXqXDf2JfzacBTr2YE0aY4853432T1G297u2X5CGz59MIx9NR6fK51p8/i+9PwyPKOvKVLJQ==\"}";

        RyxAccessModel ryxResult = JSON.parseObject(respData, RyxAccessModel.class);

        String isvParamStr = "{\"orgId\": \"OX000000002983\", \"wxUrl\": \"http://weixin.qq.com/q/02MRrq8qoreR2100000079\", \"aliUrl\": \"https://qr.alipay.com/0cb14483yoci65pbga3sif8\", \"accessId\": \"000000000000038\", \"settType\": [\"T1\", \"D1\"], \"publicKey\": \"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiZTORAOR8d8aZB1Wg2IlRPySmozSey3gIdXNH5+YiUYBGTaUGCPuBXYGRu/iaD4HXYNhPm3+fNz33V/EbTKDppXz/8798u5oRLAr+ezqtKp4sCIVrdZhTY362NcUBDcRBpOMWLUplQVXcm6c36g/Cb3sC7eHZlZporWvihES1WWtIt8b46sWjhsn9gyw+ZIE+56INl5yheCgZQY4bz7nyPh5a/YpquXDZYuN40nxx80D7D4JGM1rWFdRskIm4UwyEguDD46i3QBumgb2KcYJBs1k1rQt561iZxJsYCofgPQZhBhLd4QBwEWsDkGeQjjeOVSj/naSZAsC4NBv4jc7ewIDAQAB\", \"cooperator\": \"ROX000000002983\", \"privateKey\": \"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDConKuuoKWDDpUDDW+aSlhxpoPHEIk3RKJG0PGNh/qlOfR8QYgGRyK4Ml1W3TgAvG+W50H3o2HK7k4Prr0nHSgvte/NgYbrTUo+M0eJyWyMkKY+A0PczeYtX0rmeWd1xjnR7BnXM/Rxj1NFP13+gkecBSnBmShH9T6CJnnD4rrJLG9PbCuq1CevXNSRklAMuufncW9uXeKT946cALDZwpNQKTHHLJUuxV4OjE1nCbSTk6HuLSf7pt3mooilEo9GoqGOOV4Zc/8TgpzCHBJnm81VatEytYcm/4FDcOzt/VJSQIYJWJJni07hmpXqIqG0TFGFsJaYqp84yznj1I73fmZAgMBAAECggEBAJ33Rmi1iyLIzdZFCqF9rE5k58h3wN82ZcLNVBZc+05iiDuGCNioSNgWVKthDNx6x0UeCbbHU8pvHCagExBQov+LpILWD+MKkuir9Z4RW7DixdZtC/yMIIVaVzq57vhKCTY8otlhJHI3GK2k6YskbZ48jSZ6M9Xphetga7hyjucENXNXa9OE+UJGhEzWF0hkD8GL1Umd2WO1AMgg2xIb2gpN5SF5zC9/3hj+iuexJBFJbvjbK6QB/OG3hEEkrAW+NEBcZEZHHUpw3xx9o8gEVTYnHKttgYMjBMZ+wqmGMaOOgmpps7jw3sj1E7KzMqARO/zn6eOd+NPZt8Q/WHMuTzkCgYEA/29N8mCtDVjUNjTFmrXWC8qSYDgGOV9L33YgDBsPwUM11R0k4vkU9C47ERKAWJKqh7u4UoUjct9loYHrCoOCif/8wjpTskMdmfTrwQU+oRVwPDQHC7WYpBoSgUZpZeuZDUxyr2BIqQ3oCKLkOou0bkQFxgGfBdfu75KYQoigCTcCgYEAwxCzv9SlSYEwMqewlF2MQwTgn/Q1lPd1IqMTcxOxn5Ws52eEIzZbqo82j7gWha/Ng1jWP5calkN1ZvROM6vgNKo7ia2AtbVZA74cYBOjTjQ+ZtyVBg0CNz9zYn++9KkCULfjLwixOSok96iziawamH1sHqBwam2N93BJnnpoO68CgYBgXwgTiDICNR9rjCF5q10kebscHkcK4k9n8dNJffRFwCvRSfuevscPEVorqMVPpZY9O6/hQmYLcwmPE8sv3koj8rbUONlXCkdizKsd2fK1J7d+n0qBOnRqhYOxznT5hRfrkoo6bKM9VQfUKlDdXdQnPfbHq9jphXyaWr6ja5SlpQKBgEs1QViQ8Dlq6d5d8SMC/nVIiCKj0ZTh4/O61GDmEvHoj4WYz8WTy7vzA6Jd47EfmyktM03JQKL212kegJhDiGpFyDbJAnsMtIlQ3zfb2nbohba7DWoL0fLjL+WmQ0WpdA8TFzCnNU1JBn/GN5K5HHf2w5z7Vtxx6O6iCIBlyZJZAoGAdEcgEdvR/fBKFN/rkMYXC2pbXPtXL1nnW7Nu39QyqjggLI6nwX310SmponwmPGgc6EMGuPGID8CpIfegpr+2YT9Ma2jHfHHNl/BkAdNx59aiQTcKOoE/7Z84ojMIqfDSHcbRy4y92XDfAVHrDHj503M8/CYpBhdjuuf38Y/W/0U=\", \"smzfPubKey\": \"MIIBIjA*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************\", \"cooperatorPriKey\": \"MIIEvQI*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************\"}";
        RyxpayIsvParams isvParams = JSON.parseObject(isvParamStr, RyxpayIsvParams.class);

        RyxAccessResp ryxAccessResp = accessResultDecrypt(ryxResult, RyxAccessResp.class, isvParams);

    }

    public static  <T> T accessResultDecrypt(RyxAccessModel ryxAccessModel, Class<T> clazz, RyxpayIsvParams isvParams) {
//        log.info("瑞银信-返回报文: \n{}", JSONObject.toJSONString(ryxAccessModel));

        if (ryxAccessModel == null || ObjUtil.isEmpty(ryxAccessModel.getInfo())) {
            throw new BizException("请求异常");
        }

        String info = ryxAccessModel.getInfo();
        String encryptKey = ryxAccessModel.getEncryptKey();
        String signStr = ryxAccessModel.getSign();
        String reqTime = ryxAccessModel.getReqTime();

        Map<String, Object> aesKeyMap;

        RSA rsa = SecureUtil.rsa(isvParams.getPrivateKey(), isvParams.getPublicKey());
        try {
            // 使用客户端RSA密钥解密 AES密钥
            String aesKeyInfo = rsa.decryptStr(encryptKey, KeyType.PrivateKey);
            aesKeyMap = JSON.parseObject(aesKeyInfo);
        } catch (Exception e) {
            log.info("瑞银信-请求结果密钥解密异常", e);
            throw new BizException("瑞银信-请求结果密钥解密异常");
        }

        Assert.notNull(aesKeyMap, "瑞银信-请求结果密钥解密异常");
        // AES解密服务器返回的数据
        String data;
        String ivParam;
        try {
            byte[] bytesInfo = Base64.decode(info);
            byte[] key = Base64.decode(aesKeyMap.get(AES_KEY).toString());
            ivParam = aesKeyMap.get(IV_PARAM).toString();
            AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, key);
            aes.setIv(ivParam.getBytes(StandardCharsets.UTF_8));

            byte[] decryptData = aes.decrypt(bytesInfo);
            data = new String(decryptData, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.info("瑞银信-请求结果密钥解密异常", e);
            throw new BizException("瑞银信-请求结果密钥解密异常");
        }

        log.info("瑞银信-返回报文解密：\n{}", data);

        T result = JSON.parseObject(data, clazz);

        StringBuilder builder = new StringBuilder();
        builder.append("accessId=").append(ryxAccessModel.getAccessId()).append("&reqTime=").append(reqTime)
                .append("&ivParam=").append(ivParam).append("&result=").append(data);

        try {
            Sign sign = SignUtil.sign(SignAlgorithm.SHA256withRSA, isvParams.getPrivateKey(), isvParams.getPublicKey());
            boolean verify = sign.verify(builder.toString().getBytes(StandardCharsets.UTF_8), signStr.getBytes(StandardCharsets.UTF_8));
            log.debug(verify ? "验签成功":"验签失败");
        } catch (Exception e) {
            log.info("瑞银信-返回报文验签异常", e);
        }

        return result;
    }

    /**
     *
     * @param otherParam 主要指文件
     * @return
     */
    public static RyxAccessResp auditReqApi(ReqMethod.Method method, RyxpayIsvParams isvParams, JSONObject reqData, JSONObject otherParam) {
        String url = ReqMethod.getUrl(method);
        log.info("瑞银信-请求链接: {}", url);

        HttpRequest form = HttpRequest.post(url);

        // 组装加密请求参数
        if (reqData != null) {
            RyxAccessModel ryxAccessModel = accessCombineEncrypt(reqData, isvParams);

            form.form("accessId", ryxAccessModel.getAccessId())
                    .form("reqTime", ryxAccessModel.getReqTime())
                    .form("info", ryxAccessModel.getInfo())
                    .form("sign", ryxAccessModel.getSign())
                    .form("encryptKey", ryxAccessModel.getEncryptKey());

            if (method == ReqMethod.Method.MERCHANT_INCOME) {
                form.form("accessMerchId", ryxAccessModel.getAccessMerchId());
            }

            if (otherParam != null && !otherParam.isEmpty()) {
                form.header(Header.CONTENT_TYPE.getValue(), ContentType.MULTIPART.getValue());

                otherParam.forEach((key, value) -> {
                    if (value instanceof List) {
                        ((List<?>) value).forEach(item -> {
                            form.form(key, item);
                        });
                    } else {
                        form.form(key, value);
                    }
                });

            } else {
                form.header(Header.CONTENT_TYPE.getValue(), ContentType.FORM_URLENCODED.getValue());
            }
        }

        // 发起请求
        HttpResponse execute = form.execute();
        String body = execute.body();
        log.info("瑞银信进件请求返回参数为{}", body);
        if (!execute.isOk()) {
            log.info("瑞银信进件请求失败, 返回参数为{}", body);
            throw new BizException("瑞银信接口请求异常");
        }

        RyxAccessModel ryxResult = JSON.parseObject(body, RyxAccessModel.class);
        // 返回参数校验
        Assert.notNull(ryxResult, "未知请求结果");

        return accessResultDecrypt(ryxResult, RyxAccessResp.class, isvParams);
    }


    /**
     * 支付请求
     * @param serviceCode
     * @param isvParams
     * @param reqData
     * @return
     */
    public static PayRespEntity payReqApi(ReqMethod.ServiceCode serviceCode, RyxpayIsvParams isvParams, JSONObject reqData,String msgId,String notifyUrl){
        PayBizEntity bizEntity = new PayBizEntity(reqData,msgId);
        PayReqEntity reqEntity = payParamsEncrypt(bizEntity,isvParams, serviceCode.getValue(),notifyUrl);
        HttpResponse response = HttpRequest.post(ReqMethod.getUrl(serviceCode.getMethod())).form(reqEntity.toMap()).execute();
        log.info("【瑞银信】响应参数：{}", response.body());
        PayRespEntity result = resultParamsDecode(response.body(),isvParams);
        String respCode = result.getRespCode();
        if(!SUCCESS_CODE.equals(respCode) && !EXT_SUCCESS_CODE.equals(respCode)){
            throw new BizException(result.getRespMsg());
        }
        String respType = result.getRespType();
        if(RyxpayKit.RESP_BIZ_FAIL.equals(respType)){
            throw new BizException(result.getRespMsg());
        }
        return result;
    }

    /**
     * 解析返回参数
     * @param response
     * @return
     */
    public static PayRespEntity resultParamsDecode(Object response, RyxpayIsvParams isvParams) {
        JSONObject resp = null;
        if(response instanceof String ){
            resp = JSONObject.parseObject((String) response);
        }else if(response instanceof JSONObject ){
            resp = (JSONObject)response;
        }
        if(resp.containsKey("respCode") && RESP_BIZ_FAIL.equals(resp.getString("respType"))){
            throw new BizException(resp.getString("respMsg"));
        }
        String resEncryptData = resp.getString("encryptData");
        String resEncryptKey = resp.getString("encryptKey");
        try {
            byte[] keyBytes = Base64.decode(resEncryptKey.getBytes(StandardCharsets.UTF_8));
            RSA rsa = SecureUtil.rsa(isvParams.getCooperatorPriKey(),null);
            byte[] merchantAESKeyBytes = rsa.decrypt(keyBytes, KeyType.PrivateKey);
            byte[] dataBytes = Base64.decode(resEncryptData.getBytes(StandardCharsets.UTF_8));
            AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, merchantAESKeyBytes);
            String resXml = aes.decryptStr(dataBytes);
            log.info("【瑞银信】返回参数解析结果:{}",resXml);
            return JSONObject.parseObject(resXml,PayRespEntity.class);
        } catch (Exception e) {
            throw new BizException("【瑞银信】返回参数解析异常");
        }
    }

    private static PayReqEntity payParamsEncrypt(PayBizEntity bizEntity,RyxpayIsvParams isvParams, String tranCode,String notifyUrl) {
        String bizData = JSON.toJSONString(bizEntity);
        log.info("【瑞银信支付】请求加密前:{}",bizData);
        PayReqEntity request = new PayReqEntity();
        request.setCooperator(isvParams.getCooperator());
        try {
            byte[] plainBytes = bizData.getBytes(StandardCharsets.UTF_8);
            Sign sign = SecureUtil.sign(SignAlgorithm.SHA1withRSA, isvParams.getCooperatorPriKey(), isvParams.getSmzfPubKey());
            String key = RandomUtil.randomString(16);
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, keyBytes);
            String encryptData = aes.encryptBase64(plainBytes);
            String signData = DatatypeConverter.printBase64Binary(sign.sign(bizData));
            RSA rsa = SecureUtil.rsa(null, isvParams.getSmzfPubKey());
            String encrtptKey = DatatypeConverter.printBase64Binary(rsa.encrypt(key, KeyType.PublicKey));
            request.setEncryptData(encryptData);
            request.setSignData(signData);
            request.setEncrtptKey(encrtptKey);
            request.setTranCode(tranCode);
            request.setCallBack(notifyUrl);
            request.setReqMsgId(bizEntity.getSmzfMsgId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("【瑞银信支付】请求加密后:{}",JSON.toJSONString(request));
        return request;
    }

    /**
     * 设置通用支付参数
     * @param mchAppConfigContext
     * @param bizContent
     * @param payOrder
     */
    public static void setPayParams(MchAppConfigContext mchAppConfigContext, JSONObject bizContent, PayOrder payOrder, UnifiedOrderRQ rq) {
        bizContent.put("merchantCode", mchAppConfigContext.getMchApplyment().getChannelMchNo());
        switch (payOrder.getWayCodeType()){
            case CS.PAY_WAY_CODE_TYPE.ALIPAY:
                bizContent.put("payWay", "ZFBZF");
                if(rq.getHbFqNum() != null){
                    JSONObject extendParams = new JSONObject();
                    extendParams.put("hb_fq_num",rq.getHbFqNum());
                    extendParams.put("hb_fq_seller_percent",rq.getHbFqPercent());
                    bizContent.put("extendParams",extendParams);
                }
                break;
            case CS.PAY_WAY_CODE_TYPE.WECHAT:
                bizContent.put("payWay", "WXZF");
                break;
            case CS.PAY_WAY_CODE_TYPE.UNIONPAY:
                bizContent.put("payWay", "UPZF");
                break;
            default:
                throw new BizException("暂不支持当前支付方式，请更换");
        }
        //支付场景,条码支付-1 声波支付-2
        bizContent.put("scene", "1");
        bizContent.put("totalAmount",AmountUtil.convertCent2Dollar(payOrder.getFindAmt()));
        bizContent.put("subject",payOrder.getSubject());
        bizContent.put("desc",payOrder.getBuyerRemark());
        Integer limitPay = rq.getLimitPay();
        if(limitPay == null){
            bizContent.put("limitPay",limitPay);
        }
    }
}
