from __future__ import annotations

from dataclasses import dataclass
from typing import Callable
import time


CheckFn = Callable[[float], tuple[bool, str | None]]


@dataclass(slots=True)
class ServiceCheckResult:
    name: str
    status: str
    message: str | None = None
    elapsed_ms: float | None = None

    @property
    def is_healthy(self) -> bool:
        return self.status == "OK"


@dataclass(slots=True)
class HealthReport:
    results: list[ServiceCheckResult]

    @property
    def is_healthy(self) -> bool:
        return all(item.is_healthy for item in self.results)

    def to_dict(self) -> dict:
        return {
            "is_healthy": self.is_healthy,
            "services": [
                {
                    "name": item.name,
                    "status": item.status,
                    "message": item.message,
                    "elapsed_ms": item.elapsed_ms,
                }
                for item in self.results
            ],
        }

    def to_table(self) -> str:
        title = "Service Health Report"
        max_name = max((len(item.name) for item in self.results), default=8)
        lines = [title, ""]

        for item in self.results:
            row = f"{item.name:<{max_name}}    {item.status}"
            if item.message:
                row = f"{row} ({item.message})"
            lines.append(row)

        return "\n".join(lines)


class ServiceMonitor:
    def __init__(self, timeout: float = 3.0) -> None:
        self.timeout = timeout
        self._checks: list[tuple[str, CheckFn]] = []

    def add_check(self, name: str, check: CheckFn) -> None:
        self._checks.append((name, check))

    def run(self) -> HealthReport:
        results: list[ServiceCheckResult] = []

        for name, check in self._checks:
            start = time.perf_counter()
            try:
                ok, message = check(self.timeout)
                status = "OK" if ok else "FAILED"
            except Exception as exc:  # pragma: no cover
                status = "FAILED"
                message = str(exc)

            elapsed = (time.perf_counter() - start) * 1000
            results.append(
                ServiceCheckResult(
                    name=name,
                    status=status,
                    message=message,
                    elapsed_ms=round(elapsed, 2),
                )
            )

        return HealthReport(results=results)
