package me.qwq.doghouse.util;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Enumeration;

import jakarta.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ArrayUtils;

public class IPUtils {
    
    public static final String[] LOCALHOSTS = {"127.0.0.1", "::1", "0:0:0:0:0:0:0:1"};
    public static final String[] HEADERS = {
        "x-real-ip", "client-ip", "x-forwarded-for", 
        "x-forwarded", "x-cluster-client-ip",
        "forwarded-for", "forwarded", "cf-connecting-ip"
    };
    
    public static String getClientIP(HttpServletRequest request){
        String ipAddress = null;  
        for (String header : HEADERS) {
            ipAddress = request.getHeader(header);
            if(!(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress))) {  
                break;
            }  
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  
            ipAddress = request.getRemoteAddr();
            if(ArrayUtils.contains(LOCALHOSTS, ipAddress)){  
                //根据网卡取本机配置的IP  
                InetAddress inet = null;  
                try {  
                    inet = InetAddress.getLocalHost();  
                } catch (UnknownHostException e) {  
                    e.printStackTrace();  
                }  
                ipAddress = inet.getHostAddress();  
            }  
        }  
        //对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割  
        if (ipAddress != null && ipAddress.length() > 15){
            if (ipAddress.indexOf(",") > 0) {  
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));  
            }
        }  
        return ipAddress; 
    }
    
    /**
     * 字符串是否是合法格式 IP, 包括 IPv4 和 IPv6
     * @param ip
     * @return
     */
    public static boolean isValidIp(String ip) {
        try {
            InetAddress addr = InetAddress.getByName(ip);
            return !addr.isAnyLocalAddress();
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * 本机地址
     * @param ip
     * @return
     */
    public static Boolean isLocalhost(String ip) {
        if (ip == null) return false;
        return ArrayUtils.contains(LOCALHOSTS, ip) || ip.startsWith("::1");
    }
    
    /**
     * 是否是有效的 IPv4、IPv6 或 CIDR
     * @param s
     * @return
     */
    public static boolean isValidIPOrCIDR(String s) {
        int slash = s.indexOf('/');
        if (slash < 0) {
            return isIPv4(s) || isIPv6(s);
        }
        return isCIDR(s);
    }
    
    /**
     * 是否是有效的 cidr
     * @param s
     * @return
     */
    public static boolean isCIDR(String s) {
        int slash = s.indexOf('/');
        if (slash < 0) {
            return false;
        }
        String ip = s.substring(0, slash);
        String prefix = s.substring(slash + 1);

        if (isIPv4(ip)) {
            return isValidNumber(prefix, 0, 32);
        }
        if (isIPv6(ip)) {
            return isValidNumber(prefix, 0, 128);
        }
        return false;
    }
    
    /**
     * 是否是 IP
     * @param s
     * @return
     */
    public static boolean isIP(String s) {
        return isIPv4(s) || isIPv6(s);
    }
    
    /**
     * 是否是 IPv4
     * @param s
     * @return
     */
    public static boolean isIPv4(String s) {
        String[] parts = s.split("\\.", -1);
        if (parts.length != 4) return false;

        for (String p : parts) {
            if (p.isEmpty()) return false;
            // 不能有前导零
            if (p.length() > 1 && p.charAt(0) == '0') return false;
            if (!isDigits(p)) return false;
            int n = Integer.parseInt(p);
            if (n < 0 || n > 255) return false;
        }
        return true;
    }
    
    /**
     * 是否是 IPv6
     * @param s
     * @return
     */
    public static boolean isIPv6(String s) {
        // IPv6 中出现 "::" 最多一次
        if (s.indexOf("::") != s.lastIndexOf("::")) {
            return false;
        }

        String[] parts = s.split(":", -1);

        // "::" 情况
        if (s.contains("::")) {
            if (parts.length > 8) return false;
        } else {
            if (parts.length != 8) return false;
        }

        int validGroups = 0;
        for (String part : parts) {
            if (part.isEmpty()) continue; // "::" 产生的空段跳过
            if (part.length() > 4) return false;
            if (!isHexDigits(part)) return false;
            validGroups++;
        }

        return validGroups <= 8;
    }
    
    public static String getLocalMachineIp() {
        try {
            Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
            while (nets.hasMoreElements()) {
                NetworkInterface ni = nets.nextElement();
                if (ni.isLoopback() || ni.isVirtual() || !ni.isUp()) continue;

                // IPv4 优先
                Enumeration<InetAddress> addrs = ni.getInetAddresses();
                InetAddress ipv6Candidate = null;
                while (addrs.hasMoreElements()) {
                    InetAddress addr = addrs.nextElement();
                    if (addr instanceof Inet4Address) {
                        return addr.getHostAddress();
                    }
                    if (ipv6Candidate == null && addr instanceof Inet6Address) {
                        ipv6Candidate = addr;
                    }
                }
                if (ipv6Candidate != null) {
                    return stripZoneId(ipv6Candidate.getHostAddress());
                }
            }
        } catch (Exception ignored) {}
        return null;
    }
    
    public static String stripZoneId(String ip) {
        if (ip == null) return null;
        int idx = ip.indexOf('%');
        return idx > 0 ? ip.substring(0, idx) : ip;
    }
    
    private static boolean isValidNumber(String s, int min, int max) {
        if (!isDigits(s)) return false;
        int n;
        try { n = Integer.parseInt(s); }
        catch (Exception e) { return false; }
        return n >= min && n <= max;
    }

    private static boolean isDigits(String s) {
        for (char c : s.toCharArray()) {
            if (c < '0' || c > '9') return false;
        }
        return true;
    }

    private static boolean isHexDigits(String s) {
        for (char c : s.toCharArray()) {
            boolean digit = (c >= '0' && c <= '9');
            boolean lower = (c >= 'a' && c <= 'f');
            boolean upper = (c >= 'A' && c <= 'F');
            if (!(digit || lower || upper)) return false;
        }
        return true;
    }

}
