<?php

declare(strict_types=1);

namespace App\Framework\Health\Checks;

use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Health\HealthCheckCategory;
use App\Framework\Health\HealthCheckInterface;
use App\Framework\Health\HealthCheckResult;

final readonly class SystemHealthCheck implements HealthCheckInterface
{
    public function __construct(
        private int $memoryWarningThreshold = 85, // Warn at 85% memory usage
        private int $memoryCriticalThreshold = 95  // Critical at 95% memory usage
    ) {
    }

    public function check(): HealthCheckResult
    {
        $startTime = microtime(true);

        try {
            $memoryUsage = Byte::fromBytes(memory_get_usage(true));
            $memoryPeak = Byte::fromBytes(memory_get_peak_usage(true));
            $memoryLimit = $this->getMemoryLimit();

            $phpVersion = PHP_VERSION;
            $phpExtensions = get_loaded_extensions();
            $keyExtensions = array_intersect($phpExtensions, ['json', 'mbstring', 'curl', 'pdo', 'redis', 'apcu']);

            $responseTime = Duration::fromSeconds(microtime(true) - $startTime);
            $memoryUsagePercentage = ! $memoryLimit->isEmpty()
                ? $memoryUsage->percentOf($memoryLimit)
                : Percentage::zero();

            $details = [
                'php_version' => $phpVersion,
                'memory_usage' => $memoryUsage->toHumanReadable(),
                'memory_peak' => $memoryPeak->toHumanReadable(),
                'memory_limit' => $memoryLimit->toHumanReadable(),
                'memory_usage_percentage' => $memoryUsagePercentage->format(),
                'loaded_extensions_count' => count($phpExtensions),
                'key_extensions' => implode(', ', $keyExtensions), // Convert array to string for JavaScript
                'response_time' => $responseTime->toHumanReadable(),
            ];

            // Check memory usage
            if (! $memoryLimit->isEmpty()) {
                if ($memoryUsagePercentage->getValue() >= $this->memoryCriticalThreshold) {
                    return HealthCheckResult::unhealthy(
                        'System',
                        "Critical memory usage: {$memoryUsagePercentage->format()}",
                        $details,
                        $responseTime->toMilliseconds()
                    );
                }

                if ($memoryUsagePercentage->getValue() >= $this->memoryWarningThreshold) {
                    return HealthCheckResult::warning(
                        'System',
                        "High memory usage: {$memoryUsagePercentage->format()}",
                        $details,
                        $responseTime->toMilliseconds()
                    );
                }
            }

            // Check critical extensions
            $missingExtensions = array_diff(['json', 'mbstring'], $phpExtensions);
            if (! empty($missingExtensions)) {
                return HealthCheckResult::unhealthy(
                    'System',
                    'Critical PHP extensions missing: ' . implode(', ', $missingExtensions),
                    $details,
                    $responseTime->toMilliseconds()
                );
            }

            return HealthCheckResult::healthy(
                'System',
                $details,
                $responseTime->toMilliseconds()
            );

        } catch (\Throwable $e) {
            $responseTime = Duration::fromSeconds(microtime(true) - $startTime);

            return HealthCheckResult::unhealthy(
                'System',
                'Health check failed: ' . $e->getMessage(),
                [],
                $responseTime->toMilliseconds(),
                $e
            );
        }
    }

    private function getMemoryLimit(): Byte
    {
        $memoryLimit = ini_get('memory_limit');

        if ($memoryLimit === '-1') {
            return Byte::zero(); // No limit
        }

        // Parse memory limit string (e.g. "128M", "1G", "512K")
        try {
            return Byte::parse($memoryLimit);
        } catch (\InvalidArgumentException $e) {
            // Fallback for old-style parsing
            $value = (int) $memoryLimit;
            $unit = strtolower(substr($memoryLimit, -1));

            $bytes = match ($unit) {
                'g' => $value * 1024 * 1024 * 1024,
                'm' => $value * 1024 * 1024,
                'k' => $value * 1024,
                default => $value
            };

            return Byte::fromBytes($bytes);
        }
    }

    public function getName(): string
    {
        return 'System Resources';
    }

    public function getCategory(): HealthCheckCategory
    {
        return HealthCheckCategory::SYSTEM;
    }

    public function getTimeout(): int
    {
        return 1000; // 1 second
    }
}
