package com.pig4cloud.pig.common.core.util.ip;

import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.common.core.exception.BusinessException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.log4j.Log4j2;
import org.lionsoul.ip2region.xdb.Searcher;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Log4j2
public class IPUtils {

	// 内网IP段正则
	private static final String INTRANET_IP_REGEX = "(10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|" +
			"(172\\.((1[6-9])|(2\\d)|(3[01]))\\.\\d{1,3}\\.\\d{1,3})|" +
			"(192\\.168\\.\\d{1,3}\\.\\d{1,3})|" +
			"(127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})";
	private static final Pattern INTRANET_PATTERN = Pattern.compile(INTRANET_IP_REGEX);

	// IP地址合法性校验正则
	private static final String IP_REGEX = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\." +
			"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." +
			"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." +
			"(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
	private static final Pattern IP_PATTERN = Pattern.compile(IP_REGEX);

	private static Searcher searcher;

	static {
		initIpSearcher();
	}

	/**
	 * 初始化IP地址索引
	 */
	private static void initIpSearcher() {
		try {
			// 仅使用相对路径查找（工作目录、jar同目录、常见容器目录）
			java.util.List<Path> candidates = new java.util.ArrayList<>();
			try {
				Path cwd = Paths.get("").toAbsolutePath();
				candidates.add(cwd.resolve("ip/ip2region.xdb"));
				candidates.add(cwd.resolve("ip2region.xdb"));
				// 以运行jar所在目录为基准
				URL loc = IPUtils.class.getProtectionDomain().getCodeSource().getLocation();
				if (loc != null) {
					Path base;
					try {
						Path jarPath = Paths.get(loc.toURI());
						base = Files.isRegularFile(jarPath) ? jarPath.getParent() : jarPath;
					} catch (Exception e) {
						base = null;
					}
					if (base != null) {
						candidates.add(base.resolve("ip/ip2region.xdb"));
						candidates.add(base.resolve("../ip/ip2region.xdb"));
						candidates.add(base.resolve("ip2region.xdb"));
					}
				}
				// 常见容器工作目录
				candidates.add(Paths.get("/pig-marketing/ip/ip2region.xdb"));
			} catch (Exception ignore) {}

			for (Path cand : candidates) {
				try {
					if (cand != null && Files.exists(cand)) {
						Long sz = null; try { sz = Files.size(cand); } catch (Exception ignore) {}
						log.info("ip2region尝试相对路径: path={}, size={}", cand, sz);
						searcher = Searcher.newWithFileOnly(cand.toString());
						log.info("ip2region初始化成功 (relative path: {})", cand);
						return;
					}
				} catch (Exception tryNext) {
					log.warn("ip2region相对路径初始化失败: {}", cand, tryNext);
				}
			}

			log.error("ip2region初始化失败：未在相对路径找到ip2region.xdb，请将文件放在 ./ip/ip2region.xdb 或与jar同目录的ip目录中");
			searcher = null;
		} catch (Exception e) {
			log.error("ip2region初始化失败", e);
			searcher = null;
		}
	}

	/**
	 * 获取IP地址
	 *
	 * @param request request
	 * @return ipAddress
	 */
	public static String getIpAddress(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		// 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
		if (ip != null && ip.length() > 15) {
			if (ip.indexOf(",") > 0) {
				ip = ip.substring(0, ip.indexOf(","));
			}
		}
		if ("127.0.0.1".equals(ip)) {
			// 获取本机真正的ip地址
			try {
				ip = InetAddress.getLocalHost().getHostAddress();
			} catch (UnknownHostException e) {
				log.error(e.getMessage(), e);
			}
		}
		return ip;
	}

	/**
	 * 判断是否内网IP
	 * @param ip ip
	 * @return boolean
	 */
	public static boolean isIntranetIp(String ip) {
		if (StrUtil.isBlank(ip)) {
			return false;
		}
		Matcher matcher = INTRANET_PATTERN.matcher(ip);
		return matcher.matches();
	}

	/**
	 * 检查IP是否合法
	 * @param ip ip
	 * @return boolean
	 */

	public static boolean isValidIp(String ip) {
		if (StrUtil.isBlank(ip)) {
			return false;
		}
		Matcher matcher = IP_PATTERN.matcher(ip);
		return matcher.matches();
	}

