<?php

declare(strict_types=1);

namespace App\Framework\Health;

final class HealthCheckManager
{
    /** @var HealthCheckInterface[] */
    private array $healthChecks = [];

    public function __construct()
    {
        // Default health checks will be registered via dependency injection
    }

    public function registerHealthCheck(HealthCheckInterface $healthCheck): void
    {
        $this->healthChecks[$healthCheck->getName()] = $healthCheck;
    }

    public function runAllChecks(): HealthReport
    {
        $results = [];
        $overallStatus = HealthStatus::HEALTHY;

        foreach ($this->healthChecks as $name => $healthCheck) {
            try {
                $result = $this->runSingleCheck($healthCheck);
                $results[$name] = $result;

                // Determine overall status (worst status wins)
                if ($result->status->getPriority() < $overallStatus->getPriority()) {
                    $overallStatus = $result->status;
                }

            } catch (\Throwable $e) {
                $results[$name] = HealthCheckResult::unhealthy(
                    'Health check execution failed: ' . $e->getMessage(),
                    exception: $e
                );
                $overallStatus = HealthStatus::UNHEALTHY;
            }
        }

        return new HealthReport($overallStatus, $results);
    }

    public function runCheck(string $name): ?HealthCheckResult
    {
        if (! isset($this->healthChecks[$name])) {
            return null;
        }

        return $this->runSingleCheck($this->healthChecks[$name]);
    }

    public function runChecksByCategory(HealthCheckCategory $category): HealthReport
    {
        $results = [];
        $overallStatus = HealthStatus::HEALTHY;

        foreach ($this->healthChecks as $name => $healthCheck) {
            if ($healthCheck->getCategory() !== $category) {
                continue;
            }

            $result = $this->runSingleCheck($healthCheck);
            $results[$name] = $result;

            if ($result->status->getPriority() < $overallStatus->getPriority()) {
                $overallStatus = $result->status;
            }
        }

        return new HealthReport($overallStatus, $results);
    }

    private function runSingleCheck(HealthCheckInterface $healthCheck): HealthCheckResult
    {
        $timeout = $healthCheck->getTimeout() / 1000; // Convert to seconds

        // Simple timeout implementation
        $startTime = microtime(true);
        $result = $healthCheck->check();
        $elapsed = microtime(true) - $startTime;

        if ($elapsed > $timeout) {
            return HealthCheckResult::warning(
                "Health check '{$healthCheck->getName()}' exceeded timeout ({$timeout}s)",
                ['elapsed_time' => $elapsed, 'timeout' => $timeout]
            );
        }

        return $result;
    }

    public function getRegisteredChecks(): array
    {
        return array_keys($this->healthChecks);
    }

    public function getChecksByCategory(): array
    {
        $categorized = [];

        foreach ($this->healthChecks as $name => $healthCheck) {
            $category = $healthCheck->getCategory()->value;
            $categorized[$category][] = [
                'name' => $name,
                'display_name' => $healthCheck->getName(),
                'timeout' => $healthCheck->getTimeout(),
            ];
        }

        return $categorized;
    }
}
