<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Controllers;

use App\Framework\Attributes\Route;
use App\Framework\Auth\Auth;
use App\Framework\Http\Enums\Method;
use App\Framework\Http\Result\JsonResult;
use App\Framework\LiveComponents\Cache\CacheMetricsCollector;
use App\Framework\LiveComponents\Cache\ComponentStateCache;
use App\Framework\LiveComponents\ComponentRegistry;
use App\Framework\LiveComponents\Performance\ComponentMetadataCache;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\View\ProcessorPerformanceTracker;

/**
 * LiveComponent Monitoring Controller
 *
 * Production monitoring endpoints for LiveComponents system.
 *
 * Features:
 * - Cache metrics and performance assessment
 * - Component registry statistics
 * - Template processor performance (if enabled)
 * - System health status
 * - Performance warnings and alerts
 * - Component inspection for debugging
 *
 * Security: All endpoints require admin authentication
 */

final readonly class LiveComponentMonitoringController
{
    public function __construct(
        private CacheMetricsCollector $metricsCollector,
        private ComponentRegistry $registry,
        private ComponentMetadataCache $metadataCache,
        private ComponentStateCache $stateCache,
        private ?ProcessorPerformanceTracker $performanceTracker = null
    ) {
    }

    /**
     * Get comprehensive metrics
     *
     * Returns detailed metrics for all caching layers, component registry,
     * and optional template processor performance.
     *
     * @route GET /api/livecomponents/metrics
     */
    #[Route('/api/livecomponents/metrics', method: Method::GET)]
    #[Auth(roles: ['admin'])]
    public function metrics(): JsonResult
    {
        $metrics = [
            'cache' => $this->getCacheMetrics(),
            'registry' => $this->getRegistryStats(),
            'timestamp' => time(),
            'system' => [
                'memory_usage' => memory_get_usage(true),
                'peak_memory' => memory_get_peak_usage(true),
            ],
        ];

        // Include processor performance if tracking is enabled
        if ($this->performanceTracker !== null && $this->performanceTracker->isEnabled()) {
            $metrics['processors'] = $this->performanceTracker->generateReport()->toArray();
        }

        return new JsonResult($metrics);
    }

    /**
     * Get health status
     *
     * Quick health check for monitoring systems.
     * Returns 200 if healthy, 503 if unhealthy.
     *
     * @route GET /api/livecomponents/health
     */
    #[Route('/api/livecomponents/health', method: Method::GET)]
    public function health(): JsonResult
    {
        $hasIssues = $this->metricsCollector->hasPerformanceIssues();
        $warnings = $this->metricsCollector->getPerformanceWarnings();

        $status = [
            'status' => $hasIssues ? 'degraded' : 'healthy',
            'components' => [
                'registry' => $this->isRegistryHealthy(),
                'cache' => ! $hasIssues,
            ],
            'warnings' => $warnings,
            'timestamp' => time(),
        ];

        // Return 503 if unhealthy for monitoring systems
        $httpStatus = $hasIssues ? 503 : 200;

        return new JsonResult($status, $httpStatus);
    }

    /**
     * Get cache performance summary
     *
     * Focused metrics for cache system performance.
     *
     * @route GET /api/livecomponents/metrics/cache
     */
    #[Route('/api/livecomponents/metrics/cache', method: Method::GET)]
    #[Auth(roles: ['admin'])]
    public function cacheMetrics(): JsonResult
    {
        return new JsonResult($this->getCacheMetrics());
    }

    /**
     * Get registry statistics
     *
     * Component registry stats and metadata.
     *
     * @route GET /api/livecomponents/metrics/registry
     */
    #[Route('/api/livecomponents/metrics/registry', method: Method::GET)]
    #[Auth(roles: ['admin'])]
    public function registryMetrics(): JsonResult
    {
        return new JsonResult($this->getRegistryStats());
    }

    /**
     * Reset metrics (development only)
     *
     * Resets all collected metrics. Only available in development.
     *
     * @route POST /api/livecomponents/metrics/reset
     */
    #[Route('/api/livecomponents/metrics/reset', method: Method::POST)]
    #[Auth(roles: ['admin'])]
    public function resetMetrics(): JsonResult
    {
        // Only allow in development
        if (getenv('APP_ENV') !== 'development') {
            return new JsonResult(
                ['error' => 'Metric reset only available in development'],
                403
            );
        }

        $this->metricsCollector->reset();

        if ($this->performanceTracker !== null) {
            $this->performanceTracker->reset();
        }

        return new JsonResult([
            'message' => 'Metrics reset successfully',
            'timestamp' => time(),
        ]);
    }

    /**
     * Get cache metrics data
     */
    private function getCacheMetrics(): array
    {
        return $this->metricsCollector->getSummary();
    }

    /**
     * Get registry statistics
     */
    private function getRegistryStats(): array
    {
        $componentNames = $this->registry->getAvailableComponentNames();

        return [
            'total_components' => count($componentNames),
            'component_names' => $componentNames,
            'memory_estimate' => $this->estimateRegistryMemoryUsage(),
        ];
    }

    /**
     * Check if registry is healthy
     */
    private function isRegistryHealthy(): bool
    {
        try {
            $components = $this->registry->getAvailableComponentNames();

            return ! empty($components);
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * Estimate registry memory usage
     */
    private function estimateRegistryMemoryUsage(): int
    {
        // Rough estimate: ~5KB per component
        $componentCount = count($this->registry->getAvailableComponentNames());

        return $componentCount * 5 * 1024; // bytes
    }

    /**
     * Inspect specific component
     *
     * Returns detailed information about a specific component instance
     * for debugging and development purposes.
     *
     * @route GET /api/livecomponents/inspect/{componentId}
     */
    #[Route('/api/livecomponents/inspect/{componentId}', method: Method::GET)]
    #[Auth(roles: ['admin'])]
    public function inspectComponent(string $componentId): JsonResult
    {
        try {
            // Parse component ID
            $id = ComponentId::fromString($componentId);
            [$componentName, $instanceId] = explode(':', $componentId, 2);

            // Get component class name
            $className = $this->registry->getClassName($componentName);

            if ($className === null) {
                return new JsonResult([
                    'error' => 'Component not found',
                    'component_id' => $componentId,
                ], 404);
            }

            // Get metadata
            $metadata = $this->metadataCache->get($className);

            // Try to get cached state
            $cachedState = $this->stateCache->get($id);

            // Build inspection data
            $inspection = [
                'component' => [
                    'id' => $componentId,
                    'name' => $componentName,
                    'instance_id' => $instanceId,
                    'class' => $className,
                ],
                'metadata' => [
                    'properties' => array_map(
                        fn ($prop) => [
                            'name' => $prop->name,
                            'type' => $prop->type,
                            'nullable' => $prop->nullable,
                            'hasDefault' => $prop->hasDefaultValue,
                        ],
                        $metadata->properties
                    ),
                    'actions' => array_map(
                        fn ($action) => [
                            'name' => $action->name,
                            'parameters' => $action->parameters,
                        ],
                        $metadata->actions
                    ),
                    'constructor_params' => $metadata->constructorParams,
                    'compiled_at' => date('Y-m-d H:i:s', $metadata->compiledAt),
                ],
                'state' => $cachedState !== null ? [
                    'cached' => true,
                    'data' => $cachedState->toArray(),
                ] : [
                    'cached' => false,
                    'message' => 'No cached state found',
                ],
                'cache_info' => [
                    'metadata_cached' => $this->metadataCache->has($className),
                    'state_cached' => $cachedState !== null,
                ],
                'timestamp' => time(),
            ];

            return new JsonResult($inspection);
        } catch (\Throwable $e) {
            return new JsonResult([
                'error' => 'Inspection failed',
                'message' => $e->getMessage(),
                'component_id' => $componentId,
            ], 500);
        }
    }

    /**
     * List all component instances
     *
     * Returns a list of all active component instances with their IDs.
     * Useful for discovering what components to inspect.
     *
     * @route GET /api/livecomponents/instances
     */
    #[Route('/api/livecomponents/instances', method: Method::GET)]
    #[Auth(roles: ['admin'])]
    public function listInstances(): JsonResult
    {
        $componentNames = $this->registry->getAvailableComponentNames();

        $instances = [];
        foreach ($componentNames as $name) {
            $className = $this->registry->getClassName($name);

            if ($className !== null) {
                $instances[] = [
                    'name' => $name,
                    'class' => $className,
                    'metadata_cached' => $this->metadataCache->has($className),
                ];
            }
        }

        return new JsonResult([
            'total' => count($instances),
            'instances' => $instances,
            'timestamp' => time(),
        ]);
    }
}
