<?php

declare(strict_types=1);

namespace App\Framework\Database\NPlusOneDetection\MachineLearning;

use App\Framework\Config\Environment;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Database\NPlusOneDetection\MachineLearning\Extractors\QueryFeatureExtractor;
use App\Framework\DateTime\Clock;
use App\Framework\DI\Container;
use App\Framework\DI\Initializer;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\MachineLearning\ModelManagement\ModelPerformanceMonitor;
use App\Framework\MachineLearning\ModelManagement\ModelRegistry;
use App\Framework\Waf\MachineLearning\Detectors\ClusteringAnomalyDetector;
use App\Framework\Waf\MachineLearning\Detectors\StatisticalAnomalyDetector;

/**
 * N+1 Detection ML Engine Initializer
 *
 * Registers the N+1 Detection Machine Learning Engine with feature extractors
 * and anomaly detectors during framework startup.
 */
final readonly class NPlusOneDetectionEngineInitializer
{
    public function __construct(
        private Environment $environment,
        private Clock $clock,
        private Logger $logger,
        private Container $container
    ) {
    }

    /**
     * Initialize N+1 Detection ML Engine with extractors and detectors
     */
    #[Initializer]
    public function __invoke(): NPlusOneDetectionEngine
    {
        $this->logger->info('Initializing N+1 Detection ML Engine');

        try {
            // Configuration from environment
            $enabled = $this->environment->getBool('NPLUSONE_ML_ENABLED', true);
            $analysisTimeout = Duration::fromMilliseconds(
                $this->environment->getInt('NPLUSONE_ML_TIMEOUT_MS', 5000)
            );
            $confidenceThreshold = Percentage::from(
                $this->environment->getFloat('NPLUSONE_ML_CONFIDENCE_THRESHOLD', 60.0)
            );

            // Feature Extractors
            $extractors = [
                new QueryFeatureExtractor(
                    enabled: true,
                    priority: 10
                ),
            ];

            // Anomaly Detectors (reuse from WAF ML)
            $detectors = [
                new StatisticalAnomalyDetector(
                    enabled: true,
                    sensitivityMultiplier: 1.5,
                    minSampleSize: 3
                ),
                new ClusteringAnomalyDetector(
                    enabled: true,
                    clusterCount: 3,
                    distanceThreshold: 2.0
                ),
            ];

            $engine = new NPlusOneDetectionEngine(
                enabled: $enabled,
                extractors: $extractors,
                detectors: $detectors,
                clock: $this->clock,
                analysisTimeout: $analysisTimeout,
                confidenceThreshold: $confidenceThreshold
            );

            $config = $engine->getConfiguration();
            $this->logger->info('N+1 Detection ML Engine initialized successfully', LogContext::withData([
                'enabled' => $config['enabled'],
                'extractor_count' => $config['extractor_count'],
                'detector_count' => $config['detector_count'],
                'timeout_ms' => $config['analysis_timeout_ms'],
                'confidence_threshold' => $config['confidence_threshold'],
            ]));

            return $engine;
        } catch (\Throwable $e) {
            $this->logger->error('Failed to initialize N+1 Detection ML Engine', LogContext::withData([
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]));

            // Fallback: Return disabled engine to prevent application startup failure
            $this->logger->warning('Returning disabled N+1 Detection ML Engine as fallback');

            return new NPlusOneDetectionEngine(
                enabled: false,
                extractors: [],
                detectors: [],
                clock: $this->clock,
                analysisTimeout: Duration::fromSeconds(5),
                confidenceThreshold: Percentage::from(60.0)
            );
        }
    }

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

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

            $adapter = new NPlusOneModelAdapter(
                registry: $registry,
                performanceMonitor: $performanceMonitor,
                engine: $engine
            );

            // Auto-register current model version if enabled
            if ($this->environment->getBool('NPLUSONE_ML_AUTO_REGISTER', true)) {
                try {
                    $metadata = $adapter->registerCurrentModel();
                    $this->logger->info('N+1 model auto-registered', LogContext::withData([
                        '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('N+1 model registration skipped', LogContext::withData([
                        'reason' => $e->getMessage(),
                    ]));
                }
            }

            $this->logger->info('N+1 Model Adapter initialized successfully');

            return $adapter;
        } catch (\Throwable $e) {
            $this->logger->error('Failed to initialize N+1 Model Adapter', LogContext::withData([
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]));

            throw $e;
        }
    }
}
