<?php
/**
 * Rate Limit Controller
 *
 * Handles rate limit enforcement and violations.
 * Simplified version with only global rate limits.
 *
 * @package VMPFence
 * @since 2.0.0
 */

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

/**
 * VMPFence Rate Limit Controller
 *
 * @since 2.0.0
 */
class VMPFence_Rate_Limit_Controller {

    /**
     * Rate limiter model instance
     *
     * @var VMPFence_Rate_Limiter
     */
    private $rate_limiter;

    /**
     * Constructor
     *
     * @since 2.0.0
     */
    public function __construct() {
        $this->rate_limiter = new VMPFence_Rate_Limiter();
    }

    /**
     * Check rate limits for current request
     *
     * @since 2.0.0
     * @param array $request Request data containing IP, user_agent, etc.
     * @return array|false Violation data if limit exceeded, false otherwise
     */
    public function check_rate_limits($request) {
        if (!isset($request['ip']) || empty($request['ip'])) {
            return false;
        }

        $ip = $request['ip'];
        $user_agent = isset($request['user_agent']) ? $request['user_agent'] : '';

        // Check if rate limiting is enabled
        if (!VMPFence_Config::get('rate_limiting_enabled')) {
            return false;
        }

        // Handle Google crawler special treatment
        $google_handling = VMPFence_Visitor_Classifier::handle_google_crawler($user_agent, $ip);
        if ($google_handling === 'bypass') {
            return false; // Bypass all rate limits
        }

        // Classify visitor (human vs crawler)
        $visitor_type = ($google_handling === 'crawler') 
            ? 'crawler' 
            : VMPFence_Visitor_Classifier::classify_visitor($user_agent, $ip);

        // Determine hit type based on HTTP status code
        $hitType = 'hit';
        if (isset($request['status_code']) && $request['status_code'] == 404) {
            $hitType = '404';
        }

        // Check for rate limit violations (pass visitor_type for proper limit checking)
        $violation = $this->rate_limiter->is_rate_limited($ip, $hitType, $visitor_type);
        
        if ($violation) {
            // Check if this crawler 404 URL should be allowlisted from THROTTLE rules only
            // "These URL patterns will be excluded from the throttling rules used to limit crawlers"
            if ($violation['action'] === 'throttle' && 
                $visitor_type === 'crawler' && 
                $hitType === '404' &&
                $violation['type'] === 'crawler 404') {
                $allowlisted_urls = VMPFence_Config::get('allowlisted_404_urls', array());
                $request_uri = isset($request['uri']) ? $request['uri'] : '';
                
                if (!empty($allowlisted_urls) && !empty($request_uri)) {
                    foreach ($allowlisted_urls as $pattern) {
                        // Convert wildcard pattern to regex for partial matching
                        // Escape special regex chars, then convert * to .* for wildcard matching
                        $escaped_pattern = str_replace(['\\*', '\\?'], ['.*', '.'], preg_quote($pattern, '/'));
                        // Match if pattern appears anywhere in the URL (not just exact match)
                        $regex = '/' . $escaped_pattern . '/i';
                        
                        if (preg_match($regex, $request_uri)) {
                            return false; // Skip throttle for allowlisted crawler 404s
                        }
                    }
                }
            }
            
            // Log the violation
            $this->log_violation($ip, $violation, $request);

            // Handle the violation based on action
            if ($violation['action'] === 'block') {
                // Block the IP for future requests (NOT affected by allowlist)
                $reason = isset($violation['message']) ? $violation['message'] : 'Rate limit exceeded';
                $this->rate_limiter->block_ip($ip, $reason);
                // Return violation for immediate blocking
                return $violation;
            } elseif ($violation['action'] === 'throttle') {
                // Apply throttling delay
                $this->handle_throttle($violation);
                // Return violation data for logging but don't block
                return $violation;
            }
        }

        return false;
    }

    /**
     * Handle throttle action by adding delay
     *
     * @since 2.0.0
     * @param array $violation Violation data
     * @return void
     */
    private function handle_throttle($violation) {
        $throttle_behavior = VMPFence_Config::get('throttle_behavior', 'show_503');
        
        if ($throttle_behavior === 'show_503') {
            // Show 503 Service Unavailable page
            $this->display_503_page($violation);
        } else {
            // Default: slow_down - Add delay to slow down the request
            $delay_seconds = VMPFence_Config::get('throttle_delay_duration', 5);
            $delay_seconds = max(1, min(100, intval($delay_seconds))); // Ensure value is between 1-100
            
            if ($delay_seconds > 0) {
                // Add delay to slow down the request
                sleep($delay_seconds);
            }
        }
    }

    /**
     * Display 503 Service Unavailable page for throttling
     *
     * @since 2.0.0
     * @param array $violation Violation data
     * @return void
     */
    private function display_503_page($violation) {
        // Load the 503 throttle page view
        include VMPFENCE_PLUGIN_DIR . '/firewall/views/rate-limit-503-throttle.php';
        exit;
    }

    /**
     * Display rate limit block page
     *
     * @since 2.0.0
     * @param array $violation Violation data
     * @return void
     */
    public function display_block_page($violation) {
        // Load the rate limit block page view
        include VMPFENCE_PLUGIN_DIR . '/firewall/views/rate-limit-403-blocked.php';
        exit;
    }

    /**
     * Log rate limit violation
     *
     * @since 2.0.0
     * @param string $ip The IP address
     * @param array $violation Violation data
     * @param array $request Request data
     * @return void
     */
    private function log_violation($ip, $violation, $request) {
        $user_agent = isset($request['user_agent']) ? $request['user_agent'] : '';
        $uri = isset($request['uri']) ? $request['uri'] : '';

        // Log to WordPress error log
        // error_log(sprintf(
        //     'VMPFence Rate Limit Violation - IP: %s, Type: %s, Count: %d, Limit: %d, Action: %s, URI: %s, UA: %s',
        //     $ip,
        //     $violation['type'],
        //     $violation['count'],
        //     $violation['limit'],
        //     $violation['action'],
        //     $uri,
        //     $user_agent
        // ));

        // Log to hits table using live traffic logger
        if (class_exists('VMPFence_Live_Traffic_Logger')) {
            $logger = VMPFence_Live_Traffic_Logger::getInstance();
            $logger->setAction(
                'rate_limit_' . $violation['action'],
                $violation['message'],
                $violation
            );
        }
    }

    /**
     * Get rate limiting statistics for admin
     *
     * @since 2.0.0
     * @return array Statistics
     */
    public function get_stats() {
        return $this->rate_limiter->get_stats();
    }

    /**
     * Get list of blocked IPs
     *
     * @since 2.0.0
     * @return array Blocked IPs
     */
    public function get_blocked_ips() {
        return $this->rate_limiter->get_blocked_ips();
    }

    /**
     * Unblock an IP address
     *
     * @since 2.0.0
     * @param string $ip IP address to unblock
     * @return bool True on success, false on failure
     */
    public function unblock_ip($ip) {
        if (empty($ip)) {
            return false;
        }

        $result = $this->rate_limiter->unblock_ip($ip);
        
        return $result;
    }
}