<?php
/**
 * IP Whitelist Controller
 *
 * Manages IP whitelisting functionality that bypasses all firewall rules.
 *
 * @package VMPFence
 * @since 2.0.0
 */

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

/**
 * IP Whitelist Controller Class
 *
 * @since 2.0.0
 */
class VMPFence_IP_Whitelist_Controller {

    /**
     * Cache for whitelist status by IP
     *
     * @var array
     */
    private static $cache = array();

    /**
     * Parsed whitelist entries
     *
     * @var array|null
     */
    private static $whitelist_entries = null;

    /**
     * Check if IP is whitelisted
     *
     * @since 2.0.0
     * @param string $ip IP address to check
     * @return bool True if whitelisted, false otherwise
     */
    public static function is_whitelisted($ip) {
        // Check cache first
        $ip_hash = hash('sha256', $ip);
        if (isset(self::$cache[$ip_hash])) {
            return self::$cache[$ip_hash];
        }
        
        // Check against whitelist
        $result = self::check_whitelist($ip);

        // Cache result
        self::$cache[$ip_hash] = $result;

        return $result;
    }

    /**
     * Check IP against whitelist entries
     *
     * @since 2.0.0
     * @param string $ip IP address to check
     * @return bool
     */
    private static function check_whitelist($ip) {
        // Get whitelist entries
        $entries = self::get_whitelist_entries();

        if (empty($entries)) {
            return false;
        }

        // Check each entry
        foreach ($entries as $index => $entry) {
            if (empty($entry)) {
                continue;
            }
            
            $ip_range = new VMPFence_IP_Range($entry);
            if ($ip_range->is_ip_in_range($ip)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get whitelist entries from config
     *
     * @since 2.0.0
     * @return array Array of IP ranges
     */
    private static function get_whitelist_entries() {
        // Return cached entries if available
        if (self::$whitelist_entries !== null) {
            return self::$whitelist_entries;
        }
        
        // Get from config
        $whitelist_raw = VMPFence_Config::get('whitelisted_ips_bypass', '');

        if (empty($whitelist_raw)) {
            self::$whitelist_entries = array();
            return self::$whitelist_entries;
        }

        // Parse entries (comma or newline separated)
        $entries = array();
        
        // Split by newlines first
        $lines = preg_split('/\r\n|\r|\n/', $whitelist_raw);
        
        foreach ($lines as $line_num => $line) {
            // Split by comma
            $items = explode(',', $line);
            foreach ($items as $item) {
                $item = trim($item);
                if (!empty($item)) {
                    $entries[] = $item;
                }
            }
        }

        self::$whitelist_entries = array_unique(array_filter($entries));
        
        return self::$whitelist_entries;
    }

    /**
     * Check if IP is a private/local address
     *
     * @since 2.0.0
     * @param string $ip IP address to check
     * @return bool True if private address
     */
    public static function is_private_address($ip) {
        $private_ranges = array(
            '10.0.0.0/8',           // Private network
            '127.0.0.0/8',          // Loopback
            '172.16.0.0/12',        // Private network
            '192.0.0.0/29',         // IPv6 to IPv4 relay
            '192.168.0.0/16',       // Private network
            '::1/128',              // IPv6 loopback
            'fc00::/7',             // IPv6 unique local
            'fe80::/10',            // IPv6 link-local
        );

        foreach ($private_ranges as $range) {
            $ip_range = new VMPFence_IP_Range($range);
            if ($ip_range->is_ip_in_range($ip)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if IP is whitelisted (including private networks)
     *
     * @since 2.0.0
     * @param string $ip IP address to check
     * @param bool $include_private Whether to include private network check
     * @return bool
     */
    public static function is_whitelisted_or_private($ip, $include_private = true) {
        // Check user whitelist first
        if (self::is_whitelisted($ip)) {
            return true;
        }

        // Check private networks if enabled
        if ($include_private && self::is_private_address($ip)) {
            return true;
        }
        return false;
    }

    /**
     * Clear the whitelist cache
     *
     * @since 2.0.0
     */
    public static function clear_cache() {
        self::$cache = array();
        self::$whitelist_entries = null;
    }

    /**
     * Add IP to whitelist
     *
     * @since 2.0.0
     * @param string $ip_range IP or IP range to add
     * @return bool Success
     */
    public static function add_to_whitelist($ip_range) {
        $current = VMPFence_Config::get('whitelisted_ips_bypass', '');
        $entries = self::get_whitelist_entries();
        
        // Check if already exists
        if (in_array(trim($ip_range), $entries)) {
            return true;
        }

        // Add new entry
        $new_value = $current . "\n" . trim($ip_range);
        $result = VMPFence_Config::set('whitelisted_ips_bypass', trim($new_value));

        if ($result) {
            self::clear_cache();
        }

        return $result;
    }

    /**
     * Remove IP from whitelist
     *
     * @since 2.0.0
     * @param string $ip_range IP or IP range to remove
     * @return bool Success
     */
    public static function remove_from_whitelist($ip_range) {
        $entries = self::get_whitelist_entries();
        
        // Remove the entry
        $entries = array_filter($entries, function($entry) use ($ip_range) {
            return trim($entry) !== trim($ip_range);
        });

        // Save updated list
        $new_value = implode("\n", $entries);
        $result = VMPFence_Config::set('whitelisted_ips_bypass', $new_value);

        if ($result) {
            self::clear_cache();
        }

        return $result;
    }

    /**
     * Get all whitelist entries
     *
     * @since 2.0.0
     * @return array
     */
    public static function get_all_entries() {
        return self::get_whitelist_entries();
    }

    /**
     * Validate IP range format
     *
     * @since 2.0.0
     * @param string $range IP range to validate
     * @return bool|string True if valid, error message if invalid
     */
    public static function validate_range($range) {
        $range = trim($range);

        if (empty($range)) {
            return 'IP range cannot be empty';
        }

        // Test if the range can be parsed
        $ip_range = new VMPFence_IP_Range($range);
        
        // Try to validate with a test IP
        // If it's a single IP, it should validate itself
        if (strpos($range, '/') === false && 
            strpos($range, '[') === false && 
            strpos($range, '-') === false) {
            // Single IP - must be valid
            if (filter_var($range, FILTER_VALIDATE_IP) === false) {
                return 'Invalid IP address format';
            }
        }

        return true;
    }
}
