<?php
declare (strict_types=1);

namespace app\service;

class IpWhiteListService
{
    /**
     * 验证IP是否在白名单中
     * 
     * @param string $clientIp 客户端IP
     * @param string $whiteListIp 白名单IP配置
     * @return bool
     */
    public static function checkIpWhiteList(string $clientIp, string $whiteListIp): bool
    {
        // 如果白名单为空，表示不限制IP
        if (empty($whiteListIp)) {
            return true;
        }
        
        // 解析白名单IP配置
        $whiteList = self::parseWhiteListIp($whiteListIp);
        
        foreach ($whiteList as $allowedIp) {
            if (self::matchIp($clientIp, $allowedIp)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 解析白名单IP配置
     * 支持格式：
     * - 单个IP: 192.168.1.1
     * - IP段: 192.168.1.0/24
     * - IP范围: 192.168.1.1-192.168.1.100
     * - 多个配置用逗号分隔: 192.168.1.1,10.0.0.0/8,172.16.0.1-172.16.0.100
     * 
     * @param string $whiteListIp
     * @return array
     */
    public static function parseWhiteListIp(string $whiteListIp): array
    {
        $whiteList = [];
        $items = array_filter(array_map('trim', explode(',', $whiteListIp)));
        
        foreach ($items as $item) {
            if (!empty($item)) {
                $whiteList[] = $item;
            }
        }
        
        return $whiteList;
    }
    
    /**
     * 匹配IP地址
     * 
     * @param string $clientIp 客户端IP
     * @param string $allowedIp 允许的IP配置
     * @return bool
     */
    private static function matchIp(string $clientIp, string $allowedIp): bool
    {
        // 精确匹配
        if ($clientIp === $allowedIp) {
            return true;
        }
        
        // CIDR格式匹配 (例: 192.168.1.0/24)
        if (strpos($allowedIp, '/') !== false) {
            return self::matchCidr($clientIp, $allowedIp);
        }
        
        // IP范围匹配 (例: 192.168.1.1-192.168.1.100)
        if (strpos($allowedIp, '-') !== false) {
            return self::matchRange($clientIp, $allowedIp);
        }
        
        // 通配符匹配 (例: 192.168.1.*)
        if (strpos($allowedIp, '*') !== false) {
            return self::matchWildcard($clientIp, $allowedIp);
        }
        
        return false;
    }
    
    /**
     * CIDR格式IP匹配
     * 
     * @param string $clientIp
     * @param string $cidr
     * @return bool
     */
    private static function matchCidr(string $clientIp, string $cidr): bool
    {
        list($subnet, $mask) = explode('/', $cidr);
        
        if (!filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
            !filter_var($clientIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return false;
        }
        
        $mask = intval($mask);
        if ($mask < 0 || $mask > 32) {
            return false;
        }
        
        $clientIpLong = ip2long($clientIp);
        $subnetLong = ip2long($subnet);
        $maskLong = (-1 << (32 - $mask));
        
        return ($clientIpLong & $maskLong) === ($subnetLong & $maskLong);
    }
    
    /**
     * IP范围匹配
     * 
     * @param string $clientIp
     * @param string $range
     * @return bool
     */
    private static function matchRange(string $clientIp, string $range): bool
    {
        list($startIp, $endIp) = explode('-', $range, 2);
        $startIp = trim($startIp);
        $endIp = trim($endIp);
        
        if (!filter_var($startIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
            !filter_var($endIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
            !filter_var($clientIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return false;
        }
        
        $clientIpLong = ip2long($clientIp);
        $startIpLong = ip2long($startIp);
        $endIpLong = ip2long($endIp);
        
        return $clientIpLong >= $startIpLong && $clientIpLong <= $endIpLong;
    }
    
    /**
     * 通配符匹配
     * 
     * @param string $clientIp
     * @param string $pattern
     * @return bool
     */
    private static function matchWildcard(string $clientIp, string $pattern): bool
    {
        $pattern = str_replace('.', '\.', $pattern);
        $pattern = str_replace('*', '[0-9]+', $pattern);
        $pattern = '/^' . $pattern . '$/';
        
        return preg_match($pattern, $clientIp) === 1;
    }
        
    /**
     * 验证IP白名单格式
     * 
     * @param string $whiteListIp
     * @return array [bool $isValid, string $message, array $parsedList]
     */
    public static function validateWhiteListFormat(string $whiteListIp): array
    {
        if (empty($whiteListIp)) {
            return [true, '白名单为空，表示不限制IP访问', []];
        }
        
        $items = self::parseWhiteListIp($whiteListIp);
        $validItems = [];
        $errors = [];
        
        foreach ($items as $item) {
            $validation = self::validateSingleIpFormat($item);
            if ($validation['valid']) {
                $validItems[] = [
                    'input' => $item,
                    'type' => $validation['type'],
                    'description' => $validation['description']
                ];
            } else {
                $errors[] = "'{$item}': " . $validation['message'];
            }
        }
        
        $isValid = empty($errors);
        $message = $isValid ? 'IP白名单格式正确' : 'IP白名单格式错误: ' . implode(', ', $errors);
        
        return [$isValid, $message, $validItems];
    }
    
    /**
     * 验证单个IP配置格式
     * 
     * @param string $ipConfig
     * @return array
     */
    private static function validateSingleIpFormat(string $ipConfig): array
    {
        // 精确IP
        if (filter_var($ipConfig, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return [
                'valid' => true,
                'type' => 'exact',
                'description' => "精确IP: {$ipConfig}"
            ];
        }
        
        // CIDR格式
        if (strpos($ipConfig, '/') !== false) {
            list($subnet, $mask) = explode('/', $ipConfig);
            if (filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && 
                is_numeric($mask) && $mask >= 0 && $mask <= 32) {
                return [
                    'valid' => true,
                    'type' => 'cidr',
                    'description' => "CIDR网段: {$ipConfig}"
                ];
            }
        }
        
        // IP范围
        if (strpos($ipConfig, '-') !== false) {
            list($startIp, $endIp) = explode('-', $ipConfig, 2);
            $startIp = trim($startIp);
            $endIp = trim($endIp);
            
            if (filter_var($startIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) &&
                filter_var($endIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
                return [
                    'valid' => true,
                    'type' => 'range',
                    'description' => "IP范围: {$startIp} 到 {$endIp}"
                ];
            }
        }
        
        // 通配符
        if (strpos($ipConfig, '*') !== false) {
            $parts = explode('.', $ipConfig);
            if (count($parts) === 4) {
                $valid = true;
                foreach ($parts as $part) {
                    if ($part !== '*' && (!is_numeric($part) || $part < 0 || $part > 255)) {
                        $valid = false;
                        break;
                    }
                }
                if ($valid) {
                    return [
                        'valid' => true,
                        'type' => 'wildcard',
                        'description' => "通配符IP: {$ipConfig}"
                    ];
                }
            }
        }
        
        return [
            'valid' => false,
            'message' => '不支持的IP格式'
        ];
    }
}