package com.pig4cloud.pig.marketing.api.util;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.SSLSession;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * JA4指纹生成器（整合TLS会话获取功能）
 * 负责HTTPS请求的JA4指纹生成，内部包含从请求中提取SSL会话的逻辑
 */
@Slf4j
public class JA4GeneratorUtil {

	// 密码套件映射表（可根据实际需求扩展）
	private static final Map<String, String> CIPHER_SUITE_MAPPINGS = new HashMap<>() {{
		put("TLS_AES_128_GCM_SHA256", "a128gcm");
		put("TLS_AES_256_GCM_SHA384", "a256gcm");
		put("TLS_CHACHA20_POLY1305_SHA256", "chacha20");
		put("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "e128gcm");
		put("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "r128gcm");
		put("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "e256gcm");
		put("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "r256gcm");
		put("TLS_RSA_WITH_AES_128_GCM_SHA256", "rsa128gcm");
		put("TLS_RSA_WITH_AES_256_GCM_SHA384", "rsa256gcm");
	}};

	// 扩展类型映射（简化版）
	private static final Map<Integer, String> EXTENSION_MAPPINGS = new HashMap<>() {{
		put(0, "sni");           // 服务器名称指示
		put(10, "ec_points");    // 椭圆曲线点格式
		put(16, "alpn");         // 应用层协议协商
		put(18, "ec_points");    // 椭圆曲线
		put(21, "status_req");   // OCSP状态请求
		put(43, "ec_points");    // 加密套件
		put(44, "ec_points");    // 压缩方法
		put(51, "renegotiate");  // 重新协商
		put(65281, "grease");    // GREASE
	}};

	/**
	 * 从HttpServletRequest中获取SSLSession
	 * 整合原TlsUtils的功能
	 */
	public SSLSession getSslSession(HttpServletRequest request) {
		if (request == null) {
			return null;
		}

		try {
			// 尝试直接从请求属性获取（兼容Tomcat等容器）
			Object sslSession = request.getAttribute("javax.servlet.request.ssl_session");
			if (sslSession instanceof SSLSession) {
				return (SSLSession) sslSession;
			}

			// 反射方式获取（兼容不同容器）
			Method getSessionMethod = request.getClass().getMethod("getSession");
			Object session = getSessionMethod.invoke(request);
			if (session instanceof SSLSession) {
				return (SSLSession) session;
			}

			// 尝试获取SSL会话的其他方法
			Method getSslSessionMethod = request.getClass().getMethod("getSslSession");
			Object result = getSslSessionMethod.invoke(request);
			if (result instanceof SSLSession) {
				return (SSLSession) result;
			}
		} catch (Exception e) {
			// 获取失败，可能不是HTTPS请求或容器不支持
			log.error("Failed to get SSL session", e);
		}

		return null;
	}

	/**
	 * 生成JA4指纹
	 * @param request HTTP请求对象（需为HTTPS请求）
	 * @return JA4指纹字符串，非HTTPS请求或生成失败时返回null
	 */
	public String generateJA4(HttpServletRequest request) {
		try {
			SSLSession sslSession = getSslSession(request);
			if (sslSession == null) {
				return null; // 非HTTPS请求，无法生成指纹
			}
			return generateJA4(sslSession);
		} catch (Exception e) {
			log.error("Failed to generate JA4 fingerprint:{}",e.getMessage());
			return null;
		}
	}

	/**
	 * 生成JA4指纹
	 * @param sslSession SSL会话对象
	 * @return JA4指纹字符串
	 */
	public String generateJA4(SSLSession sslSession) {
		try {
			// 1. 提取TLS版本
			String tlsVersion = getNormalizedTlsVersion(sslSession.getProtocol());

			// 2. 提取密码套件
			String cipherSuite = getNormalizedCipherSuite(sslSession.getCipherSuite());

			// 3. 提取扩展信息（简化实现，实际需解析握手包）
			String extensions = getNormalizedExtensions(sslSession);

			// 4. 组合生成JA4指纹
			String rawJA4 = String.format("%s,%s,%s", tlsVersion, cipherSuite, extensions);

			// 5. 计算哈希值作为最终指纹
			return computeMD5(rawJA4);
		} catch (Exception e) {
			log.error("生成JA4指纹失败:{}", e.getMessage());
			return null;
		}
	}

	/**
	 * 规范化TLS版本
	 */
	private String getNormalizedTlsVersion(String protocol) {
		if (protocol == null) {
			return "unknown";
		}
		return switch (protocol) {
			case "TLSv1.0" -> "1.0";
			case "TLSv1.1" -> "1.1";
			case "TLSv1.2" -> "1.2";
			case "TLSv1.3" -> "1.3";
			default -> protocol.replace("TLSv", "").replace("SSLv", "");
		};
	}

	/**
	 * 规范化密码套件
	 */
	private String getNormalizedCipherSuite(String cipherSuite) {
		if (cipherSuite == null) {
			return "unknown";
		}
		return CIPHER_SUITE_MAPPINGS.getOrDefault(cipherSuite, hashUnknownValue(cipherSuite));
	}

	/**
	 * 规范化TLS扩展信息（基于实际扩展类型提取）
	 */
	private String getNormalizedExtensions(SSLSession sslSession) {
		List<String> extensions = new ArrayList<>();

		try {
			// 1. 尝试从SSLSession中提取扩展信息（不同JDK实现可能有差异）
			// 反射获取底层TLS握手信息（以SunJDK的SSLSessionImpl为例）
			Class<?> sslSessionClass = sslSession.getClass();

			// 2. 提取SNI（服务器名称指示，扩展类型0）
			if (hasSNIExtension(sslSession, sslSessionClass)) {
				extensions.add("sni");
			}

			// 3. 提取ALPN（应用层协议协商，扩展类型16）
			String alpnProtocol = getALPNProtocol(sslSession, sslSessionClass);
			if (alpnProtocol != null && !alpnProtocol.isEmpty()) {
				extensions.add("alpn");
			}

			// 4. 提取椭圆曲线扩展（EC Points，扩展类型10）
			if (hasECExtension(sslSession, sslSessionClass)) {
				extensions.add("ec_points");
			}

			// 5. 提取GREASE扩展（用于测试兼容性，扩展类型0x0a0a等）
			if (hasGreaseExtension(sslSession, sslSessionClass)) {
				extensions.add("grease");
			}

		} catch (Exception e) {
			// 提取失败时返回默认扩展组合（不影响主流程）
			return "sni,alpn,ec_points";
		}

		// 若未提取到任何扩展，返回默认值
		return extensions.isEmpty() ? "sni,alpn,ec_points" : String.join(",", extensions);
	}

	/**
	 * 判断是否存在SNI扩展（服务器名称指示）
	 */
	private boolean hasSNIExtension(SSLSession sslSession, Class<?> sslSessionClass) throws Exception {
		// SunJDK中，SSLSessionImpl的handshakeSession可能包含扩展信息
		Field handshakeSessionField = sslSessionClass.getDeclaredField("handshakeSession");
		handshakeSessionField.setAccessible(true);
		Object handshakeSession = handshakeSessionField.get(sslSession);

		// 从握手信息中获取SNI主机名（若存在则说明有SNI扩展）
		Method getPeerHostMethod = handshakeSession.getClass().getMethod("getPeerHost");
		String peerHost = (String) getPeerHostMethod.invoke(handshakeSession);
		return peerHost != null && !peerHost.isEmpty();
	}

	/**
	 * 获取ALPN协商的协议（如http/1.1、h2等）
	 */
	private String getALPNProtocol(SSLSession sslSession, Class<?> sslSessionClass) throws Exception {
		// 从SSLSession中提取ALPN结果（不同JDK实现方法不同）
		try {
			Method getApplicationProtocolMethod = sslSessionClass.getMethod("getApplicationProtocol");
			return (String) getApplicationProtocolMethod.invoke(sslSession);
		} catch (NoSuchMethodException e) {
			// 旧版本JDK可能没有该方法，返回null
			return null;
		}
	}

	/**
	 * 判断是否存在椭圆曲线扩展
	 */
	private boolean hasECExtension(SSLSession sslSession, Class<?> sslSessionClass) throws Exception {
		// 反射获取TLS握手过程中的椭圆曲线参数
		Field cipherSuiteField = sslSessionClass.getDeclaredField("cipherSuite");
		cipherSuiteField.setAccessible(true);
		String cipherSuite = (String) cipherSuiteField.get(sslSession);

		// 包含ECDHE的密码套件通常依赖椭圆曲线扩展
		return cipherSuite.contains("ECDHE");
	}

	/**
	 * 判断是否存在GREASE扩展（用于兼容性测试的预留扩展）
	 */
	private boolean hasGreaseExtension(SSLSession sslSession, Class<?> sslSessionClass) throws Exception {
		// GREASE扩展类型为0x0a0a、0x1a1a等特殊值，此处简化判断
		// 实际需解析扩展类型列表，这里通过密码套件特征近似判断
		String protocol = sslSession.getProtocol();
		return protocol.startsWith("TLSv1.3") || protocol.startsWith("TLSv1.2");
	}

	/**
	 * 对未知值进行哈希处理
	 */
	private String hashUnknownValue(String value) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] digest = md.digest(value.getBytes(StandardCharsets.UTF_8));
			StringBuilder hexString = new StringBuilder();
			for (byte b : digest) {
				hexString.append(String.format("%02x", b));
			}
			return hexString.substring(0, 8); // 取前8位
		} catch (NoSuchAlgorithmException e) {
			return value.hashCode() + "";
		}
	}

	/**
	 * 计算MD5哈希
	 */
	private String computeMD5(String input) throws NoSuchAlgorithmException {
		MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));

		StringBuilder hexString = new StringBuilder();
		for (byte b : digest) {
			hexString.append(String.format("%02x", b));
		}
		return hexString.toString();
	}
}