<?php

declare(strict_types=1);

namespace App\Framework\RateLimit;

use App\Framework\Config\SecurityConfig;

/**
 * Rate Limiting Configuration
 *
 * Migrated from Firewall module to RateLimit module.
 * Now integrated with SlidingWindow for improved accuracy.
 */
final readonly class RateLimitConfig
{
    public function __construct(
        public bool $enabled = true,
        public int $requestsPerMinute = 60,
        public int $requestsPerHour = 1000,
        public int $requestsPerDay = 10000,
        public int $burstLimit = 10,
        public float $windowSize = 60.0, // seconds
        public bool $enableBurstDetection = true,
        public float $threatThreshold = 0.8,
        public array $exemptPaths = ['/health', '/ping', '/metrics'],
        public array $trustedIps = ['127.0.0.1', '::1'],
        public bool $logViolations = true,
        public bool $blockOnViolation = true,
    ) {
    }

    /**
     * Create configuration from SecurityConfig
     */
    public static function fromSecurityConfig(SecurityConfig $securityConfig): self
    {
        // Todo: This should use the AppConfig instead of the SecurityConfig
        return match($securityConfig->appKey) {
            'production' => self::production(),
            'development' => self::development(),
            'testing' => self::testing(),
            default => self::development()
        };
    }

    /**
     * Production configuration (strict limits)
     */
    public static function production(): self
    {
        return new self(
            enabled: true,
            requestsPerMinute: 30,
            requestsPerHour: 500,
            requestsPerDay: 5000,
            burstLimit: 5,
            windowSize: 60.0,
            enableBurstDetection: true,
            threatThreshold: 0.7,
            exemptPaths: ['/health'],
            trustedIps: [],
            logViolations: true,
            blockOnViolation: true,
        );
    }

    /**
     * Development configuration (permissive)
     */
    public static function development(): self
    {
        return new self(
            enabled: true,
            requestsPerMinute: 120,
            requestsPerHour: 2000,
            requestsPerDay: 20000,
            burstLimit: 20,
            windowSize: 60.0,
            enableBurstDetection: false,
            threatThreshold: 0.9,
            exemptPaths: ['/health', '/ping', '/metrics', '/debug', '/api/test'],
            trustedIps: ['127.0.0.1', '::1', '192.168.0.0/16', '10.0.0.0/8'],
            logViolations: true,
            blockOnViolation: false, // Log only in development
        );
    }

    /**
     * Testing configuration (disabled)
     */
    public static function testing(): self
    {
        return new self(
            enabled: false,
            requestsPerMinute: 1000,
            requestsPerHour: 10000,
            requestsPerDay: 100000,
            burstLimit: 100,
            windowSize: 1.0,
            enableBurstDetection: false,
            threatThreshold: 1.0,
            exemptPaths: ['*'],
            trustedIps: ['127.0.0.1', '::1'],
            logViolations: false,
            blockOnViolation: false,
        );
    }

    /**
     * Check if path is exempt from rate limiting
     */
    public function isExemptPath(string $path): bool
    {
        foreach ($this->exemptPaths as $exemptPath) {
            if ($exemptPath === '*' || str_starts_with($path, $exemptPath)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if IP is trusted
     */
    public function isTrustedIp(string $ip): bool
    {
        foreach ($this->trustedIps as $trustedIp) {
            if ($this->ipMatches($ip, $trustedIp)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if IP matches pattern (supports CIDR)
     */
    private function ipMatches(string $ip, string $pattern): bool
    {
        // Exact match
        if ($ip === $pattern) {
            return true;
        }

        // CIDR match
        if (str_contains($pattern, '/')) {
            [$network, $maskBits] = explode('/', $pattern, 2);
            $maskBits = (int) $maskBits;

            // IPv4 CIDR
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) &&
                filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
                $ipLong = ip2long($ip);
                $networkLong = ip2long($network);
                $mask = -1 << (32 - $maskBits);

                return ($ipLong & $mask) === ($networkLong & $mask);
            }

            // IPv6 CIDR (simplified)
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) &&
                filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
                // For IPv6, we'd need more complex subnet matching
                // For now, just do exact match for IPv6
                return $ip === $network;
            }
        }

        return false;
    }

    /**
     * Convert to array for serialization
     */
    public function toArray(): array
    {
        return [
            'enabled' => $this->enabled,
            'limits' => [
                'requests_per_minute' => $this->requestsPerMinute,
                'requests_per_hour' => $this->requestsPerHour,
                'requests_per_day' => $this->requestsPerDay,
                'burst_limit' => $this->burstLimit,
            ],
            'window' => [
                'size_seconds' => $this->windowSize,
                'burst_detection' => $this->enableBurstDetection,
                'threat_threshold' => $this->threatThreshold,
            ],
            'security' => [
                'exempt_paths_count' => count($this->exemptPaths),
                'trusted_ips_count' => count($this->trustedIps),
                'log_violations' => $this->logViolations,
                'block_on_violation' => $this->blockOnViolation,
            ],
        ];
    }
}