	/**
	 * 比较两个IP地址大小（将IP转为Long比较）
	 * @return true：endIp > startIp
	 */
	public static boolean isEndIpGreater(String startIp, String endIp) {
		if (!isValidIp(startIp) || !isValidIp(endIp)) {
			return false;
		}
		return ipToLong(startIp) < ipToLong(endIp);
	}

	/**
	 * 获取IP归属地（国家-省份）
	 * @param ip ip
	 * @return 国家-省份
	 */
	public static String getIpRegion(String ip) {
		// 处理多IP情况，取第一个
		if (StrUtil.isNotBlank(ip) && ip.contains(",")) {
			String[] split = ip.split(",");
			ip = split[0].trim();
		}

		// 检查IP合法性
		if (!isValidIp(ip)) {
			return "未知";
		}

		// 检查是否为内网IP
		if (isIntranetIp(ip)) {
			return "内网";
		}

		// 如果搜索器未初始化成功，返回未知
		if (searcher == null) {
			log.warn("ip2region搜索器未初始化，无法查询IP归属地");
			return "未知";
		}

		try {
			String region = searcher.search(ip);
			if (StrUtil.isNotBlank(region)) {
				List<String> arr = StrUtil.split(region, '|');
				// 确保数组有足够的元素
				if (arr.size() >= 3) {
					String country = "0".equals(arr.get(0)) ? "未知" : arr.get(0);
					String province = "0".equals(arr.get(2)) ? "未知" : arr.get(2);

					// 处理国外省份可能为0的情况
					if ("未知".equals(province) && !"中国".equals(country)) {
						return country;
					}

					return country + "-" + province;
				}
			}
		} catch (Exception e) {
			log.warn("查询IP[{}]归属地出错", ip, e);
		}

		return "未知";
	}

