<?php

declare(strict_types=1);

namespace App\Framework\MachineLearning\ModelManagement;

use App\Framework\MachineLearning\ModelManagement\ValueObjects\MLNotificationType;
use App\Framework\Notification\Notification;
use App\Framework\Notification\NotificationDispatcherInterface;
use App\Framework\Notification\ValueObjects\NotificationChannel;
use App\Framework\Notification\ValueObjects\NotificationPriority;
use App\Framework\Core\ValueObjects\Version;

/**
 * Notification-based Alerting Service for ML Model Management
 *
 * Uses the framework's Notification system for ML alerts:
 * - Email notifications for critical alerts
 * - Database persistence for audit trail
 * - Async delivery via Queue system
 * - Priority-based routing
 */
final readonly class NotificationAlertingService implements AlertingService
{
    public function __construct(
        private NotificationDispatcherInterface $dispatcher,
        private MLConfig $config,
        private string $adminRecipientId = 'admin'
    ) {}

    public function sendAlert(
        string $level,
        string $title,
        string $message,
        array $data = []
    ): void {
        // Map level to notification type and priority
        [$type, $priority] = $this->mapLevelToTypeAndPriority($level);

        $notification = Notification::create(
            $this->adminRecipientId,
            $type,
            $title,
            $message,
            ...$type->getRecommendedChannels()
        )
        ->withPriority($priority)
        ->withData($data);

        // Send asynchronously
        $this->dispatcher->send($notification, async: true);
    }

    private function mapLevelToTypeAndPriority(string $level): array
    {
        return match (strtolower($level)) {
            'critical' => [MLNotificationType::PERFORMANCE_DEGRADATION, NotificationPriority::URGENT],
            'warning' => [MLNotificationType::LOW_CONFIDENCE, NotificationPriority::HIGH],
            'info' => [MLNotificationType::MODEL_DEPLOYED, NotificationPriority::NORMAL],
            default => [MLNotificationType::MODEL_DEPLOYED, NotificationPriority::LOW],
        };
    }

    public function alertDriftDetected(
        string $modelName,
        Version $version,
        float $driftValue
    ): void {
        if (!$this->config->monitoringEnabled) {
            return;
        }

        $notification = Notification::create(
            $this->adminRecipientId,
            MLNotificationType::DRIFT_DETECTED,
            "Model Drift Detected: {$modelName}",
            $this->buildDriftMessage($modelName, $version, $driftValue),
            ...MLNotificationType::DRIFT_DETECTED->getRecommendedChannels()
        )
        ->withPriority(NotificationPriority::HIGH)
        ->withData([
            'model_name' => $modelName,
            'version' => $version->toString(),
            'drift_value' => $driftValue,
            'threshold' => $this->config->driftThreshold,
            'detection_time' => time(),
        ])
        ->withAction(
            url: "/admin/ml/models/{$modelName}",
            label: 'View Model Details'
        );

        // Send asynchronously
        $this->dispatcher->send($notification, async: true);
    }

    public function alertPerformanceDegradation(
        string $modelName,
        Version $version,
        float $currentAccuracy,
        float $baselineAccuracy
    ): void {
        if (!$this->config->monitoringEnabled) {
            return;
        }

        $degradationPercent = (($baselineAccuracy - $currentAccuracy) / $baselineAccuracy) * 100;

        $notification = Notification::create(
            $this->adminRecipientId,
            MLNotificationType::PERFORMANCE_DEGRADATION,
            "Performance Degradation: {$modelName}",
            $this->buildPerformanceDegradationMessage(
                $modelName,
                $version,
                $currentAccuracy,
                $baselineAccuracy,
                $degradationPercent
            ),
            ...MLNotificationType::PERFORMANCE_DEGRADATION->getRecommendedChannels()
        )
        ->withPriority(NotificationPriority::URGENT)
        ->withData([
            'model_name' => $modelName,
            'version' => $version->toString(),
            'current_accuracy' => $currentAccuracy,
            'baseline_accuracy' => $baselineAccuracy,
            'degradation_percent' => $degradationPercent,
            'detection_time' => time(),
        ])
        ->withAction(
            url: "/admin/ml/models/{$modelName}",
            label: 'Investigate Issue'
        );

        // Send asynchronously
        $this->dispatcher->send($notification, async: true);
    }

    public function alertLowConfidence(
        string $modelName,
        Version $version,
        float $averageConfidence
    ): void {
        if (!$this->config->monitoringEnabled) {
            return;
        }

        $notification = Notification::create(
            $this->adminRecipientId,
            MLNotificationType::LOW_CONFIDENCE,
            "Low Confidence Warning: {$modelName}",
            $this->buildLowConfidenceMessage($modelName, $version, $averageConfidence),
            ...MLNotificationType::LOW_CONFIDENCE->getRecommendedChannels()
        )
        ->withPriority(NotificationPriority::NORMAL)
        ->withData([
            'model_name' => $modelName,
            'version' => $version->toString(),
            'average_confidence' => $averageConfidence,
            'threshold' => $this->config->confidenceAlertThreshold,
            'detection_time' => time(),
        ])
        ->withAction(
            url: "/admin/ml/models/{$modelName}",
            label: 'Review Predictions'
        );

        // Send asynchronously
        $this->dispatcher->send($notification, async: true);
    }

    public function alertModelDeployed(
        string $modelName,
        Version $version,
        string $environment
    ): void {
        $notification = Notification::create(
            $this->adminRecipientId,
            MLNotificationType::MODEL_DEPLOYED,
            "Model Deployed: {$modelName} v{$version->toString()}",
            "Model {$modelName} version {$version->toString()} has been deployed to {$environment} environment.",
            ...MLNotificationType::MODEL_DEPLOYED->getRecommendedChannels()
        )
        ->withPriority(NotificationPriority::LOW)
        ->withData([
            'model_name' => $modelName,
            'version' => $version->toString(),
            'environment' => $environment,
            'deployment_time' => time(),
        ]);

        // Send asynchronously
        $this->dispatcher->send($notification, async: true);
    }

    public function alertAutoTuningTriggered(
        string $modelName,
        Version $version,
        array $suggestedParameters
    ): void {
        if (!$this->config->autoTuningEnabled) {
            return;
        }

        $notification = Notification::create(
            $this->adminRecipientId,
            MLNotificationType::AUTO_TUNING_TRIGGERED,
            "Auto-Tuning Triggered: {$modelName}",
            "Auto-tuning has been triggered for model {$modelName} v{$version->toString()} based on performance analysis.",
            NotificationChannel::DATABASE
        )
        ->withPriority(NotificationPriority::NORMAL)
        ->withData([
            'model_name' => $modelName,
            'version' => $version->toString(),
            'suggested_parameters' => $suggestedParameters,
            'trigger_time' => time(),
        ])
        ->withAction(
            url: "/admin/ml/tuning/{$modelName}",
            label: 'Review Suggestions'
        );

        // Send asynchronously
        $this->dispatcher->send($notification, async: true);
    }

    private function buildDriftMessage(
        string $modelName,
        Version $version,
        float $driftValue
    ): string {
        $driftPercent = round($driftValue * 100, 2);
        $thresholdPercent = round($this->config->driftThreshold * 100, 2);

        return "Model drift detected for {$modelName} v{$version->toString()}.\n\n"
            . "Drift Value: {$driftPercent}% (threshold: {$thresholdPercent}%)\n"
            . "This indicates the model's predictions are deviating from the baseline.\n\n"
            . "Recommended Actions:\n"
            . "- Review recent predictions\n"
            . "- Check for data distribution changes\n"
            . "- Consider model retraining";
    }

    private function buildPerformanceDegradationMessage(
        string $modelName,
        Version $version,
        float $currentAccuracy,
        float $baselineAccuracy,
        float $degradationPercent
    ): string {
        $current = round($currentAccuracy * 100, 2);
        $baseline = round($baselineAccuracy * 100, 2);
        $degradation = round($degradationPercent, 2);

        return "Performance degradation detected for {$modelName} v{$version->toString()}.\n\n"
            . "Current Accuracy: {$current}%\n"
            . "Baseline Accuracy: {$baseline}%\n"
            . "Degradation: {$degradation}%\n\n"
            . "Immediate action required:\n"
            . "- Investigate root cause\n"
            . "- Review recent data quality\n"
            . "- Consider model rollback or retraining";
    }

    private function buildLowConfidenceMessage(
        string $modelName,
        Version $version,
        float $averageConfidence
    ): string {
        $confidence = round($averageConfidence * 100, 2);
        $threshold = round($this->config->confidenceAlertThreshold * 100, 2);

        return "Low confidence detected for {$modelName} v{$version->toString()}.\n\n"
            . "Average Confidence: {$confidence}% (threshold: {$threshold}%)\n"
            . "The model is showing lower confidence in its predictions than expected.\n\n"
            . "Suggested Actions:\n"
            . "- Review prediction patterns\n"
            . "- Check input data quality\n"
            . "- Monitor for further degradation";
    }
}
