<?php

declare(strict_types=1);

namespace App\Framework\Waf\Layers;

use App\Framework\Waf\Layers\LayerInterface;
use App\Framework\Waf\LayerResult;
use App\Framework\Waf\ValueObjects\LayerConfig;
use App\Framework\Waf\ValueObjects\LayerMetrics;
use App\Framework\Waf\ValueObjects\Detection;
use App\Framework\Waf\ValueObjects\DetectionCategory;
use App\Framework\Waf\ValueObjects\DetectionSeverity;
use App\Framework\Waf\ValueObjects\DetectionStatus;
use App\Framework\Waf\MachineLearning\BehaviorPatternExtractor;
use App\Framework\Waf\MachineLearning\BehaviorAnomalyDetector;
use App\Framework\Waf\MachineLearning\RequestHistoryTracker;
use App\Framework\Http\Request;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Core\ValueObjects\Score;
use Psr\Log\LoggerInterface;

/**
 * ML-Enhanced WAF Layer for Advanced Behavioral Analysis
 *
 * Provides sophisticated threat detection through:
 * - Behavioral pattern extraction (8 features)
 * - Statistical anomaly detection
 * - Heuristic-based threat classification
 * - Request sequence analysis
 *
 * Architecture:
 * RequestHistoryTracker → BehaviorPatternExtractor → BehaviorAnomalyDetector → LayerResult
 */
final class MLEnhancedWafLayer implements LayerInterface
{
    private const string LAYER_NAME = 'ML-Enhanced Behavioral Analysis';
    private const string VERSION = '1.0.0';
    private const int PRIORITY = 100; // High priority for ML analysis

    private LayerConfig $config;
    private LayerMetrics $metrics;
    private bool $isHealthy = true;
    private int $detectionCount = 0;
    private int $requestCount = 0;

    /**
     * @param Score $confidenceThreshold Minimum confidence for threat classification
     * @param int $minHistorySize Minimum request history for analysis
     * @param bool $enableStatisticalDetection Use statistical baseline comparison
     */
    public function __construct(
        private readonly RequestHistoryTracker $historyTracker,
        private readonly BehaviorPatternExtractor $patternExtractor,
        private readonly BehaviorAnomalyDetector $anomalyDetector,
        private readonly LoggerInterface $logger,
        private readonly Score $confidenceThreshold = new Score(0.6),
        private readonly int $minHistorySize = 5,
        private readonly bool $enableStatisticalDetection = true
    ) {
        $this->config = LayerConfig::default()
            ->withTimeout(Duration::fromMilliseconds(100))
            ->withConfidenceThreshold(Percentage::from(60.0));

        $this->metrics = new LayerMetrics(
            totalRequests: 0,
            totalDetections: 0,
            totalBlocks: 0,
            averageProcessingTime: Duration::fromMilliseconds(0),
            falsePositiveRate: Percentage::from(0.0),
            detectionRate: Percentage::from(0.0)
        );
    }

    public function getName(): string
    {
        return self::LAYER_NAME;
    }

