<?php

declare(strict_types=1);

namespace App\Framework\Queue\MachineLearning;

use App\Framework\Config\Environment;
use App\Framework\DI\Container;
use App\Framework\DI\Attributes\Initializer;
use App\Framework\MachineLearning\ModelManagement\ModelPerformanceMonitor;
use App\Framework\MachineLearning\ModelManagement\ModelRegistry;
use App\Framework\Queue\Services\JobMetricsManagerInterface;
use App\Framework\Core\ValueObjects\Score;
use Psr\Log\LoggerInterface;

/**
 * Job Anomaly Detection Initializer
 *
 * Registers ML-based job anomaly detection components in DI container.
 *
 * Registered Services:
 * - JobFeatureExtractor: Extract 8 behavioral features from job sequences
 * - JobAnomalyDetector: Statistical + heuristic anomaly detection
 * - JobHistoryAnalyzer: Historical job pattern analysis coordinator
 *
 * Configuration:
 * - Uses framework defaults for thresholds (customizable via environment)
 * - Integrates with existing JobMetricsManager infrastructure
 */
final readonly class JobAnomalyDetectionInitializer
{
    public function __construct(
        private Container $container,
        private Environment $environment,
        private LoggerInterface $logger
    ) {}

    #[Initializer]
    public function initialize(Container $container): void
    {
        // Register JobFeatureExtractor
        $container->singleton(JobFeatureExtractor::class, function() {
            return new JobFeatureExtractor(
                minConfidence: $this->getMinConfidence()
            );
        });

        // Register JobAnomalyDetector
        $container->singleton(JobAnomalyDetector::class, function() {
            return new JobAnomalyDetector(
                anomalyThreshold: $this->getAnomalyThreshold(),
                zScoreThreshold: $this->getZScoreThreshold(),
                iqrMultiplier: $this->getIqrMultiplier()
            );
        });

        // Register JobHistoryAnalyzer
        $container->singleton(JobHistoryAnalyzer::class, function(Container $container) {
            return new JobHistoryAnalyzer(
                metricsManager: $container->get(JobMetricsManagerInterface::class),
                featureExtractor: $container->get(JobFeatureExtractor::class),
                anomalyDetector: $container->get(JobAnomalyDetector::class)
            );
        });
    }

    /**
     * Get minimum confidence threshold for feature extraction
     *
     * Default: 0.6 (60%)
     * Environment: JOB_ANOMALY_MIN_CONFIDENCE
     */
    private function getMinConfidence(): float
    {
        $envValue = getenv('JOB_ANOMALY_MIN_CONFIDENCE');

        if ($envValue !== false) {
            $value = (float) $envValue;
            return max(0.0, min(1.0, $value));  // Clamp to 0.0-1.0
        }

        return 0.6;
    }

    /**
     * Get anomaly detection threshold
     *
     * Default: 0.5 (50% - Score of 50)
     * Environment: JOB_ANOMALY_THRESHOLD
     */
    private function getAnomalyThreshold(): Score
    {
        $envValue = getenv('JOB_ANOMALY_THRESHOLD');

        if ($envValue !== false) {
            $value = (int) $envValue;
            $value = max(0, min(100, $value));  // Clamp to 0-100
            return new Score($value);
        }

        return new Score(50);  // 50% threshold
    }

    /**
     * Get Z-score threshold for statistical outlier detection
     *
     * Default: 3.0 (3 standard deviations)
     * Environment: JOB_ANOMALY_ZSCORE_THRESHOLD
     */
    private function getZScoreThreshold(): float
    {
        $envValue = getenv('JOB_ANOMALY_ZSCORE_THRESHOLD');

        if ($envValue !== false) {
            $value = (float) $envValue;
            return max(1.0, min(5.0, $value));  // Clamp to 1.0-5.0
        }

        return 3.0;
    }

    /**
     * Get IQR multiplier for outlier detection
     *
     * Default: 1.5 (standard IQR method)
     * Environment: JOB_ANOMALY_IQR_MULTIPLIER
     */
    private function getIqrMultiplier(): float
    {
        $envValue = getenv('JOB_ANOMALY_IQR_MULTIPLIER');

        if ($envValue !== false) {
            $value = (float) $envValue;
            return max(1.0, min(3.0, $value));  // Clamp to 1.0-3.0
        }

        return 1.5;
    }

    /**
     * Initialize Queue Anomaly Model Adapter for ML Model Management integration
     */
    #[Initializer]
    public function initializeModelAdapter(): QueueAnomalyModelAdapter
    {
        $this->logger->info('Initializing Queue Anomaly Model Adapter');

        try {
            // Get required dependencies from container
            $registry = $this->container->get(ModelRegistry::class);
            $performanceMonitor = $this->container->get(ModelPerformanceMonitor::class);
            $anomalyDetector = $this->container->get(JobAnomalyDetector::class);

            $adapter = new QueueAnomalyModelAdapter(
                registry: $registry,
                performanceMonitor: $performanceMonitor,
                detector: $anomalyDetector
            );

            // Auto-register current model version if enabled
            if ($this->environment->getBool('QUEUE_ML_AUTO_REGISTER', true)) {
                try {
                    $metadata = $adapter->registerCurrentModel();
                    $this->logger->info('Queue anomaly model auto-registered', [
                        'model_name' => $metadata->modelName,
                        'version' => $metadata->version->toString(),
                        'type' => $metadata->modelType->value,
                    ]);
                } catch (\Exception $e) {
                    // Model might already exist, which is fine
                    $this->logger->debug('Queue anomaly model registration skipped', [
                        'reason' => $e->getMessage(),
                    ]);
                }
            }

            $this->logger->info('Queue Anomaly Model Adapter initialized successfully');

            return $adapter;
        } catch (\Throwable $e) {
            $this->logger->error('Failed to initialize Queue Anomaly Model Adapter', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);

            throw $e;
        }
    }
}
