<?php

declare(strict_types=1);

namespace App\Framework\Queue\MachineLearning;

use App\Framework\Queue\MachineLearning\ValueObjects\JobAnomalyResult;
use App\Framework\Queue\Services\JobMetricsManager;
use App\Framework\Queue\ValueObjects\JobMetadata;
use App\Framework\Queue\ValueObjects\JobMetrics;
use App\Framework\Logging\Logger;
use App\Framework\Events\EventDispatcher;

/**
 * Queue Anomaly Monitor
 *
 * Monitors queue job execution for anomalies using ML-based detection.
 *
 * Features:
 * - Real-time anomaly detection on job completion
 * - Automatic alerting for critical anomalies
 * - Anomaly result persistence
 * - Event dispatching for anomaly notifications
 */
final readonly class QueueAnomalyMonitor
{
    public function __construct(
        private JobAnomalyDetector $anomalyDetector,
        private QueueJobFeatureExtractor $featureExtractor,
        private JobMetricsManager $metricsManager,
        private Logger $logger,
        private ?EventDispatcher $eventDispatcher = null
    ) {}

    /**
     * Analyze job for anomalies after execution
     *
     * @param JobMetrics $metrics Job execution metrics
     * @param JobMetadata $metadata Job metadata
     * @param int $queueDepth Current queue size
     * @return JobAnomalyResult Detection result
     */
    public function analyzeJobExecution(
        JobMetrics $metrics,
        JobMetadata $metadata,
        int $queueDepth = 0
    ): JobAnomalyResult {
        // Extract features from job metrics
        $features = $this->featureExtractor->extractFeatures($metrics, $metadata, $queueDepth);

        // Detect anomalies
        $result = $this->anomalyDetector->detect($features);

        // Log anomaly if detected
        if ($result->isAnomalous) {
            $this->logAnomaly($metrics, $metadata, $result);
        }

        // Alert on critical anomalies
        if ($result->requiresImmediateAttention()) {
            $this->alertCriticalAnomaly($metrics, $metadata, $result);
        }

        // Dispatch event for anomaly notification
        if ($result->isAnomalous && $this->eventDispatcher !== null) {
            $this->eventDispatcher->dispatch(
                new QueueJobAnomalyDetectedEvent($metrics, $metadata, $result)
            );
        }

        return $result;
    }

    /**
     * Analyze multiple jobs in batch
     *
     * @param array $jobsData Array of [JobMetrics, JobMetadata] tuples
     * @return array Array of JobAnomalyResult indexed by job ID
     */
    public function analyzeBatch(array $jobsData): array
    {
        $results = [];

        foreach ($jobsData as [$metrics, $metadata]) {
            $results[$metrics->jobId] = $this->analyzeJobExecution($metrics, $metadata);
        }

        return $results;
    }

    /**
     * Get anomaly statistics for a queue
     *
     * @param string $queueName Queue to analyze
     * @param string $timeWindow Time window (e.g., '24 hours')
     * @return array Anomaly statistics
     */
    public function getQueueAnomalyStats(string $queueName, string $timeWindow = '24 hours'): array
    {
        // This would query persisted anomaly results
        // For now, return placeholder stats

        return [
            'queue_name' => $queueName,
            'time_window' => $timeWindow,
            'total_jobs_analyzed' => 0,
            'anomalies_detected' => 0,
            'critical_anomalies' => 0,
            'anomaly_rate' => 0.0,
            'most_common_patterns' => [],
            'top_anomalous_jobs' => []
        ];
    }

    /**
     * Log anomaly detection
     */
    private function logAnomaly(
        JobMetrics $metrics,
        JobMetadata $metadata,
        JobAnomalyResult $result
    ): void {
        $this->logger->warning('Queue job anomaly detected', [
            'job_id' => $metrics->jobId,
            'queue_name' => $metrics->queueName,
            'job_class' => $metadata->class->toString(),
            'anomaly_score' => $result->anomalyScore->value(),
            'severity' => $result->getSeverity(),
            'primary_indicator' => $result->primaryIndicator,
            'detected_patterns' => array_map(
                fn($pattern) => [
                    'type' => $pattern['type'],
                    'confidence' => $pattern['confidence']->value()
                ],
                $result->detectedPatterns
            ),
            'execution_time_ms' => $metrics->executionTimeMs,
            'memory_usage_mb' => $metrics->getMemoryUsageMB(),
            'attempts' => $metrics->attempts,
            'status' => $metrics->status
        ]);
    }

    /**
     * Alert on critical anomalies
     */
    private function alertCriticalAnomaly(
        JobMetrics $metrics,
        JobMetadata $metadata,
        JobAnomalyResult $result
    ): void {
        $this->logger->critical('CRITICAL queue job anomaly requires immediate attention', [
            'job_id' => $metrics->jobId,
            'queue_name' => $metrics->queueName,
            'job_class' => $metadata->class->toString(),
            'anomaly_score' => $result->anomalyScore->value(),
            'severity' => $result->getSeverity(),
            'recommended_action' => $result->getRecommendedAction(),
            'top_contributors' => $result->getTopContributors(3),
            'alert_type' => 'queue_job_critical_anomaly',
            'alert_priority' => 'high'
        ]);

        // In a full implementation, this would:
        // - Send PagerDuty/OpsGenie alert
        // - Post to Slack channel
        // - Trigger incident response workflow
    }

    /**
     * Enable anomaly monitoring for a queue
     *
     * This would be called during queue worker initialization.
     *
     * @param string $queueName Queue to monitor
     */
    public function enableMonitoring(string $queueName): void
    {
        $this->logger->info('Anomaly monitoring enabled', [
            'queue_name' => $queueName,
            'detector_threshold' => $this->anomalyDetector->getThreshold()->value(),
            'monitoring_active' => true
        ]);
    }

    /**
     * Disable anomaly monitoring for a queue
     *
     * @param string $queueName Queue to stop monitoring
     */
    public function disableMonitoring(string $queueName): void
    {
        $this->logger->info('Anomaly monitoring disabled', [
            'queue_name' => $queueName,
            'monitoring_active' => false
        ]);
    }

    /**
     * Get monitoring status
     *
     * @return array Monitoring configuration and stats
     */
    public function getMonitoringStatus(): array
    {
        return [
            'enabled' => true,
            'detector_threshold' => $this->anomalyDetector->getThreshold()->value(),
            'z_score_threshold' => 3.0, // From detector config
            'iqr_multiplier' => 1.5, // From detector config
            'monitored_queues' => [], // Would list active queues
            'detection_count_24h' => 0, // Would query persisted results
            'critical_count_24h' => 0
        ];
    }
}
