<?php

declare(strict_types=1);

namespace App\Framework\Queue\MachineLearning\ValueObjects;

use App\Framework\Core\ValueObjects\Score;

/**
 * Job Anomaly Detection Result - ML-Based Job Behavior Analysis Result
 *
 * Immutable value object containing job anomaly detection results
 * using framework's Core Score for confidence scoring.
 *
 * Features:
 * - Core Score integration for anomaly confidence (0.0-1.0)
 * - Pattern-based anomaly classification
 * - Feature contribution analysis
 * - Severity assessment and recommended actions
 * - Detailed metadata for investigation
 *
 * Used by: JobAnomalyDetector for unified anomaly detection results
 */
final readonly class JobAnomalyResult
{
    /**
     * @param Score $anomalyScore Framework Core Score (0.0-1.0) representing anomaly confidence
     * @param bool $isAnomalous Whether the job execution is classified as anomalous
     * @param array<string, Score> $featureScores Individual feature contribution scores
     * @param array<array{type: string, confidence: Score, description: string}> $detectedPatterns Detected anomaly patterns
     * @param string $primaryIndicator Primary feature indicating anomaly
     * @param array<string, mixed> $metadata Additional context and debugging information
     */
    public function __construct(
        public Score $anomalyScore,
        public bool $isAnomalous,
        public array $featureScores,
        public array $detectedPatterns,
        public string $primaryIndicator,
        public array $metadata = []
    ) {}

    /**
     * Create result for normal (non-anomalous) job execution
     */
    public static function normal(string $reason): self
    {
        return new self(
            anomalyScore: Score::zero(),
            isAnomalous: false,
            featureScores: [],
            detectedPatterns: [],
            primaryIndicator: 'none',
            metadata: ['reason' => $reason]
        );
    }

    /**
     * Create result for low confidence detection (inconclusive)
     */
    public static function lowConfidence(
        Score $score,
        array $featureScores,
        string $reason = 'Insufficient data or low confidence'
    ): self {
        return new self(
            anomalyScore: $score,
            isAnomalous: false,
            featureScores: $featureScores,
            detectedPatterns: [],
            primaryIndicator: 'unknown',
            metadata: [
                'reason' => $reason,
                'confidence_level' => 'low'
            ]
        );
    }

    /**
     * Create result for detected anomaly
     *
     * @param Score $score Overall anomaly confidence score
     * @param array<string, Score> $featureScores Individual feature scores
     * @param array<array{type: string, confidence: Score, description: string}> $detectedPatterns Detected patterns
     * @param string $primaryIndicator Primary anomaly indicator feature
     */
    public static function anomalous(
        Score $score,
        array $featureScores,
        array $detectedPatterns,
        string $primaryIndicator
    ): self {
        return new self(
            anomalyScore: $score,
            isAnomalous: true,
            featureScores: $featureScores,
            detectedPatterns: $detectedPatterns,
            primaryIndicator: $primaryIndicator,
            metadata: [
                'detection_method' => 'statistical_and_heuristic',
                'pattern_count' => count($detectedPatterns)
            ]
        );
    }

    /**
     * Get severity level based on anomaly score
     *
     * - critical: Score >= 0.8 (immediate action required)
     * - high: Score >= 0.6 (investigate soon)
     * - medium: Score >= 0.4 (monitor closely)
     * - low: Score < 0.4 (awareness)
     */
    public function getSeverity(): string
    {
        if (!$this->isAnomalous) {
            return 'none';
        }

        return match (true) {
            $this->anomalyScore->value() >= 80 => 'critical',
            $this->anomalyScore->value() >= 60 => 'high',
            $this->anomalyScore->value() >= 40 => 'medium',
            default => 'low'
        };
    }

    /**
     * Get recommended action based on severity and patterns
     */
    public function getRecommendedAction(): string
    {
        if (!$this->isAnomalous) {
            return 'No action required';
        }

        $severity = $this->getSeverity();

        // Pattern-specific recommendations
        foreach ($this->detectedPatterns as $pattern) {
            $type = $pattern['type'];

            if ($type === 'high_failure_risk') {
                return match ($severity) {
                    'critical' => 'Immediate investigation required - High failure rate with excessive retries',
                    'high' => 'Investigate job logic and error handling',
                    default => 'Monitor failure patterns and retry frequency'
                };
            }

            if ($type === 'performance_degradation') {
                return match ($severity) {
                    'critical' => 'Critical performance issue - Check for resource bottlenecks',
                    'high' => 'Investigate execution time variance and memory patterns',
                    default => 'Monitor performance metrics'
                };
            }

            if ($type === 'resource_exhaustion') {
                return match ($severity) {
                    'critical' => 'Resource exhaustion detected - Scale infrastructure or optimize jobs',
                    'high' => 'Review queue depth impact and memory usage',
                    default => 'Monitor resource utilization trends'
                };
            }

            if ($type === 'automated_execution') {
                return match ($severity) {
                    'critical' => 'Potential bot activity - Verify job submission source',
                    'high' => 'Investigate automated job submission patterns',
                    default => 'Monitor timing regularity'
                };
            }

            if ($type === 'data_processing_anomaly') {
                return match ($severity) {
                    'critical' => 'Data anomaly detected - Verify payload integrity and processing logic',
                    'high' => 'Investigate payload size variations and memory patterns',
                    default => 'Monitor data processing patterns'
                };
            }
        }

        // Generic recommendations by severity
        return match ($severity) {
            'critical' => 'Immediate investigation required',
            'high' => 'Investigate anomaly patterns',
            'medium' => 'Monitor closely',
            default => 'Review and monitor'
        };
    }

    /**
     * Get top contributing features to the anomaly
     *
     * @param int $limit Maximum number of contributors to return
     * @return array<array{feature: string, score: Score, contribution_percentage: float}>
     */
    public function getTopContributors(int $limit = 3): array
    {
        if (empty($this->featureScores)) {
            return [];
        }

        // Calculate total score for percentage calculation
        $totalScore = array_reduce(
            $this->featureScores,
            fn(float $carry, Score $score) => $carry + $score->value(),
            0.0
        );

        if ($totalScore === 0.0) {
            return [];
        }

        // Sort features by score descending
        $sorted = $this->featureScores;
        uasort($sorted, fn(Score $a, Score $b) => $b->value() <=> $a->value());

        // Take top N and calculate contribution percentages
        $contributors = [];
        $count = 0;

        foreach ($sorted as $feature => $score) {
            if ($count >= $limit) {
                break;
            }

            $contributors[] = [
                'feature' => $feature,
                'score' => $score,
                'contribution_percentage' => ($score->value() / $totalScore) * 100.0
            ];

            $count++;
        }

        return $contributors;
    }

    /**
     * Check if result has specific pattern type
     */
    public function hasPattern(string $patternType): bool
    {
        foreach ($this->detectedPatterns as $pattern) {
            if ($pattern['type'] === $patternType) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get pattern by type
     *
     * @return array{type: string, confidence: Score, description: string}|null
     */
    public function getPattern(string $patternType): ?array
    {
        foreach ($this->detectedPatterns as $pattern) {
            if ($pattern['type'] === $patternType) {
                return $pattern;
            }
        }

        return null;
    }

    /**
     * Get all pattern types
     *
     * @return array<string>
     */
    public function getPatternTypes(): array
    {
        return array_map(
            fn(array $pattern) => $pattern['type'],
            $this->detectedPatterns
        );
    }

    /**
     * Check if anomaly requires immediate attention
     */
    public function requiresImmediateAttention(): bool
    {
        return $this->isAnomalous && $this->getSeverity() === 'critical';
    }

    /**
     * Get confidence level as string
     */
    public function getConfidenceLevel(): string
    {
        $value = $this->anomalyScore->value();

        return match (true) {
            $value >= 80 => 'very_high',
            $value >= 60 => 'high',
            $value >= 40 => 'medium',
            $value >= 20 => 'low',
            default => 'very_low'
        };
    }

    /**
     * Convert to array for serialization
     *
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return [
            'anomaly_score' => $this->anomalyScore->value(),
            'is_anomalous' => $this->isAnomalous,
            'severity' => $this->getSeverity(),
            'confidence_level' => $this->getConfidenceLevel(),
            'primary_indicator' => $this->primaryIndicator,
            'detected_patterns' => array_map(
                fn(array $pattern) => [
                    'type' => $pattern['type'],
                    'confidence' => $pattern['confidence']->value(),
                    'description' => $pattern['description']
                ],
                $this->detectedPatterns
            ),
            'feature_scores' => array_map(
                fn(Score $score) => $score->value(),
                $this->featureScores
            ),
            'top_contributors' => $this->getTopContributors(3),
            'recommended_action' => $this->getRecommendedAction(),
            'requires_immediate_attention' => $this->requiresImmediateAttention(),
            'metadata' => $this->metadata
        ];
    }

    /**
     * Create debugging representation
     */
    public function __toString(): string
    {
        if (!$this->isAnomalous) {
            return 'JobAnomalyResult[Normal]';
        }

        $score = $this->anomalyScore->value();
        $severity = $this->getSeverity();
        $patterns = implode(', ', $this->getPatternTypes());

        return sprintf(
            'JobAnomalyResult[Anomalous, Score=%.2f, Severity=%s, Patterns=%s]',
            $score,
            $severity,
            $patterns ?: 'none'
        );
    }
}
