import threading
from abc import ABC, abstractmethod
from typing import Any, Generic, Optional, TypeVar

T = TypeVar("T")

_STOP_TIMEOUT_SECONDS = 5.0


class BaseSampler(ABC, Generic[T]):
    """Background-thread sampler that polls at a fixed interval.

    Subclasses implement ``_take_sample`` to collect one data point and
    ``_compute_stats`` to aggregate all collected points into a result.
    """

    def __init__(self, interval: float) -> None:
        self._interval = interval
        self._samples: list[Any] = []
        self._stop_event = threading.Event()
        self._thread: Optional[threading.Thread] = None

    def start(self) -> None:
        self._samples = []
        self._stop_event.clear()
        self._thread = threading.Thread(target=self._run, daemon=True)
        self._thread.start()

    def stop(self) -> T:
        self._stop_event.set()
        if self._thread is not None:
            self._thread.join(timeout=_STOP_TIMEOUT_SECONDS)
        return self._compute_stats()

    def _run(self) -> None:
        while not self._stop_event.wait(self._interval):
            self._collect()

    def _collect(self) -> None:
        sample = self._take_sample()
        if sample is not None:
            self._samples.append(sample)

    @abstractmethod
    def _take_sample(self) -> Optional[object]: ...

    @abstractmethod
    def _compute_stats(self) -> T: ...