	/**
	 * 获取外网IP
	 * @return ip
	 */
	public static String getPublicIp() {
		String ip = "";
		StringBuilder inputLine = new StringBuilder();
		String read;
		BufferedReader in = null;
		try {
			URL url = new URL("http://ip.chinaz.com");
			HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
			in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
			while ((read = in.readLine()) != null) {
				inputLine.append(read).append("\r\n");
			}
		} catch (IOException e) {
			log.error("获取公网IP失败:{}",e.getMessage());
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					log.error("获取公网IP失败:{}",e.getMessage());
				}
			}
		}
		Pattern p = Pattern.compile("<dd class=\"fz24\">(.*?)</dd>");
		Matcher m = p.matcher(inputLine.toString());
		if (m.find()) {
			ip = m.group(1);
		}
		log.info("获取的公网ip为[{}]", ip);
		return ip;
	}

	public static void main(String[] args) {
		// 测试案例
		System.out.println(getIpRegion("39.144.24.215"));   // 中国-北京
		System.out.println(getIpRegion("103.26.94.206"));  // 中国-香港
		System.out.println(getIpRegion("153.35.52.159"));  // 日本-未知
		System.out.println(getIpRegion("8.8.8.8"));        // 美国-加利福尼亚州
		System.out.println(getIpRegion("192.168.1.1"));    // 全球（内网）
		System.out.println(getIpRegion("127.0.0.1"));      // 全球（内网）
		System.out.println(getIpRegion("invalid.ip"));     // 未知（非法IP）
		System.out.println(getIpRegion(""));               // 未知（空IP）
	}

	/**
	 * 验证IP列表，检查是否存在冲突
	 * @param ipList IP列表
	 */
	public static void validateIpList(List<String> ipList) {
		if (ipList == null || ipList.isEmpty()) {
			return;
		}

		// 过滤空值和去重
		List<String> validIps = ipList.stream()
				.filter(org.springframework.util.StringUtils::hasText)
				.map(String::trim)
				.toList();

		// 检查是否有重复的单IP
		Set<String> singleIps = new HashSet<>();
		Set<String> ipRanges = new HashSet<>();

		for (String ip : validIps) {
			if (ip.contains("/")) {
				// IP段格式
				ipRanges.add(ip);
			} else {
				// 单IP格式
				if (!singleIps.add(ip)) {
					throw new BusinessException("IP列表中存在重复的单IP: " + ip);
				}
			}
		}

		// 检查是否有重复的IP段
		List<String> rangeList = validIps.stream()
				.filter(ip -> ip.contains("/"))
				.toList();
		Set<String> uniqueRanges = new HashSet<>(rangeList);
		if (rangeList.size() != uniqueRanges.size()) {
			throw new BusinessException("IP列表中存在重复的IP段");
		}

		// 检查单IP是否在IP段中
		for (String singleIp : singleIps) {
			for (String ipRange : ipRanges) {
				if (isIpInRange(singleIp, ipRange)) {
					throw new BusinessException("单IP '" + singleIp + "' 在IP段 '" + ipRange + "' 中，存在冲突");
				}
			}
		}

		// 检查IP段之间是否重叠
		List<String> uniqueRangeList = new ArrayList<>(ipRanges);
		for (int i = 0; i < uniqueRangeList.size(); i++) {
			for (int j = i + 1; j < uniqueRangeList.size(); j++) {
				if (isIpRangesOverlap(uniqueRangeList.get(i), uniqueRangeList.get(j))) {
					throw new BusinessException("IP段 '" + uniqueRangeList.get(i) + "' 和 '" + uniqueRangeList.get(j) + "' 存在重叠");
				}
			}
		}
	}

	/**
	 * 检查单IP是否在IP段范围内
	 * @param singleIp 单IP
	 * @param ipRange IP段 (格式: 192.168.10.200/220 表示 192.168.10.200~192.168.10.220)
	 * @return 是否在范围内
	 */
	public static boolean isIpInRange(String singleIp, String ipRange) {
		if (!IPUtils.isValidIp(singleIp)) {
			return false;
		}

		String[] parts = ipRange.split("/");
		if (parts.length != 2) {
			return false;
		}

		String startIp = parts[0];
		String endPart = parts[1];

		if (!IPUtils.isValidIp(startIp)) {
			return false;
		}

		// 构造完整的结束IP
		String[] startParts = startIp.split("\\.");
		String endIp = String.format("%s.%s.%s.%s",
				startParts[0], startParts[1], startParts[2], endPart);

		if (!IPUtils.isValidIp(endIp)) {
			return false;
		}

		long singleIpLong = ipToLong(singleIp);
		long startIpLong = ipToLong(startIp);
		long endIpLong = ipToLong(endIp);

		// 确保开始IP小于等于结束IP
		if (startIpLong > endIpLong) {
			long temp = startIpLong;
			startIpLong = endIpLong;
			endIpLong = temp;
		}

		return singleIpLong >= startIpLong && singleIpLong <= endIpLong;
	}

	/**
	 * 检查两个IP段是否重叠
	 * @param range1 第一个IP段
	 * @param range2 第二个IP段
	 * @return 是否重叠
	 */
	public static boolean isIpRangesOverlap(String range1, String range2) {
		String[] parts1 = range1.split("/");
		String[] parts2 = range2.split("/");

		if (parts1.length != 2 || parts2.length != 2) {
			return false;
		}

		String network1 = parts1[0];
		String network2 = parts2[0];
		int prefix1 = Integer.parseInt(parts1[1]);
		int prefix2 = Integer.parseInt(parts2[1]);

		if (!IPUtils.isValidIp(network1) || !IPUtils.isValidIp(network2)) {
			return false;
		}

		long network1Long = ipToLong(network1);
		long network2Long = ipToLong(network2);

		// 计算网络掩码
		long mask1 = (0xFFFFFFFFL << (32 - prefix1)) & 0xFFFFFFFFL;
		long mask2 = (0xFFFFFFFFL << (32 - prefix2)) & 0xFFFFFFFFL;

		// 计算网络地址
		long net1 = network1Long & mask1;
		long net2 = network2Long & mask2;

		// 计算广播地址
		long broadcast1 = net1 | (~mask1 & 0xFFFFFFFFL);
		long broadcast2 = net2 | (~mask2 & 0xFFFFFFFFL);

		// 检查是否重叠：一个网络的开始地址小于等于另一个网络的结束地址，且一个网络的结束地址大于等于另一个网络的开始地址
		return net1 <= broadcast2 && broadcast1 >= net2;
	}

	/**
	 * 将IP地址转换为长整型
	 * @param ip IP地址
	 * @return 长整型值
	 */
	public static long ipToLong(String ip) {
		String[] parts = ip.split("\\.");
		long result = 0;
		for (int i = 0; i < 4; i++) {
			result |= Long.parseLong(parts[i]) << (24 - i * 8);
		}
		return result;
	}
}
