<?php
/**
 * Proxy Presets Manager
 *
 * Manages trusted proxy IP ranges for common CDN/proxy providers
 *
 * @package VMPFence
 * @since 2.2.2
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

class VMPFence_Proxy_Presets {
    
    /**
     * Get all proxy presets
     *
     * @return array Proxy presets with IP ranges
     */
    public static function get_presets() {
        return array(
            'cloudflare' => array(
                'name' => 'Cloudflare',
                'ipv4' => array(
                    '173.245.48.0/20',
                    '103.21.244.0/22',
                    '103.22.200.0/22',
                    '103.31.4.0/22',
                    '141.101.64.0/18',
                    '108.162.192.0/18',
                    '190.93.240.0/20',
                    '188.114.96.0/20',
                    '197.234.240.0/22',
                    '198.41.128.0/17',
                    '162.158.0.0/15',
                    '104.16.0.0/13',
                    '104.24.0.0/14',
                    '172.64.0.0/13',
                    '131.0.72.0/22',
                ),
                'ipv6' => array(
                    '2400:cb00::/32',
                    '2606:4700::/32',
                    '2803:f800::/32',
                    '2405:b500::/32',
                    '2405:8100::/32',
                    '2a06:98c0::/29',
                    '2c0f:f248::/32',
                ),
            ),
            'cloudfront' => array(
                'name' => 'Amazon CloudFront',
                'ipv4' => array(
                    // CloudFront IP ranges (sample - these change frequently)
                    '13.32.0.0/15',
                    '13.35.0.0/16',
                    '13.224.0.0/14',
                    '13.249.0.0/16',
                    '18.64.0.0/14',
                    '34.195.252.0/24',
                    '34.226.14.0/24',
                    '52.46.0.0/18',
                    '52.84.0.0/15',
                    '52.94.248.0/28',
                    '54.182.0.0/16',
                    '54.192.0.0/16',
                    '54.230.0.0/16',
                    '54.239.128.0/18',
                    '64.252.64.0/18',
                    '70.132.0.0/18',
                    '99.84.0.0/16',
                    '204.246.164.0/22',
                    '204.246.168.0/22',
                    '204.246.174.0/23',
                    '204.246.176.0/20',
                    '205.251.192.0/19',
                    '205.251.249.0/24',
                    '205.251.250.0/23',
                    '205.251.252.0/23',
                    '216.137.32.0/19',
                ),
                'ipv6' => array(
                    '2600:9000::/28',
                ),
            ),
            'ezoic' => array(
                'name' => 'Ezoic',
                'ipv4' => array(
                    '188.172.128.0/20',
                    '188.172.224.0/19',
                    '141.101.115.0/24',
                    '141.101.116.0/24',
                    '141.101.117.0/24',
                    '141.101.118.0/24',
                    '141.101.119.0/24',
                    '141.101.120.0/24',
                    '141.101.121.0/24',
                    '141.101.122.0/24',
                    '141.101.123.0/24',
                ),
                'ipv6' => array(),
            ),
            'quic' => array(
                'name' => 'Quic.cloud',
                'ipv4' => array(
                    '104.156.80.0/20',
                    '172.81.0.0/17',
                    '185.56.148.0/22',
                ),
                'ipv6' => array(
                    '2001:19f0::/32',
                    '2a02:26f0::/32',
                ),
            ),
            'sucuri' => array(
                'name' => 'Sucuri',
                'ipv4' => array(
                    '66.228.32.0/20',
                    '185.93.228.0/22',
                    '192.88.134.0/23',
                    '192.124.249.0/24',
                    '2.21.24.0/22',
                    '185.180.12.0/22',
                ),
                'ipv6' => array(
                    '2a02:fe80::/29',
                ),
            ),
            'incapsula' => array(
                'name' => 'Incapsula',
                'ipv4' => array(
                    '45.64.64.0/22',
                    '45.60.0.0/16',
                    '103.28.248.0/22',
                    '185.11.124.0/22',
                    '192.230.64.0/18',
                    '107.154.0.0/16',
                    '199.83.128.0/21',
                ),
                'ipv6' => array(
                    '2a02:e980::/29',
                ),
            ),
        );
    }
    
    /**
     * Get preset options for select dropdown
     *
     * @return array Array of preset keys and names
     */
    public static function get_preset_options() {
        $presets = self::get_presets();
        $options = array('' => 'None');
        
        foreach ($presets as $key => $preset) {
            $options[$key] = $preset['name'];
        }
        
        return $options;
    }
    
    /**
     * Get IP ranges for a specific preset
     *
     * @param string $preset_key Preset key
     * @return array Array with 'ipv4' and 'ipv6' ranges, or empty array if not found
     */
    public static function get_preset_ips($preset_key) {
        if (empty($preset_key)) {
            return array('ipv4' => array(), 'ipv6' => array());
        }
        
        $presets = self::get_presets();
        
        if (isset($presets[$preset_key])) {
            return array(
                'ipv4' => $presets[$preset_key]['ipv4'],
                'ipv6' => $presets[$preset_key]['ipv6'],
            );
        }
        
        return array('ipv4' => array(), 'ipv6' => array());
    }
    
    /**
     * Get unified list of trusted proxies (manual + preset)
     *
     * @return array Array of IP addresses and CIDR ranges
     */
    public static function get_unified_trusted_proxies() {
        $proxies = array();
        
        // Get manually entered proxies
        $manual = VMPFence_Config::get('trusted_proxies', '');
        if (!empty($manual)) {
            $manual_proxies = preg_split('/[\r\n,]+/', $manual);
            $manual_proxies = array_filter(array_map('trim', $manual_proxies));
            $proxies = array_merge($proxies, $manual_proxies);
        }
        
        // Get preset proxies
        $preset_key = VMPFence_Config::get('trusted_proxy_preset', '');
        if (!empty($preset_key)) {
            $preset_ips = self::get_preset_ips($preset_key);
            
            // Validate and add IPv4 ranges
            foreach ($preset_ips['ipv4'] as $ip) {
                if (!empty($ip) && (self::is_valid_ip($ip) || self::is_valid_cidr($ip))) {
                    $proxies[] = $ip;
                }
            }
            
            // Validate and add IPv6 ranges
            foreach ($preset_ips['ipv6'] as $ip) {
                if (!empty($ip) && (self::is_valid_ip($ip) || self::is_valid_cidr($ip))) {
                    $proxies[] = $ip;
                }
            }
        }
        
        return array_unique($proxies);
    }
    
    /**
     * Check if an IP is valid
     *
     * @param string $ip IP address
     * @return bool True if valid
     */
    private static function is_valid_ip($ip) {
        return filter_var($ip, FILTER_VALIDATE_IP) !== false;
    }
    
    /**
     * Check if a CIDR range is valid
     *
     * @param string $cidr CIDR range (e.g., 192.168.1.0/24)
     * @return bool True if valid
     */
    private static function is_valid_cidr($cidr) {
        if (strpos($cidr, '/') === false) {
            return false;
        }
        
        list($ip, $mask) = explode('/', $cidr, 2);
        
        // Validate IP part
        if (!self::is_valid_ip($ip)) {
            return false;
        }
        
        // Validate mask
        $mask = (int) $mask;
        
        // Check if IPv4 or IPv6
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return $mask >= 0 && $mask <= 32;
        } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            return $mask >= 0 && $mask <= 128;
        }
        
        return false;
    }
    
    /**
     * Check if an IP is within a CIDR range
     *
     * @param string $ip IP address to check
     * @param string $cidr CIDR range
     * @return bool True if IP is in range
     */
    public static function ip_in_range($ip, $cidr) {
        // Handle plain IP (not CIDR)
        if (strpos($cidr, '/') === false) {
            return $ip === $cidr;
        }
        
        list($subnet, $mask) = explode('/', $cidr);
        
        // Convert to long for IPv4
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && 
            filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            
            $ip_long = ip2long($ip);
            $subnet_long = ip2long($subnet);
            $mask_long = -1 << (32 - (int)$mask);
            
            return ($ip_long & $mask_long) === ($subnet_long & $mask_long);
        }
        
        // IPv6 handling (simplified)
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && 
            filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            
            $ip_bin = inet_pton($ip);
            $subnet_bin = inet_pton($subnet);
            
            if ($ip_bin === false || $subnet_bin === false) {
                return false;
            }
            
            $mask = (int)$mask;
            $ip_bits = unpack('C*', $ip_bin);
            $subnet_bits = unpack('C*', $subnet_bin);
            
            for ($i = 1; $i <= 16; $i++) {
                $bits_to_check = min(8, $mask);
                $bitmask = (0xFF << (8 - $bits_to_check)) & 0xFF;
                
                if (($ip_bits[$i] & $bitmask) !== ($subnet_bits[$i] & $bitmask)) {
                    return false;
                }
                
                $mask -= $bits_to_check;
                if ($mask <= 0) {
                    break;
                }
            }
            
            return true;
        }
        
        return false;
    }
}
