<?php

declare(strict_types=1);

namespace App\Framework\Database\HealthCheck;

final readonly class HealthCheckResult
{
    public function __construct(
        public bool $isHealthy,
        public float $responseTimeMs,
        public ?string $message = null,
        public ?\Throwable $exception = null,
        public array $additionalData = []
    ) {}

    public function toArray(): array
    {
        return [
            'healthy' => $this->isHealthy,
            'response_time_ms' => round($this->responseTimeMs, 2),
            'message' => $this->message,
            'error' => $this->exception?->getMessage(),
            'error_code' => $this->exception?->getCode(),
            'additional_data' => $this->additionalData,
            'timestamp' => time(),
        ];
    }

    public function toJson(): string
    {
        return json_encode($this->toArray(), JSON_THROW_ON_ERROR);
    }

    public function withAdditionalData(string $key, mixed $value): self
    {
        return new self(
            $this->isHealthy,
            $this->responseTimeMs,
            $this->message,
            $this->exception,
            array_merge($this->additionalData, [$key => $value])
        );
    }

    public function getAdditionalData(string $key, mixed $default = null): mixed
    {
        return $this->additionalData[$key] ?? $default;
    }

    public function hasAdditionalData(string $key): bool
    {
        return array_key_exists($key, $this->additionalData);
    }

    public static function healthy(float $responseTimeMs, ?string $message = null, array $additionalData = []): self
    {
        return new self(true, $responseTimeMs, $message, null, $additionalData);
    }

    public static function unhealthy(float $responseTimeMs, string $message, ?\Throwable $exception = null, array $additionalData = []): self
    {
        return new self(false, $responseTimeMs, $message, $exception, $additionalData);
    }

    public static function timeout(float $timeoutMs): self
    {
        return self::unhealthy($timeoutMs, "Health check timed out after {$timeoutMs}ms");
    }

    public static function error(\Throwable $exception, float $responseTimeMs = 0): self
    {
        return self::unhealthy($responseTimeMs, $exception->getMessage(), $exception);
    }
}
