<?php

declare(strict_types=1);

namespace App\Application\Metrics;

use App\Framework\Attributes\Route;
use App\Framework\Cache\Cache;
use App\Framework\Database\ConnectionInterface;
use App\Framework\DateTime\Clock;
use App\Framework\Health\HealthCheckManager;
use App\Framework\Http\Method;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\Queue\Queue;
use App\Framework\Router\Result\PlainTextResult;
use App\Framework\Router\Result\JsonResult;

/**
 * Production Metrics Controller for Prometheus/Monitoring Integration
 *
 * Provides metrics in Prometheus format for monitoring systems:
 * - Health check metrics
 * - System resource metrics
 * - Application performance metrics
 */
final readonly class ProductionMetricsController
{
    public function __construct(
        private HealthCheckManager $healthCheckManager,
        private MemoryMonitor $memoryMonitor,
        private Clock $clock,
        private Queue $queue,
        private Cache $cache,
        private ConnectionInterface $database
    ) {
    }

    /**
     * Prometheus metrics endpoint
     * Format: https://prometheus.io/docs/instrumenting/exposition_formats/
     */
    #[Route(path: '/metrics', method: Method::GET)]
    public function metrics(): PlainTextResult
    {
        $metrics = [];

        // Health check metrics
        $healthReport = $this->healthCheckManager->runAllChecks();

        foreach ($healthReport->results as $name => $result) {
            $status = match ($result->status->value) {
                'healthy' => 1,
                'warning' => 0.5,
                'unhealthy' => 0,
                default => 0
            };

            $metricName = $this->sanitizeMetricName($name);
            $metrics[] = "# HELP health_check_{$metricName} Health check status (1=healthy, 0.5=warning, 0=unhealthy)";
            $metrics[] = "# TYPE health_check_{$metricName} gauge";
            $metrics[] = "health_check_{$metricName} {$status}";

            // Response time metric
            if ($result->responseTimeMs > 0) {
                $metrics[] = "# HELP health_check_{$metricName}_response_time_ms Health check response time in milliseconds";
                $metrics[] = "# TYPE health_check_{$metricName}_response_time_ms gauge";
                $metrics[] = "health_check_{$metricName}_response_time_ms {$result->responseTimeMs}";
            }
        }

        // Memory metrics
        $memoryUsage = $this->memoryMonitor->getCurrentMemory();
        $memoryLimit = $this->memoryMonitor->getMemoryLimit();
        $memoryPercent = $this->memoryMonitor->getMemoryUsagePercentage();

        $metrics[] = "# HELP php_memory_usage_bytes Current PHP memory usage in bytes";
        $metrics[] = "# TYPE php_memory_usage_bytes gauge";
        $metrics[] = "php_memory_usage_bytes {$memoryUsage->toBytes()}";

        $metrics[] = "# HELP php_memory_limit_bytes PHP memory limit in bytes";
        $metrics[] = "# TYPE php_memory_limit_bytes gauge";
        $metrics[] = "php_memory_limit_bytes {$memoryLimit->toBytes()}";

        $metrics[] = "# HELP php_memory_usage_percent PHP memory usage percentage";
        $metrics[] = "# TYPE php_memory_usage_percent gauge";
        $metrics[] = "php_memory_usage_percent {$memoryPercent->getValue()}";

        // Queue metrics
        try {
            $queueStats = $this->queue->getStats();
            $metrics[] = "# HELP queue_size Total number of jobs in queue";
            $metrics[] = "# TYPE queue_size gauge";
            $metrics[] = "queue_size " . ($queueStats['total_size'] ?? 0);

            $metrics[] = "# HELP queue_failed_jobs Total number of failed jobs";
            $metrics[] = "# TYPE queue_failed_jobs counter";
            $metrics[] = "queue_failed_jobs " . ($queueStats['failed_jobs'] ?? 0);
        } catch (\Throwable $e) {
            // Queue metrics unavailable
        }

        // System metrics
        $diskFree = disk_free_space('/');
        $diskTotal = disk_total_space('/');

        if ($diskFree !== false && $diskTotal !== false) {
            $metrics[] = "# HELP disk_free_bytes Free disk space in bytes";
            $metrics[] = "# TYPE disk_free_bytes gauge";
            $metrics[] = "disk_free_bytes {$diskFree}";

            $metrics[] = "# HELP disk_total_bytes Total disk space in bytes";
            $metrics[] = "# TYPE disk_total_bytes gauge";
            $metrics[] = "disk_total_bytes {$diskTotal}";

            $diskUsedPercent = (($diskTotal - $diskFree) / $diskTotal) * 100;
            $metrics[] = "# HELP disk_usage_percent Disk usage percentage";
            $metrics[] = "# TYPE disk_usage_percent gauge";
            $metrics[] = "disk_usage_percent " . round($diskUsedPercent, 2);
        }

        // Application uptime (approximate - since request start)
        $metrics[] = "# HELP app_uptime_seconds Application uptime in seconds";
        $metrics[] = "# TYPE app_uptime_seconds counter";
        $metrics[] = "app_uptime_seconds " . (time() - $_SERVER['REQUEST_TIME']);

        return new PlainTextResult(
            content: implode("\n", $metrics) . "\n",
            contentType: 'text/plain; version=0.0.4'
        );
    }

    /**
     * JSON metrics endpoint (for non-Prometheus monitoring)
     */
    #[Route(path: '/metrics/json', method: Method::GET)]
    public function metricsJson(): JsonResult
    {
        $healthReport = $this->healthCheckManager->runAllChecks();

        $healthChecks = [];
        foreach ($healthReport->results as $name => $result) {
            $healthChecks[$name] = [
                'status' => $result->status->value,
                'healthy' => $result->status->isHealthy(),
                'response_time_ms' => $result->responseTimeMs,
                'message' => $result->message,
                'details' => $result->details->toArray(),
            ];
        }

        $queueStats = [];
        try {
            $queueStats = $this->queue->getStats();
        } catch (\Throwable $e) {
            $queueStats = ['error' => 'Queue unavailable'];
        }

        $diskFree = disk_free_space('/');
        $diskTotal = disk_total_space('/');

        return new JsonResult([
            'timestamp' => $this->clock->now()->format('c'),
            'overall_health' => $healthReport->overallStatus->value,
            'health_checks' => $healthChecks,
            'resources' => [
                'memory' => [
                    'usage_bytes' => $this->memoryMonitor->getCurrentMemory()->toBytes(),
                    'limit_bytes' => $this->memoryMonitor->getMemoryLimit()->toBytes(),
                    'usage_percent' => $this->memoryMonitor->getMemoryUsagePercentage()->getValue(),
                ],
                'disk' => [
                    'free_bytes' => $diskFree,
                    'total_bytes' => $diskTotal,
                    'usage_percent' => $diskTotal > 0
                        ? round((($diskTotal - $diskFree) / $diskTotal) * 100, 2)
                        : 0,
                ],
            ],
            'queue' => $queueStats,
        ]);
    }

    /**
     * Sanitize metric name for Prometheus format
     */
    private function sanitizeMetricName(string $name): string
    {
        // Convert to lowercase and replace spaces/special chars with underscores
        $sanitized = strtolower($name);
        $sanitized = preg_replace('/[^a-z0-9_]/', '_', $sanitized);
        $sanitized = preg_replace('/_+/', '_', $sanitized);
        $sanitized = trim($sanitized, '_');

        return $sanitized;
    }
}
