<?php

declare(strict_types=1);

namespace App\Framework\Waf\Rules;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Waf\Rules\ValueObjects\RuleMatch;

/**
 * Result of rule engine evaluation
 */
final readonly class RuleEvaluationResult
{
    public function __construct(
        public array $matches,
        public int $evaluatedRules,
        public int $skippedRules,
        public int $errorCount,
        public array $errors,
        public Duration $evaluationTime,
        public Timestamp $timestamp
    ) {
    }

    /**
     * Check if any rules matched
     */
    public function hasMatches(): bool
    {
        return ! empty($this->matches);
    }

    /**
     * Get number of matches
     */
    public function getMatchCount(): int
    {
        return count($this->matches);
    }

    /**
     * Get matches by severity
     */
    public function getMatchesBySeverity(string $severity): array
    {
        return array_filter(
            $this->matches,
            fn (RuleMatch $match) => $match->severity->value === $severity
        );
    }

    /**
     * Get critical matches
     */
    public function getCriticalMatches(): array
    {
        return $this->getMatchesBySeverity('critical');
    }

    /**
     * Get high severity matches
     */
    public function getHighSeverityMatches(): array
    {
        return $this->getMatchesBySeverity('high');
    }

    /**
     * Get blocking matches (matches that should block the request)
     */
    public function getBlockingMatches(): array
    {
        return array_filter(
            $this->matches,
            fn (RuleMatch $match) => $match->shouldBlock()
        );
    }

    /**
     * Get matches that should trigger alerts
     */
    public function getAlertingMatches(): array
    {
        return array_filter(
            $this->matches,
            fn (RuleMatch $match) => $match->shouldAlert()
        );
    }

    /**
     * Get OWASP Top 10 matches
     */
    public function getOwaspTop10Matches(): array
    {
        return array_filter(
            $this->matches,
            fn (RuleMatch $match) => $match->category->isOwaspTop10()
        );
    }

    /**
     * Get matches by category
     */
    public function getMatchesByCategory(string $category): array
    {
        return array_filter(
            $this->matches,
            fn (RuleMatch $match) => $match->category->value === $category
        );
    }

    /**
     * Check if any blocking matches exist
     */
    public function shouldBlock(): bool
    {
        return ! empty($this->getBlockingMatches());
    }

    /**
     * Check if any alerting matches exist
     */
    public function shouldAlert(): bool
    {
        return ! empty($this->getAlertingMatches());
    }

    /**
     * Check if evaluation had errors
     */
    public function hasErrors(): bool
    {
        return $this->errorCount > 0;
    }

    /**
     * Get highest severity level from matches
     */
    public function getHighestSeverity(): ?string
    {
        if (empty($this->matches)) {
            return null;
        }

        $severityOrder = ['critical', 'high', 'medium', 'low', 'info'];

        foreach ($severityOrder as $severity) {
            if (! empty($this->getMatchesBySeverity($severity))) {
                return $severity;
            }
        }

        return null;
    }

    /**
     * Get categories of all matches
     */
    public function getMatchedCategories(): array
    {
        $categories = [];

        foreach ($this->matches as $match) {
            $categories[$match->category->value] = $match->category;
        }

        return array_values($categories);
    }

    /**
     * Get rule IDs of all matches
     */
    public function getMatchedRuleIds(): array
    {
        return array_map(
            fn (RuleMatch $match) => $match->ruleId->value,
            $this->matches
        );
    }

    /**
     * Calculate overall confidence score
     */
    public function getOverallConfidence(): Percentage
    {
        if (empty($this->matches)) {
            return Percentage::from(0.0);
        }

        $totalConfidence = 0.0;
        $confidenceCount = 0;

        foreach ($this->matches as $match) {
            if ($match->confidence !== null) {
                $totalConfidence += $match->confidence->getValue();
                $confidenceCount++;
            }
        }

        if ($confidenceCount === 0) {
            return Percentage::from(0.0);
        }

        return Percentage::from($totalConfidence / $confidenceCount);
    }

    /**
     * Calculate threat score based on matches
     */
    public function getThreatScore(): Percentage
    {
        if (empty($this->matches)) {
            return Percentage::from(0.0);
        }

        $score = 0.0;
        $maxScore = 0.0;

        foreach ($this->matches as $match) {
            $matchScore = match ($match->severity->value) {
                'critical' => 25.0,
                'high' => 20.0,
                'medium' => 10.0,
                'low' => 5.0,
                'info' => 1.0,
                default => 0.0
            };

            // Apply confidence modifier
            if ($match->confidence !== null) {
                $matchScore *= ($match->confidence->getValue() / 100.0);
            }

            $score += $matchScore;
            $maxScore = max($maxScore, $matchScore);
        }

        // Use weighted combination: 70% cumulative + 30% maximum
        $finalScore = ($score * 0.7) + ($maxScore * 0.3);

        return Percentage::from(min(100.0, $finalScore));
    }

    /**
     * Get evaluation performance summary
     */
    public function getPerformanceSummary(): array
    {
        $efficiency = 0.0;
        if ($this->evaluatedRules > 0) {
            $rulesPerMs = $this->evaluatedRules / max(1, $this->evaluationTime->toMilliseconds());
            $efficiency = $rulesPerMs * 1000; // Rules per second
        }

        return [
            'evaluation_time_ms' => $this->evaluationTime->toMilliseconds(),
            'evaluated_rules' => $this->evaluatedRules,
            'skipped_rules' => $this->skippedRules,
            'error_count' => $this->errorCount,
            'rules_per_second' => round($efficiency, 2),
            'error_rate' => $this->evaluatedRules > 0
                ? round(($this->errorCount / $this->evaluatedRules) * 100, 2)
                : 0.0,
        ];
    }

    /**
     * Convert to array for serialization
     */
    public function toArray(): array
    {
        return [
            'timestamp' => $this->timestamp->toIsoString(),
            'evaluation_time_ms' => $this->evaluationTime->toMilliseconds(),
            'evaluated_rules' => $this->evaluatedRules,
            'skipped_rules' => $this->skippedRules,
            'error_count' => $this->errorCount,
            'match_count' => $this->getMatchCount(),
            'has_matches' => $this->hasMatches(),
            'should_block' => $this->shouldBlock(),
            'should_alert' => $this->shouldAlert(),
            'highest_severity' => $this->getHighestSeverity(),
            'threat_score' => $this->getThreatScore()->getValue(),
            'overall_confidence' => $this->getOverallConfidence()->getValue(),
            'matched_categories' => array_map(fn ($cat) => $cat->value, $this->getMatchedCategories()),
            'matched_rule_ids' => $this->getMatchedRuleIds(),
            'owasp_top10_matches' => count($this->getOwaspTop10Matches()),
            'critical_matches' => count($this->getCriticalMatches()),
            'high_severity_matches' => count($this->getHighSeverityMatches()),
            'blocking_matches' => count($this->getBlockingMatches()),
            'alerting_matches' => count($this->getAlertingMatches()),
            'performance_summary' => $this->getPerformanceSummary(),
            'matches' => array_map(fn (RuleMatch $match) => $match->toArray(), $this->matches),
            'errors' => $this->errors,
        ];
    }
}
