package org.jeecg.modules.pay.unionPay;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.jeecg.common.constant.CommonConstant;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 银联商务API调用工具类
 * @link <a href="http://dhjt.chinaums.com/online-pay-doc/html/mini/pay.html">银联商务大华捷通综合支付小程序支付接口文档</a>
 * @author wangzq
 * @date 2025/09/23
 */
@Slf4j
public class UnionPayUtils {

    public static void main(String[] args) throws Exception {
        String jsonString = "{\n" +
                "    \"id\": \"\",\n" +
                "    \"name\": \"test\",\n" +
                "    \"age\": 25,\n" +
                "    \"timestamp\": 1633046400,\n" +
                "    \"sign\": \"0ddf80902bbd92cf2686f229d7d05f64da7f2a298e2433072701cfb39fa69a77\",\n" +
                "    \"xxx\": \"\"\n" +
                "}";
        String secretKey = "udik876ehjde32dU61edsxsf";
        boolean isValid = verifySignature(jsonString, secretKey);
        log.info("签名验证结果: {}", isValid ? "通过" : "不通过");

        TreeMap<String, String> treeMap = new TreeMap<>();
        treeMap.put("version", "20191031");
        treeMap.put("msgId", "4217");
        treeMap.put("msgType", "wx.appPreOrder");
        treeMap.put("requestTimestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        treeMap.put("expireTime", "");
        treeMap.put("merOrderId", "4217" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
        treeMap.put("mid", "8981200539800CH");
        treeMap.put("tid", "01672395");
        treeMap.put("instMid", "APPDEFAULT");
        treeMap.put("attachedData", "");
        treeMap.put("orderDesc", "");
        treeMap.put("originalAmount", "");
        treeMap.put("totalAmount", "1");
        treeMap.put("notifyUrl", "https://dhjt-uat.chinaums.com/test/notify");
        treeMap.put("signType", "SHA256");

        String s = sendPost(treeMap);
        log.info("响应结果: {}", s);
    }

    /**
     * 银联密钥
     */
    public static final String UNION_SECRET_KEY = "udik876ehjde32dU61edsxsf";
    /**
     * 银联统一请求接口地址
     */
    public static final String UNION_PAY_URL = "https://dhjt-uat.chinaums.com/queryService/UmsWebPayPlugins";

    /**
     * 发送POST请求
     *
     * @param paramsMap    请求参数
     * @return
     */
    public static String sendPost(Map<String, String> paramsMap)
            throws Exception {
        URL u = null;
        HttpURLConnection con = null;

        OutputStreamWriter osw = null;
        BufferedReader br = null;

        //排序参数并构建待签名字符串
        String stringA = buildStringToSign(paramsMap, UNION_SECRET_KEY);

        String localSign = generateSignature(stringA);
        //localSign转大写
        String sign = localSign.toUpperCase();
        //添加参数sign
        paramsMap.put("sign", sign);
        String params = formatUrlParam(paramsMap);
        log.info("请求地址连接:{},参数:{}", UNION_PAY_URL,params);

        try {
            u = new URL(UNION_PAY_URL);
            con = (HttpURLConnection) u.openConnection();
            con.setConnectTimeout(30000);
            con.setReadTimeout(700000);
            con.setRequestMethod("POST");
            con.setDoOutput(true);
            con.setDoInput(true);
            con.setUseCaches(false);
            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            con.setRequestProperty("Charset", "UTF-8");
            osw = new OutputStreamWriter(con.getOutputStream(), StandardCharsets.UTF_8);
            osw.write(params);
            osw.flush();
        } catch (Exception e) {
            throw new Exception();
        } finally {
            try {
                if (osw != null) {
                    osw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        StringBuffer buffer = new StringBuffer();
        try {
            br = new BufferedReader(new InputStreamReader(con.getInputStream(),
                    StandardCharsets.UTF_8));
            String temp;
            while ((temp = br.readLine()) != null) {
                buffer.append(temp);
                buffer.append("\n");
            }
        } catch (Exception e) {
            throw new Exception();
        } finally {
            try {
                if (osw != null) {
                    osw.close();
                }
                if (br != null) {
                    br.close();
                }
                if (con != null) {
                    con.disconnect();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return buffer.toString();
    }

    /**
     * 验证JSON字符串的签名
     * @param jsonString 待验证的JSON字符串
     * @param secretKey 签名密钥
     * @return 验证结果
     */
    public static boolean verifySignature(String jsonString, String secretKey) {
        try {
            // 1. 解析JSON字符串获取所有参数（除sign外）
            Map<String, String> paramsMap = extractParamsFromJson(jsonString);
            log.info("参数列表: {}", paramsMap);

            // 2. 获取sign参数值
            String signValue = getSignValueFromJson(jsonString);
            if (signValue == null || signValue.isEmpty()) {
                throw new IllegalArgumentException("JSON字符串中缺少sign参数");
            }
            log.info("sign参数值: {}", signValue);

            // 3. 排序参数并构建待签名字符串
            String stringA = buildStringToSign(paramsMap, secretKey);
            log.info("待签名字符串: {}", stringA);

            // 4. 生成签名
            String localSign = generateSignature(stringA);
            log.info("本地签名: {}", localSign);

            // 5. 比较签名（忽略大小写）
            return localSign.equalsIgnoreCase(signValue);

        } catch (Exception e) {
            throw new RuntimeException("签名验证失败: " + e.getMessage(), e);
        }
    }

    /**
     * @return 订单号
     * @Author wzq
     * @Description 订单编号生成逻辑
     * @params 类型：0-（D:订单） 1-（T:退单） 2-（B:保单）
     **/
    private String genOrderNum(String msgId, int type) {
        String format = DateUtil.format(new Date(), "yyyyMMddHHmmssSSS");
        String nextInt = RandomUtil.randomNumbers(CommonConstant.NUMBER_7);
        if (type == 0) {
            return msgId + "D" + format + nextInt;
        } else {
            return msgId + "T" + format + nextInt;
        }
    }

    /**
     * 从JSON中获取sign参数的值
     */
    private static String getSignValueFromJson(String jsonString)
            throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = objectMapper.readTree(jsonString);
        JsonNode signNode = rootNode.get("sign");
        return (signNode != null && !signNode.isNull()) ? signNode.asText() : null;
    }

    /**
     * 从JSON字符串中提取所有参数（除了sign）
     */
    public static Map<String, String> extractParamsFromJson(String jsonString) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = objectMapper.readTree(jsonString);

        Map<String, String> paramsMap = new HashMap<>();
        traverseJsonNode(rootNode, "", paramsMap);
        paramsMap.remove("sign");
        return paramsMap;
    }

    private static void traverseJsonNode(JsonNode node, String currentPath, Map<String, String> paramsMap) {
        if (node.isObject()) {
            node.fields().forEachRemaining(entry -> {
                String newPath = currentPath.isEmpty() ? entry.getKey() : currentPath + "." + entry.getKey();
                traverseJsonNode(entry.getValue(), newPath, paramsMap);
            });
        } else if (node.isArray()) {
            for (int i = 0; i < node.size(); i++) {
                traverseJsonNode(node.get(i), currentPath + "[" + i + "]", paramsMap);
            }
        } else if (node.isValueNode()) {
            // 只处理值节点
            if (!node.isNull()) {
                // 排除JsonNode类型的NullNode
                String value = node.asText();
                // 对于非Null节点，获取其文本表示
                if (value != null && !value.isEmpty()) {
                    // 进一步排除空字符串
                    paramsMap.put(currentPath, value);
                }
            }
        }
    }

    /**
     * 构建待签名字符串
     */
    private static String buildStringToSign(Map<String, String> paramsMap, String secretKey) {
        String params = "";
        try {
            // 按key字典序排序
            List<String> sortedKeys = new ArrayList<>(paramsMap.keySet());
            Collections.sort(sortedKeys);

            StringBuilder sb = new StringBuilder();

            // 拼接参数键值对
            for (String key : sortedKeys) {
                String value = URLEncoder.encode(paramsMap.get(key), StandardCharsets.UTF_8);
                // 跳过空值参数
                if (value != null && !value.trim().isEmpty()) {
                    if (sb.length() > 0) {
                        sb.append("&");
                    }
                    sb.append(key).append("=").append(value);
                }
            }
            // 删除末尾多余的&号和空格
            params = sb.toString();
            if (!params.isEmpty()) {
                params = params.substring(0, params.length() - 1);
            }
            params+= params+ secretKey;
        } catch (Exception e) {
            return "";
        }
        return params;
    }

    /**
     * 生成签名（使用SHA-256算法）
     */
    private static String generateSignature(String stringToSign) {
        return SHA256Util.encrypt(stringToSign);
    }

    /**
     *
     * 遍历集合M,取出全部的参数key，将参数key字典序排列，根据排序后的参数key获取对应的value,将非空的参数key和参数value用"="拼接，多个参数之间用"&"拼接
     *
     * @param param   参数
     * @return
     */
    public static String formatUrlParam(Map<String, String> param) {
        String params = "";
        try {
            List<Map.Entry<String, String>> items = new ArrayList<>(param.entrySet());

            //对所有传入的参数按照字段名从小到大排序
            items.sort(Map.Entry.comparingByKey());

            //构造URL 键值对的形式
            StringBuffer sb = new StringBuffer();
            for (Map.Entry<String, String> item : items) {
                if (StringUtils.isNotBlank(item.getKey())) {
                    String key = item.getKey();
                    String val = item.getValue();
                    val = URLEncoder.encode(val, StandardCharsets.UTF_8);
                    // 跳过空值参数
                    if (val != null && !val.trim().isEmpty()) {
                        if (sb.length() > 0) {
                            sb.append("&");
                        }
                        sb.append(key).append("=").append(val);
                    }
                }
            }
            // 删除末尾多余的&号和空格
            params = sb.toString().trim();
        } catch (Exception e) {
            return "";
        }
        return params;
    }
}
