from __future__ import annotations

from typing import Callable
import shutil
import socket
import urllib.request


def tcp_check(host: str, port: int) -> Callable[[float], tuple[bool, str | None]]:
    def _run(timeout: float) -> tuple[bool, str | None]:
        with socket.create_connection((host, port), timeout=timeout):
            return True, None

    return _run


def http_check(
    url: str,
    method: str = "GET",
    expected_status_min: int = 200,
    expected_status_max: int = 399,
) -> Callable[[float], tuple[bool, str | None]]:
    method_upper = method.upper()

    def _run(timeout: float) -> tuple[bool, str | None]:
        request = urllib.request.Request(url=url, method=method_upper)
        with urllib.request.urlopen(request, timeout=timeout) as response:
            status_code = response.status

        is_ok = expected_status_min <= status_code <= expected_status_max
        if is_ok:
            return True, None

        return (
            False,
            f"HTTP {status_code} not in {expected_status_min}-{expected_status_max}",
        )

    return _run


def disk_space_check(
    path: str = "/",
    min_free_percent: float = 10.0,
) -> Callable[[float], tuple[bool, str | None]]:
    def _run(timeout: float) -> tuple[bool, str | None]:
        del timeout
        total, used, free = shutil.disk_usage(path)
        free_percent = (free / total) * 100 if total else 0.0

        if free_percent >= min_free_percent:
            return True, None

        return (
            False,
            f"free space {free_percent:.2f}% is below required {min_free_percent:.2f}%",
        )

    return _run
