from collections.abc import Callable
from dataclasses import dataclass
from typing import Generic, TypeVar


@dataclass
class UncertaintyEstimate:
    name_key: str


@dataclass
class UncertaintyPointEstimate(UncertaintyEstimate):
    value: float


@dataclass
class ProbabilityIsCorrect(UncertaintyPointEstimate):
    pass


T = TypeVar("T", bound=UncertaintyEstimate)


class DelayedUncertaintyEstimate(Generic[T]):
    """
    A return from a model for calculating uncertainty.

    It is potentially delayed as if it is computationally
    expensive to estimate the uncertainty the model might
    decide to lazily compute it only if needed.
    """

    def __init__(self, estimate_callback: Callable[[], T]) -> None:
        self._callback = estimate_callback
        self._value = None
        self._computed = False

    def get_estimate(self) -> T:
        if not self._computed:
            self._value = self._callback()
            self._computed = True
            self._callback = None

        return self._value


class PrecomputedUncertaintyEstimate(DelayedUncertaintyEstimate[T], Generic[T]):
    def __init__(self, estimate: T) -> None:
        super().__init__(lambda: estimate)
