package com.jeesharp.business.modules.pay.tlinx.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeesharp.business.modules.pay.tlinx.core.TLinx2Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * TLinx2支付工具类
 * 包含签名、验签、加密、解密等核心功能
 * 
 * @author wangmeng
 * @since 2026-03-16
 */
public class TLinx2Util {
    
    private static final Logger logger = LoggerFactory.getLogger(TLinx2Util.class);
    
    private static final String AES_ALGORITHM = "AES";
    private static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding";
    
    /**
     * 签名方法
     * 按A~z排序，串联成字符串，先进行sha1加密(小写)，再进行md5加密(小写)，得到签名
     *
     * @param paramMap 参数Map
     * @return 签名字符串
     */
    public static String sign(Map<String, String> paramMap) {
        try {
            // 1. A~z排序(加上open_key)
            String sortStr = sort(paramMap);
            logger.debug("排序后的待签名字符串: {}", sortStr);
            
            // 2. SHA1加密(小写)
            String sha1 = sha1(sortStr).toLowerCase();
            logger.debug("SHA1加密后的待签名字符串: {}", sha1);
            
            // 3. MD5加密(小写)
            String sign = md5(sha1).toLowerCase();
            logger.debug("最终签名: {}", sign);
            
            return sign;
        } catch (Exception e) {
            logger.error("签名过程发生异常", e);
            throw new RuntimeException("签名失败", e);
        }
    }
    
    /**
     * 验签方法
     *
     * @param respObject 响应JSON对象
     * @return 是否验签成功
     */
    public static boolean verifySign(JSONObject respObject) {
        try {
            String respSign = respObject.getString("sign");
            if (respSign == null) {
                logger.warn("响应中缺少签名字段");
                return false;
            }
            
            // 删除sign节点并添加open_key
            respObject.remove("sign");
            respObject.put("open_key", TLinx2Config.OPEN_KEY);
            
            logger.debug("开始验签...");
            String veriSign = sign(JSONObject.toJavaObject(respObject, Map.class));
            
            boolean result = respSign.equals(veriSign);
            if (result) {
                logger.debug("验签成功");
            } else {
                logger.warn("验签失败: 响应签名={}, 计算签名={}", respSign, veriSign);
            }
            
            return result;
        } catch (Exception e) {
            logger.error("验签过程发生异常", e);
            return false;
        }
    }
    
    /**
     * AES加密，再二进制转十六进制(bin2hex)
     *
     * @param dataMap 数据Map
     * @param postMap 请求参数Map
     * @throws Exception 加密异常
     */
    public static void handleEncrypt(TreeMap<String, ?> dataMap, TreeMap<String, String> postMap) throws Exception {
        JSONObject dataObj = JSONObject.parseObject(JSON.toJSONString(dataMap));
        String jsonData = dataObj.toString();
        String encryptedData = encrypt(jsonData, TLinx2Config.OPEN_KEY);
        logger.debug("加密后的data: {}", encryptedData);
        postMap.put("data", encryptedData);
    }
    
    /**
     * 处理签名
     *
     * @param postMap 请求参数Map
     */
    public static void handleSign(TreeMap<String, String> postMap) {
        Map<String, String> signMap = new HashMap<>(postMap);
        signMap.put("open_key", TLinx2Config.OPEN_KEY);
        
        // 签名
        String signature = sign(signMap);
        logger.debug("已签名字符串: {}", signature);
        postMap.put("sign", signature);
    }
    
    /**
     * 发送POST请求
     *
     * @param postMap 请求参数Map
     * @param interfaceName 接口名称
     * @return 响应字符串
     */
    public static String handlePost(TreeMap<String, String> postMap, String interfaceName) {
        String url = TLinx2Config.OPEN_URL + interfaceName;
        logger.info("请求地址: {}", url);
        
        if (url.startsWith("https")) {
            return HttpsUtil.post(url, postMap, TLinx2Config.CHARSET);
        } else {
            return HttpUtil.post(url, postMap, TLinx2Config.CHARSET);
        }
    }
    
    /**
     * AES加密
     *
     * @param content 待加密内容
     * @param key 密钥
     * @return 加密后的十六进制字符串
     * @throws Exception 加密异常
     */
    public static String encrypt(String content, String key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        
        byte[] encryptedBytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return bytesToHex(encryptedBytes);
    }
    
    /**
     * AES解密
     *
     * @param content 待解密内容（十六进制字符串）
     * @param key 密钥
     * @return 解密后的字符串
     * @throws Exception 解密异常
     */
    public static String decrypt(String content, String key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        
        byte[] decryptedBytes = cipher.doFinal(hexToBytes(content));
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }
    
    /**
     * SHA1加密
     *
     * @param content 待加密内容
     * @return SHA1加密结果
     */
    public static String sha1(String content) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA1");
            byte[] digest = md.digest(content.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (Exception e) {
            throw new RuntimeException("SHA1加密失败", e);
        }
    }
    
    /**
     * MD5加密
     *
     * @param content 待加密内容
     * @return MD5加密结果
     */
    public static String md5(String content) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(content.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (Exception e) {
            throw new RuntimeException("MD5加密失败", e);
        }
    }
    
    /**
     * 字节数组转十六进制字符串
     *
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
    
    /**
     * 十六进制字符串转字节数组
     *
     * @param hex 十六进制字符串
     * @return 字节数组
     */
    public static byte[] hexToBytes(String hex) {
        if (hex == null || hex.length() % 2 != 0) {
            throw new IllegalArgumentException("无效的十六进制字符串");
        }
        
        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length(); i += 2) {
            bytes[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
        }
        return bytes;
    }
    
    /**
     * 参数排序
     *
     * @param paramMap 参数Map
     * @return 排序后的字符串
     * @throws Exception 排序异常
     */
    public static String sort(Map<String, String> paramMap) throws Exception {
        if (paramMap == null || paramMap.isEmpty()) {
            return "";
        }
        
        TreeMap<String, String> sortedMap = new TreeMap<>(paramMap);
        StringBuilder sb = new StringBuilder();
        
        for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // 过滤空值
            if (value != null && !value.isEmpty()) {
                sb.append(key).append("=").append(value).append("&");
            }
        }
        
        // 移除最后一个&
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        
        return sb.toString();
    }
    
    /**
     * Unicode转中文
     *
     * @param unicode Unicode字符串
     * @return 中文字符串
     */
    public static String unicodeToCn(String unicode) {
        Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
        Matcher matcher = pattern.matcher(unicode);
        
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            char ch = (char) Integer.parseInt(matcher.group(2), 16);
            matcher.appendReplacement(result, String.valueOf(ch));
        }
        matcher.appendTail(result);
        
        return result.toString();
    }
    
    /**
     * 生成时间戳
     *
     * @return 当前时间戳（秒）
     */
    public static String getCurrentTimestamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }
}