"""Versioned, non-interpolating policy for future modelled snow-depth projection."""

from __future__ import annotations

from dataclasses import asdict, dataclass
from enum import Enum
from typing import Any


class SnowDepthSpatialPolicy(str, Enum):
    """How future route facts may represent modelled ground snow depth."""

    NEAREST_PROVIDER_ROUTE_SAMPLE = "NEAREST_PROVIDER_ROUTE_SAMPLE"


class SnowDepthTemporalPolicy(str, Enum):
    """How future timeline facts may represent modelled ground snow depth."""

    NEAREST_PROVIDER_HOUR = "NEAREST_PROVIDER_HOUR"


@dataclass(frozen=True)
class ModelledSnowDepthNearestContextPolicy:
    """A3 v0.1 policy; it does not reconstruct a local snowpack."""

    policy_id: str
    version: str
    spatial_policy: SnowDepthSpatialPolicy
    temporal_policy: SnowDepthTemporalPolicy
    preserves_exact_source_support: bool
    permits_linear_interpolation: bool
    fact_semantics: str
    limitations: tuple[str, ...]

    def __post_init__(self) -> None:
        if not self.policy_id or not self.version:
            raise ValueError("snow-depth policy requires ID and version")
        if self.spatial_policy is not SnowDepthSpatialPolicy.NEAREST_PROVIDER_ROUTE_SAMPLE:
            raise ValueError("A3 v0.1 snow depth requires nearest route-sample context")
        if self.temporal_policy is not SnowDepthTemporalPolicy.NEAREST_PROVIDER_HOUR:
            raise ValueError("A3 v0.1 snow depth requires nearest provider-hour context")
        if not self.preserves_exact_source_support:
            raise ValueError("snow-depth policy must preserve exact source support")
        if self.permits_linear_interpolation:
            raise ValueError("A3 v0.1 snow depth must not linearly interpolate")
        if self.fact_semantics != "MODELLED_GROUND_SNOW_DEPTH":
            raise ValueError("snow-depth policy has invalid fact semantics")

    def to_dict(self) -> dict[str, Any]:
        return _json_value(asdict(self))


MODELLED_SNOW_DEPTH_NEAREST_CONTEXT_POLICY_V0_1 = ModelledSnowDepthNearestContextPolicy(
    policy_id="modelled_snow_depth_nearest_context_v0_1",
    version="v0_1",
    spatial_policy=SnowDepthSpatialPolicy.NEAREST_PROVIDER_ROUTE_SAMPLE,
    temporal_policy=SnowDepthTemporalPolicy.NEAREST_PROVIDER_HOUR,
    preserves_exact_source_support=True,
    permits_linear_interpolation=False,
    fact_semantics="MODELLED_GROUND_SNOW_DEPTH",
    limitations=(
        "MODELLED_SNOW_DEPTH_NEAREST_CONTEXT_IS_NOT_LOCAL_SNOWPACK_RECONSTRUCTION",
        "MODELLED_SNOW_DEPTH_IS_NOT_OBSERVED_SNOW_DEPTH_OR_FRACTIONAL_SNOW_COVER",
    ),
)


def _json_value(value: Any) -> Any:
    if isinstance(value, Enum):
        return value.value
    if isinstance(value, dict):
        return {str(key): _json_value(item) for key, item in value.items()}
    if isinstance(value, (tuple, list)):
        return [_json_value(item) for item in value]
    return value