    public function analyze(Request $request): LayerResult
    {
        $startTime = microtime(true);
        $this->requestCount++;

        try {
            // 1. Track current request for sequence building
            $this->historyTracker->track($request);

            // 2. Get request sequence for client IP
            $clientIp = $request->server->getRemoteAddr();
            $sequence = $this->historyTracker->getSequence($clientIp);

            // 3. Check if we have enough history for analysis
            if (!$this->historyTracker->hasSufficientHistory($clientIp, $this->minHistorySize)) {
                $processingTime = Duration::fromMilliseconds((microtime(true) - $startTime) * 1000);
                return LayerResult::clean(
                    $this->getName(),
                    'Insufficient request history for behavioral analysis',
                    $processingTime
                );
            }

            // 4. Extract behavioral features
            $features = $this->patternExtractor->extract($sequence);

            // 5. Detect anomalies
            $anomalyResult = $this->anomalyDetector->detect(
                $features,
                [] // TODO: Implement baseline storage for statistical detection
            );

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

            // 6. Evaluate threat level
            if (!$anomalyResult->isAnomalous) {
                return LayerResult::clean(
                    $this->getName(),
                    $anomalyResult->primaryIndicator,
                    $processingTime
                );
            }

            // 7. Check if anomaly score exceeds threshold
            if ($anomalyResult->anomalyScore->isBelow($this->confidenceThreshold)) {
                return LayerResult::clean(
                    $this->getName(),
                    "Low confidence anomaly: {$anomalyResult->primaryIndicator}",
                    $processingTime,
                    metadata: [
                        'anomaly_score' => $anomalyResult->anomalyScore->value(),
                        'severity' => $anomalyResult->getSeverity(),
                        'below_threshold' => true
                    ]
                );
            }

            // 8. Build detections from anomaly result
            $detections = $this->buildDetections($anomalyResult, $sequence);

            $this->detectionCount += count($detections);

            // 9. Log threat detection
            if ($this->config->logDetections) {
                $this->logger->warning('ML WAF: Behavioral anomaly detected', [
                    'layer' => $this->getName(),
                    'client_ip' => $clientIp->toString(),
                    'anomaly_score' => $anomalyResult->anomalyScore->value(),
                    'severity' => $anomalyResult->getSeverity(),
                    'primary_indicator' => $anomalyResult->primaryIndicator,
                    'detected_patterns' => $anomalyResult->detectedPatterns,
                    'sequence_stats' => $sequence->getStatistics()
                ]);
            }

            return LayerResult::threat(
                $this->getName(),
                $anomalyResult->primaryIndicator,
                $this->mapScoreToStatus($anomalyResult->anomalyScore),
                $detections,
                $processingTime,
                metadata: [
                    'anomaly_score' => $anomalyResult->anomalyScore->value(),
                    'severity' => $anomalyResult->getSeverity(),
                    'recommended_action' => $anomalyResult->getRecommendedAction(),
                    'top_contributors' => $anomalyResult->getTopContributors()
                ]
            );

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

            $this->logger->error('ML WAF Layer analysis failed', [
                'layer' => $this->getName(),
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            $this->isHealthy = false;

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

    /**
     * Build Detection objects from anomaly result
     */
    private function buildDetections(
        \App\Framework\Waf\MachineLearning\ValueObjects\BehaviorAnomalyResult $anomalyResult,
        \App\Framework\Waf\MachineLearning\ValueObjects\RequestSequence $sequence
    ): array {
        $detections = [];

        foreach ($anomalyResult->detectedPatterns as $pattern) {
            $patternType = $pattern['type'] ?? 'unknown';

            $category = $this->mapPatternToCategory($patternType);
            $severity = $this->mapScoreToSeverity($anomalyResult->anomalyScore);

            $detections[] = new Detection(
                category: $category,
                severity: $severity,
                pattern: $patternType,
                value: json_encode($pattern),
                description: $this->buildPatternDescription($pattern),
                confidence: Percentage::fromDecimal($anomalyResult->anomalyScore->value()),
                location: 'behavioral_analysis',
                status: DetectionStatus::CONFIRMED
            );
        }

        // Add feature-based detections for top contributors
        foreach ($anomalyResult->getTopContributors(3) as $featureName => $score) {
            $detections[] = new Detection(
                category: DetectionCategory::BEHAVIORAL_ANOMALY,
                severity: $this->mapScoreToSeverity($score),
                pattern: "anomalous_{$featureName}",
                value: (string) $score->value(),
                description: "Anomalous {$featureName} pattern detected",
                confidence: Percentage::fromDecimal($score->value()),
                location: 'feature_analysis',
                status: DetectionStatus::CONFIRMED
            );
        }

        return $detections;
    }

    /**
     * Map pattern type to detection category
     */
    private function mapPatternToCategory(string $patternType): DetectionCategory
    {
        return match ($patternType) {
            'potential_ddos' => DetectionCategory::DDOS_ATTACK,
            'potential_scanning' => DetectionCategory::SECURITY_SCANNING,
            'potential_bot' => DetectionCategory::BOT_ACTIVITY,
            'potential_credential_stuffing' => DetectionCategory::AUTHENTICATION_ABUSE,
            'statistical_outlier', 'iqr_outlier' => DetectionCategory::BEHAVIORAL_ANOMALY,
            default => DetectionCategory::BEHAVIORAL_ANOMALY
        };
    }

    /**
     * Map Score to DetectionSeverity
     */
    private function mapScoreToSeverity(Score $score): DetectionSeverity
    {
        return match (true) {
            $score->isCritical() => DetectionSeverity::CRITICAL,
            $score->isHigh() => DetectionSeverity::HIGH,
            $score->isMedium() => DetectionSeverity::MEDIUM,
            default => DetectionSeverity::LOW
        };
    }

    /**
     * Map Score to DetectionStatus
     */
    private function mapScoreToStatus(Score $score): DetectionStatus
    {
        return match (true) {
            $score->isCritical(), $score->isHigh() => DetectionStatus::CONFIRMED,
            $score->isMedium() => DetectionStatus::SUSPECTED,
            default => DetectionStatus::POSSIBLE
        };
    }

    /**
     * Build human-readable pattern description
     */
    private function buildPatternDescription(array $pattern): string
    {
        $type = $pattern['type'] ?? 'unknown';

        return match ($type) {
            'potential_ddos' => sprintf(
                'High request frequency (%.2f req/s) with low endpoint diversity (%.2f)',
                $pattern['frequency'] ?? 0.0,
                $pattern['diversity'] ?? 0.0
            ),
            'potential_scanning' => sprintf(
                'High parameter entropy (%.2f) combined with geographic anomaly (%.2f)',
                $pattern['entropy'] ?? 0.0,
                $pattern['geo_anomaly'] ?? 0.0
            ),
            'potential_bot' => sprintf(
                'Perfect timing regularity (%.2f) with high payload similarity (%.2f)',
                $pattern['regularity'] ?? 0.0,
                $pattern['similarity'] ?? 0.0
            ),
            'potential_credential_stuffing' => sprintf(
                'High frequency (%.2f req/s) with inconsistent User-Agent (%.2f)',
                $pattern['frequency'] ?? 0.0,
                $pattern['ua_consistency'] ?? 0.0
            ),
            'statistical_outlier' => sprintf(
                'Feature %s is a statistical outlier (z-score: %.2f)',
                $pattern['feature'] ?? 'unknown',
                $pattern['z_score'] ?? 0.0
            ),
            default => 'Behavioral anomaly detected'
        };
    }

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

    public function isHealthy(): bool
    {
        return $this->isHealthy;
    }

    public function getPriority(): int
    {
        return self::PRIORITY;
    }

    public function getConfidenceLevel(): Percentage
    {
        // Average confidence based on detection rate
        if ($this->requestCount === 0) {
            return Percentage::from(100.0);
        }

        $detectionRate = ($this->detectionCount / $this->requestCount) * 100;
        $confidence = 100.0 - min($detectionRate, 50.0); // Lower confidence if too many detections

        return Percentage::from($confidence);
    }

    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->detectionCount = 0;
        $this->requestCount = 0;
        $this->isHealthy = true;
        $this->metrics = new LayerMetrics(
            totalRequests: 0,
            totalDetections: 0,
            totalBlocks: 0,
            averageProcessingTime: Duration::fromMilliseconds(0),
            falsePositiveRate: Percentage::from(0.0),
            detectionRate: Percentage::from(0.0)
        );
    }

    public function warmUp(): void
    {
        $this->logger->info('ML WAF Layer warming up', [
            'layer' => $this->getName(),
            'confidence_threshold' => $this->confidenceThreshold->value(),
            'min_history_size' => $this->minHistorySize
        ]);
    }

    public function shutdown(): void
    {
        $this->logger->info('ML WAF Layer shutting down', [
            'layer' => $this->getName(),
            'total_requests' => $this->requestCount,
            'total_detections' => $this->detectionCount
        ]);
    }

    public function getDependencies(): array
    {
        return [
            RequestHistoryTracker::class,
            BehaviorPatternExtractor::class,
            BehaviorAnomalyDetector::class
        ];
    }

    public function supportsParallelProcessing(): bool
    {
        return true; // Can run in parallel with other layers
    }

    public function getVersion(): string
    {
        return self::VERSION;
    }

    public function getSupportedCategories(): array
    {
        return [
            DetectionCategory::BEHAVIORAL_ANOMALY,
            DetectionCategory::DDOS_ATTACK,
            DetectionCategory::SECURITY_SCANNING,
            DetectionCategory::BOT_ACTIVITY,
            DetectionCategory::AUTHENTICATION_ABUSE
        ];
    }
}
