<?php

declare(strict_types=1);

namespace App\Framework\Waf\Layers;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\DateTime\Clock;
use App\Framework\Http\Request;
use App\Framework\RateLimit\RateLimiter;
use App\Framework\Waf\DetectionCategory;
use App\Framework\Waf\DetectionSeverity;
use App\Framework\Waf\LayerResult;
use App\Framework\Waf\LayerStatus;
use App\Framework\Waf\ValueObjects\Detection;
use App\Framework\Waf\ValueObjects\LayerConfig;
use App\Framework\Waf\ValueObjects\LayerMetrics;

/**
 * Intelligent Rate Limiting WAF Layer
 *
 * Integrates advanced WAF threat analysis with the existing RateLimit framework.
 * Uses the enhanced RateLimiter with traffic pattern analysis, burst detection,
 * and baseline deviation tracking for sophisticated DDoS protection.
 */
final class IntelligentRateLimitLayer implements LayerInterface
{
    private LayerConfig $config;

    private LayerMetrics $metrics;

    // Rate limiting tiers for different threat levels
    private const array RATE_LIMITS = [
        'normal' => ['limit' => 100, 'window' => 60],      // 100 req/min
        'elevated' => ['limit' => 50, 'window' => 60],     // 50 req/min
        'high' => ['limit' => 20, 'window' => 60],         // 20 req/min
        'critical' => ['limit' => 5, 'window' => 60],      // 5 req/min
    ];

    public function __construct(
        private readonly RateLimiter $rateLimiter,
        private readonly Clock $clock
    ) {
        $this->config = new LayerConfig(
            enabled: true,
            timeout: Duration::fromMilliseconds(100),
            confidenceThreshold: Percentage::from(85.0),
            blockingMode: true,
            logDetections: true,
            maxDetectionsPerRequest: 5,
            clock: $this->clock
        );

        $this->metrics = LayerMetrics::empty($this->clock);
    }

    public function getName(): string
    {
        return 'intelligent_rate_limit';
    }

    public function analyze(Request $request): LayerResult
    {
        $startTime = microtime(true);
        $detections = [];

        try {
            // Extract request context for intelligent analysis
            $requestContext = $this->buildRequestContext($request);
            $clientIp = $requestContext['client_ip'];
            $rateLimitKey = "ip:{$clientIp}";

            // Determine appropriate rate limit based on request characteristics
            $rateLimitConfig = $this->selectRateLimitTier($requestContext);

            // Perform intelligent rate limit check with threat analysis
            $rateLimitResult = $this->rateLimiter->checkLimitWithAnalysis(
                $rateLimitKey,
                $rateLimitConfig['limit'],
                $rateLimitConfig['window'],
                $requestContext
            );

            // Process results and create detections
            if (! $rateLimitResult->isAllowed()) {
                $detections[] = $this->createRateLimitDetection($rateLimitResult, $requestContext);
            }

            // Check for suspicious patterns even if rate limit not exceeded
            if ($rateLimitResult->isAttackSuspected()) {
                $detections[] = $this->createSuspiciousPatternDetection($rateLimitResult, $requestContext);
            }

            // Check for anomalous traffic patterns
            if ($rateLimitResult->hasAnomalousTraffic()) {
                $detections[] = $this->createAnomalyDetection($rateLimitResult, $requestContext);
            }

            $processingTime = Duration::fromMilliseconds((microtime(true) - $startTime) * 1000);

            // Determine overall result
            if (! empty($detections)) {
                $threatLevel = $rateLimitResult->getThreatLevel();
                $status = $this->shouldBlock($threatLevel) ? LayerStatus::THREAT_DETECTED : LayerStatus::SUSPICIOUS;

                return LayerResult::threat(
                    $this->getName(),
                    $this->buildThreatMessage($rateLimitResult, $threatLevel),
                    $status,
                    $detections,
                    $processingTime
                );
            }

            return LayerResult::clean(
                $this->getName(),
                'Traffic patterns within normal parameters',
                $processingTime
            );

        } catch (\Throwable $e) {
            $processingTime = Duration::fromMilliseconds((microtime(true) - $startTime) * 1000);

            return LayerResult::error(
                $this->getName(),
                'Intelligent rate limit analysis failed: ' . $e->getMessage(),
                $processingTime
            );
        }
    }

    /**
     * Build comprehensive request context for analysis
     */
    private function buildRequestContext(Request $request): array
    {
        return [
            'client_ip' => $request->server->getRemoteAddr(),
            'request_path' => $request->path,
            'user_agent' => $request->headers->getFirst('User-Agent') ?? '',
            'request_method' => $request->method->value,
            'query_params' => $request->queryParams,
            'content_length' => $request->headers->getFirst('Content-Length') ?? '0',
            'timestamp' => time(),
        ];
    }

