<?php

declare(strict_types=1);

namespace App\Application\Api\MachineLearning;

use App\Framework\Attributes\Route;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\MachineLearning\ModelManagement\ModelPerformanceMonitor;
use App\Framework\MachineLearning\ModelManagement\ModelRegistry;
use App\Framework\OpenApi\Attributes\ApiEndpoint;
use App\Framework\OpenApi\Attributes\ApiParameter;
use App\Framework\OpenApi\Attributes\ApiResponse;
use App\Framework\OpenApi\Attributes\ApiSecurity;
use App\Framework\Router\Result\JsonResult;

/**
 * ML Dashboard API Controller
 *
 * RESTful API endpoints for ML monitoring dashboard:
 * - Performance overview
 * - Degradation alerts
 * - System health indicators
 * - Comprehensive dashboard data export
 */
#[ApiSecurity('bearerAuth')]
final readonly class MLDashboardController
{
    public function __construct(
        private ModelRegistry $registry,
        private ModelPerformanceMonitor $performanceMonitor
    ) {}

    /**
     * Get all models from registry (all names and all versions)
     */
    private function getAllModels(): array
    {
        $modelNames = $this->registry->getAllModelNames();

        $allModels = [];
        foreach ($modelNames as $modelName) {
            $versions = $this->registry->getAll($modelName);
            $allModels = array_merge($allModels, $versions);
        }

        return $allModels;
    }

    #[Route(path: '/api/ml/dashboard', method: Method::GET)]
    #[ApiEndpoint(
        summary: 'Get complete dashboard data',
        description: 'Retrieve comprehensive ML system dashboard data including performance, alerts, and health',
        tags: ['Machine Learning'],
    )]
    #[ApiParameter(
        name: 'timeWindow',
        in: 'query',
        description: 'Time window in hours for metrics (default: 24)',
        required: false,
        type: 'integer',
        example: 24,
    )]
    #[ApiResponse(
        statusCode: 200,
        description: 'Dashboard data retrieved successfully',
        example: [
            'timestamp' => '2024-01-01T00:00:00Z',
            'summary' => [
                'total_models' => 5,
                'healthy_models' => 4,
                'degraded_models' => 1,
                'total_predictions' => 10523,
                'average_accuracy' => 0.91,
                'overall_status' => 'healthy',
            ],
            'models' => [
                [
                    'model_name' => 'fraud-detector',
                    'version' => '1.0.0',
                    'type' => 'supervised',
                    'accuracy' => 0.94,
                    'status' => 'healthy',
                ],
            ],
            'alerts' => [],
        ],
    )]
    public function getDashboardData(HttpRequest $request): JsonResult
    {
        $timeWindowHours = $request->query->getInt('timeWindow', 24);
        $timeWindow = Duration::fromHours($timeWindowHours);

        // Get all models
        $allModels = $this->getAllModels();

        // Collect performance overview
        $performanceOverview = [];
        $totalPredictions = 0;
        $accuracySum = 0.0;
        $healthyCount = 0;
        $degradedCount = 0;

        foreach ($allModels as $metadata) {
            $metrics = $this->performanceMonitor->getCurrentMetrics(
                $metadata->modelName,
                $metadata->version,
                $timeWindow
            );

            $isHealthy = $metrics['accuracy'] >= 0.85;

            if ($isHealthy) {
                $healthyCount++;
            } else {
                $degradedCount++;
            }

            $performanceOverview[] = [
                'model_name' => $metadata->modelName,
                'version' => $metadata->version->toString(),
                'type' => $metadata->modelType->value,
                'accuracy' => $metrics['accuracy'],
                'precision' => $metrics['precision'] ?? null,
                'recall' => $metrics['recall'] ?? null,
                'f1_score' => $metrics['f1_score'] ?? null,
                'total_predictions' => $metrics['total_predictions'],
                'average_confidence' => $metrics['average_confidence'] ?? null,
                'threshold' => $metadata->configuration['threshold'] ?? null,
                'status' => $isHealthy ? 'healthy' : 'degraded',
            ];

            $totalPredictions += $metrics['total_predictions'];
            $accuracySum += $metrics['accuracy'];
        }

        // Calculate degradation alerts
        $degradationAlerts = [];
        foreach ($performanceOverview as $model) {
            if ($model['status'] === 'degraded') {
                $degradationAlerts[] = [
                    'model_name' => $model['model_name'],
                    'version' => $model['version'],
                    'current_accuracy' => $model['accuracy'],
                    'threshold' => 0.85,
                    'severity' => $model['accuracy'] < 0.7 ? 'critical' : 'warning',
                    'recommendation' => 'Consider retraining or rolling back to previous version',
                ];
            }
        }

        // Calculate health indicators
        $modelCount = count($allModels);
        $averageAccuracy = $modelCount > 0 ? $accuracySum / $modelCount : 0.0;
        $overallStatus = $degradedCount === 0 ? 'healthy' : ($degradedCount > $modelCount / 2 ? 'critical' : 'warning');

        // Build dashboard data
        $dashboardData = [
            'timestamp' => Timestamp::now()->format('Y-m-d\TH:i:s\Z'),
            'time_window_hours' => $timeWindowHours,
            'summary' => [
                'total_models' => $modelCount,
                'healthy_models' => $healthyCount,
                'degraded_models' => $degradedCount,
                'total_predictions' => $totalPredictions,
                'average_accuracy' => round($averageAccuracy, 4),
                'overall_status' => $overallStatus,
            ],
            'models' => $performanceOverview,
            'alerts' => $degradationAlerts,
        ];

        return new JsonResult($dashboardData);
    }

    #[Route(path: '/api/ml/dashboard/health', method: Method::GET)]
    #[ApiEndpoint(
        summary: 'Get system health indicators',
        description: 'Retrieve ML system health status and key indicators',
        tags: ['Machine Learning'],
    )]
    #[ApiResponse(
        statusCode: 200,
        description: 'Health indicators retrieved successfully',
        example: [
            'overall_status' => 'healthy',
            'healthy_models' => 4,
            'degraded_models' => 1,
            'critical_models' => 0,
            'total_models' => 5,
            'health_percentage' => 80.0,
            'average_accuracy' => 0.91,
        ],
    )]
    public function getHealthIndicators(): JsonResult
    {
        $allModels = $this->getAllModels();
        $timeWindow = Duration::fromHours(1);

        $healthyCount = 0;
        $degradedCount = 0;
        $criticalCount = 0;
        $accuracySum = 0.0;

        foreach ($allModels as $metadata) {
            $metrics = $this->performanceMonitor->getCurrentMetrics(
                $metadata->modelName,
                $metadata->version,
                $timeWindow
            );

            $accuracy = $metrics['accuracy'];
            $accuracySum += $accuracy;

            if ($accuracy >= 0.85) {
                $healthyCount++;
            } elseif ($accuracy >= 0.7) {
                $degradedCount++;
            } else {
                $criticalCount++;
            }
        }

        $modelCount = count($allModels);
        $healthPercentage = $modelCount > 0 ? ($healthyCount / $modelCount) * 100 : 0.0;
        $averageAccuracy = $modelCount > 0 ? $accuracySum / $modelCount : 0.0;

        $overallStatus = match (true) {
            $criticalCount > 0 => 'critical',
            $degradedCount > $modelCount / 2 => 'warning',
            $degradedCount > 0 => 'warning',
            default => 'healthy'
        };

        return new JsonResult([
            'overall_status' => $overallStatus,
            'healthy_models' => $healthyCount,
            'degraded_models' => $degradedCount,
            'critical_models' => $criticalCount,
            'total_models' => $modelCount,
            'health_percentage' => round($healthPercentage, 2),
            'average_accuracy' => round($averageAccuracy, 4),
            'timestamp' => date('c'),
        ]);
    }

    #[Route(path: '/api/ml/dashboard/alerts', method: Method::GET)]
    #[ApiEndpoint(
        summary: 'Get active alerts',
        description: 'Retrieve all active degradation and performance alerts',
        tags: ['Machine Learning'],
    )]
    #[ApiParameter(
        name: 'severity',
        in: 'query',
        description: 'Filter by severity (warning, critical)',
        required: false,
        type: 'string',
        example: 'critical',
    )]
    #[ApiResponse(
        statusCode: 200,
        description: 'Alerts retrieved successfully',
        example: [
            'alerts' => [
                [
                    'model_name' => 'spam-classifier',
                    'version' => '1.0.0',
                    'severity' => 'warning',
                    'current_accuracy' => 0.78,
                    'threshold' => 0.85,
                    'recommendation' => 'Consider retraining or rolling back',
                ],
            ],
            'total_alerts' => 1,
        ],
    )]
    public function getAlerts(HttpRequest $request): JsonResult
    {
        $severityFilter = $request->query->get('severity');
        $allModels = $this->getAllModels();
        $timeWindow = Duration::fromHours(1);

        $alerts = [];

        foreach ($allModels as $metadata) {
            $metrics = $this->performanceMonitor->getCurrentMetrics(
                $metadata->modelName,
                $metadata->version,
                $timeWindow
            );

            $accuracy = $metrics['accuracy'];

            if ($accuracy < 0.85) {
                $severity = $accuracy < 0.7 ? 'critical' : 'warning';

                // Apply severity filter if specified
                if ($severityFilter !== null && $severity !== strtolower($severityFilter)) {
                    continue;
                }

                $alerts[] = [
                    'model_name' => $metadata->modelName,
                    'version' => $metadata->version->toString(),
                    'type' => $metadata->modelType->value,
                    'severity' => $severity,
                    'current_accuracy' => $accuracy,
                    'threshold' => 0.85,
                    'deviation' => round((0.85 - $accuracy) * 100, 2),
                    'total_predictions' => $metrics['total_predictions'],
                    'recommendation' => 'Consider retraining or rolling back to previous version',
                    'detected_at' => date('c'),
                ];
            }
        }

        return new JsonResult([
            'alerts' => $alerts,
            'total_alerts' => count($alerts),
            'severity_filter' => $severityFilter,
            'timestamp' => date('c'),
        ]);
    }

    #[Route(path: '/api/ml/dashboard/confusion-matrices', method: Method::GET)]
    #[ApiEndpoint(
        summary: 'Get confusion matrices',
        description: 'Retrieve confusion matrices for all models with classification metrics',
        tags: ['Machine Learning'],
    )]
    #[ApiResponse(
        statusCode: 200,
        description: 'Confusion matrices retrieved successfully',
        example: [
            'matrices' => [
                [
                    'model_name' => 'fraud-detector',
                    'version' => '1.0.0',
                    'confusion_matrix' => [
                        'true_positive' => 234,
                        'true_negative' => 145,
                        'false_positive' => 12,
                        'false_negative' => 9,
                    ],
                    'fp_rate' => 0.03,
                    'fn_rate' => 0.023,
                ],
            ],
        ],
    )]
    public function getConfusionMatrices(): JsonResult
    {
        $allModels = $this->getAllModels();
        $timeWindow = Duration::fromHours(24);

        $matrices = [];

        foreach ($allModels as $metadata) {
            $metrics = $this->performanceMonitor->getCurrentMetrics(
                $metadata->modelName,
                $metadata->version,
                $timeWindow
            );

            if (isset($metrics['confusion_matrix'])) {
                $cm = $metrics['confusion_matrix'];
                $total = $metrics['total_predictions'];

                $matrices[] = [
                    'model_name' => $metadata->modelName,
                    'version' => $metadata->version->toString(),
                    'type' => $metadata->modelType->value,
                    'confusion_matrix' => $cm,
                    'fp_rate' => $total > 0 ? round($cm['false_positive'] / $total, 4) : 0.0,
                    'fn_rate' => $total > 0 ? round($cm['false_negative'] / $total, 4) : 0.0,
                    'total_predictions' => $total,
                ];
            }
        }

        return new JsonResult([
            'matrices' => $matrices,
            'total_models' => count($matrices),
            'timestamp' => date('c'),
        ]);
    }

    #[Route(path: '/api/ml/dashboard/registry-summary', method: Method::GET)]
    #[ApiEndpoint(
        summary: 'Get registry summary',
        description: 'Retrieve summary statistics about the model registry',
        tags: ['Machine Learning'],
    )]
    #[ApiResponse(
        statusCode: 200,
        description: 'Registry summary retrieved successfully',
        example: [
            'total_models' => 5,
            'by_type' => [
                'supervised' => 3,
                'unsupervised' => 2,
                'reinforcement' => 0,
            ],
            'total_versions' => 12,
            'models' => [
                [
                    'model_name' => 'fraud-detector',
                    'version_count' => 3,
                    'latest_version' => '3.0.0',
                ],
            ],
        ],
    )]
    public function getRegistrySummary(): JsonResult
    {
        $allModels = $this->getAllModels();

        // Count by type
        $byType = [
            'supervised' => 0,
            'unsupervised' => 0,
            'reinforcement' => 0,
        ];

        // Group by model name
        $modelGroups = [];

        foreach ($allModels as $metadata) {
            $typeName = strtolower($metadata->modelType->value);
            $byType[$typeName] = ($byType[$typeName] ?? 0) + 1;

            $modelName = $metadata->modelName;
            if (!isset($modelGroups[$modelName])) {
                $modelGroups[$modelName] = [
                    'model_name' => $modelName,
                    'type' => $metadata->modelType->value,
                    'versions' => [],
                ];
            }

            $modelGroups[$modelName]['versions'][] = $metadata->version->toString();
        }

        // Calculate summary per model
        $modelsSummary = [];
        foreach ($modelGroups as $modelName => $group) {
            // Sort versions
            $versions = $group['versions'];
            usort($versions, 'version_compare');

            $modelsSummary[] = [
                'model_name' => $modelName,
                'type' => $group['type'],
                'version_count' => count($versions),
                'latest_version' => end($versions),
                'oldest_version' => reset($versions),
            ];
        }

        return new JsonResult([
            'total_models' => count($modelGroups),
            'by_type' => $byType,
            'total_versions' => count($allModels),
            'models' => $modelsSummary,
            'timestamp' => date('c'),
        ]);
    }
}