    /**
     * Select appropriate rate limit tier based on request characteristics
     */
    private function selectRateLimitTier(array $requestContext): array
    {
        $userAgent = strtolower($requestContext['user_agent']);
        $path = $requestContext['request_path'];

        // Critical paths get stricter limits
        if (str_contains($path, '/admin/') || str_contains($path, '/api/')) {
            return self::RATE_LIMITS['elevated'];
        }

        // Known malicious user agents get strict limits
        $maliciousPatterns = ['sqlmap', 'nikto', 'scanner', 'bot', 'crawler'];
        foreach ($maliciousPatterns as $pattern) {
            if (str_contains($userAgent, $pattern)) {
                return self::RATE_LIMITS['critical'];
            }
        }

        // Resource-intensive operations get elevated limits
        if (str_contains($path, '/search') || str_contains($path, '/export')) {
            return self::RATE_LIMITS['elevated'];
        }

        return self::RATE_LIMITS['normal'];
    }

    /**
     * Create detection for rate limit exceeded
     */
    private function createRateLimitDetection($rateLimitResult, array $context): Detection
    {
        $threatLevel = $rateLimitResult->getThreatLevel();
        $severity = match ($threatLevel) {
            'critical' => DetectionSeverity::CRITICAL,
            'high' => DetectionSeverity::HIGH,
            'medium' => DetectionSeverity::MEDIUM,
            default => DetectionSeverity::LOW
        };

        return new Detection(
            category: DetectionCategory::RATE_LIMITING,
            severity: $severity,
            message: sprintf(
                'Rate limit exceeded: %d/%d requests from %s (threat level: %s)',
                $rateLimitResult->getCurrent(),
                $rateLimitResult->getLimit(),
                $context['client_ip'],
                $threatLevel
            ),
            confidence: Percentage::from(95.0),
            location: 'request_rate',
            timestamp: null,
            context: null
        );
    }

    /**
     * Create detection for suspicious attack patterns
     */
    private function createSuspiciousPatternDetection($rateLimitResult, array $context): Detection
    {
        $attackPatterns = implode(', ', $rateLimitResult->attackPatterns);

        return new Detection(
            category: DetectionCategory::SUSPICIOUS_BEHAVIOR,
            severity: DetectionSeverity::HIGH,
            message: sprintf(
                'Suspicious attack patterns detected from %s: %s',
                $context['client_ip'],
                $attackPatterns
            ),
            confidence: Percentage::from(85.0),
            location: 'traffic_analysis',
            timestamp: null,
            context: null
        );
    }

    /**
     * Create detection for traffic anomalies
     */
    private function createAnomalyDetection($rateLimitResult, array $context): Detection
    {
        return new Detection(
            category: DetectionCategory::ANOMALY,
            severity: DetectionSeverity::MEDIUM,
            message: sprintf(
                'Anomalous traffic pattern from %s: %.2f standard deviations from baseline',
                $context['client_ip'],
                $rateLimitResult->baselineDeviation ?? 0.0
            ),
            confidence: Percentage::from(75.0),
            location: 'baseline_analysis',
            timestamp: null,
            context: null
        );
    }

    /**
     * Determine if threat level warrants blocking
     */
    private function shouldBlock(string $threatLevel): bool
    {
        return in_array($threatLevel, ['critical', 'high']);
    }

    /**
     * Build descriptive threat message
     */
    private function buildThreatMessage($rateLimitResult, string $threatLevel): string
    {
        $messages = ['Intelligent rate limiting detected threat'];

        if (! $rateLimitResult->isAllowed()) {
            $messages[] = 'rate limit exceeded';
        }

        if ($rateLimitResult->isAttackSuspected()) {
            $messages[] = 'attack patterns identified';
        }

        if ($rateLimitResult->hasAnomalousTraffic()) {
            $messages[] = 'traffic anomalies detected';
        }

        $messages[] = "threat level: {$threatLevel}";

        return implode(', ', $messages);
    }

    // ===== LayerInterface Implementation =====

    public function isEnabled(): bool
    {
        return $this->config->enabled;
    }

    public function isHealthy(): bool
    {
        return true;
    }

    public function getPriority(): int
    {
        return 200; // High priority for rate limiting
    }

    public function getConfidenceLevel(): Percentage
    {
        return $this->config->getEffectiveConfidenceThreshold();
    }

    public function getTimeoutThreshold(): Duration
    {
        return $this->config->getEffectiveTimeout();
    }

    public function configure(LayerConfig $config): void
    {
        $this->config = $config;
    }

    public function getConfig(): LayerConfig
    {
        return $this->config;
    }

    public function getMetrics(): LayerMetrics
    {
        return $this->metrics;
    }

    public function reset(): void
    {
        $this->metrics = LayerMetrics::empty($this->clock);
    }

    public function warmUp(): void
    {
        // No warmup needed
    }

    public function shutdown(): void
    {
        // No cleanup needed
    }

    public function getDependencies(): array
    {
        return [RateLimiter::class, Clock::class];
    }

    public function supportsParallelProcessing(): bool
    {
        return true;
    }

    public function getVersion(): string
    {
        return '1.0.0';
    }

    public function getSupportedCategories(): array
    {
        return [
            DetectionCategory::RATE_LIMITING,
            DetectionCategory::SUSPICIOUS_BEHAVIOR,
            DetectionCategory::ANOMALY,
        ];
    }
}
