"""Typed, default-off configuration for NutriCoach weekly operations."""

from __future__ import annotations

import hashlib
import json
from collections.abc import Mapping, Sequence
from dataclasses import dataclass
from typing import ClassVar, Final, TypeAlias

from pydantic import BaseModel, ConfigDict, StrictBool, StrictInt, StrictStr, ValidationError
from typing_extensions import TypeIs, override


JsonValue: TypeAlias = str | int | float | bool | None | Mapping[str, "JsonValue"] | Sequence["JsonValue"]
WEEKLY_OPERATIONS_FEATURE_EPOCH: Final = "weekly-operations-v1"
DEFAULT_REMINDER_TIME: Final = "20:00:00"
DEFAULT_MISSED_CUTOFF_TIME: Final = "23:00:00"
DEFAULT_WEEKLY_WEEKDAY: Final = 0
_CONFIG_SCHEMA: Final = "nutricoach-weekly-operations-config-v1"


@dataclass(frozen=True, slots=True)
class WeeklyOperationsConfigError(Exception):
    """The weekly-operations configuration crossed its trust boundary invalidly."""

    reason: str

    @override
    def __str__(self) -> str:
        return f"weekly operations config is invalid: {self.reason}"


@dataclass(frozen=True, slots=True)
class WeeklyOperationsAuthorityError(Exception):
    """The weekly-operations authority receipt crossed its trust boundary invalidly."""

    reason: str

    @override
    def __str__(self) -> str:
        return f"weekly operations authority is invalid: {self.reason}"


class _WeeklyOperationsInput(BaseModel):
    model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True, extra="forbid", strict=True)

    enabled: StrictBool = False
    reminder_time: StrictStr = DEFAULT_REMINDER_TIME
    missed_cutoff_time: StrictStr = DEFAULT_MISSED_CUTOFF_TIME
    weekly_weekday: StrictInt = DEFAULT_WEEKLY_WEEKDAY
    feature_epoch: StrictStr = WEEKLY_OPERATIONS_FEATURE_EPOCH
    registry_identity_binding_digest: StrictStr | None = None


class _ReviewRouteInput(BaseModel):
    model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True, extra="forbid", strict=True)

    user_id: StrictStr
    chat_id: StrictStr
    topic_id: StrictInt | StrictStr


@dataclass(frozen=True, slots=True)
class WeeklyOperationsReviewRoute:
    """The fixed Topic-59 operator route; this capability never generalizes it."""

    user_id: str
    chat_id: str

    @property
    def key(self) -> tuple[str, str, str]:
        return self.user_id, self.chat_id, "59"


@dataclass(frozen=True, slots=True)
class WeeklyOperationsConfig:
    """Validated recurring KST schedule and bound review route for v1.4."""

    enabled: bool = False
    reminder_time: str = DEFAULT_REMINDER_TIME
    missed_cutoff_time: str = DEFAULT_MISSED_CUTOFF_TIME
    weekly_weekday: int = DEFAULT_WEEKLY_WEEKDAY
    review_route: WeeklyOperationsReviewRoute | None = None
    feature_epoch: str = WEEKLY_OPERATIONS_FEATURE_EPOCH
    registry_identity_binding_digest: str | None = None

    def __post_init__(self) -> None:
        if type(self.enabled) is not bool:
            raise WeeklyOperationsConfigError("enabled")
        if self.reminder_time >= self.missed_cutoff_time:
            raise WeeklyOperationsConfigError("reminder must precede cutoff")
        if self.reminder_time != DEFAULT_REMINDER_TIME:
            raise WeeklyOperationsConfigError("reminder time")
        if self.missed_cutoff_time != DEFAULT_MISSED_CUTOFF_TIME:
            raise WeeklyOperationsConfigError("missed cutoff time")
        if self.weekly_weekday != DEFAULT_WEEKLY_WEEKDAY:
            raise WeeklyOperationsConfigError("weekly weekday")
        if self.feature_epoch != WEEKLY_OPERATIONS_FEATURE_EPOCH:
            raise WeeklyOperationsConfigError("feature epoch")
        if self.enabled and self.review_route is None:
            raise WeeklyOperationsConfigError("Topic-59 review route")
        identity = self.registry_identity_binding_digest
        if self.enabled and (
            identity is None
            or len(identity) != 64
            or any(character not in "0123456789abcdef" for character in identity)
        ):
            raise WeeklyOperationsConfigError("registry identity digest")

    @property
    def is_compiled(self) -> bool:
        """Report whether this config is for the compiled v1.4 capability epoch."""
        return self.feature_epoch == WEEKLY_OPERATIONS_FEATURE_EPOCH

    @property
    def digest(self) -> str:
        """Return the canonical digest bound into a separate authority receipt."""
        route = None if self.review_route is None else self.review_route.key
        encoded = json.dumps(
            {
                "schema": _CONFIG_SCHEMA,
                "enabled": self.enabled,
                "reminder_time": self.reminder_time,
                "missed_cutoff_time": self.missed_cutoff_time,
                "weekly_weekday": self.weekly_weekday,
                "review_route": route,
                "feature_epoch": self.feature_epoch,
                "registry_identity_binding_digest": self.registry_identity_binding_digest,
            },
            ensure_ascii=False,
            sort_keys=True,
            separators=(",", ":"),
        ).encode("utf-8")
        return hashlib.sha256(encoded).hexdigest()


def parse_weekly_operations_config(extra: Mapping[str, JsonValue]) -> WeeklyOperationsConfig:
    """Parse only the v1.4 feature section; absent configuration remains inert."""
    nutrition = extra.get("nutrition_coaching")
    if nutrition is None:
        return WeeklyOperationsConfig()
    if not _is_json_mapping(nutrition):
        raise WeeklyOperationsConfigError("nutrition_coaching")
    if "weekly_operations" not in nutrition:
        return WeeklyOperationsConfig()
    weekly = nutrition["weekly_operations"]
    if not _is_json_mapping(weekly):
        raise WeeklyOperationsConfigError("weekly_operations")
    try:
        parsed = _WeeklyOperationsInput.model_validate(weekly)
    except ValidationError as error:
        raise WeeklyOperationsConfigError("weekly_operations shape") from error
    if parsed.enabled and not {"reminder_time", "missed_cutoff_time", "weekly_weekday"} <= set(weekly):
        raise WeeklyOperationsConfigError("enabled schedule fields")
    route = _parse_review_route(nutrition) if parsed.enabled else None
    return WeeklyOperationsConfig(
        enabled=parsed.enabled,
        reminder_time=parsed.reminder_time,
        missed_cutoff_time=parsed.missed_cutoff_time,
        weekly_weekday=parsed.weekly_weekday,
        review_route=route,
        feature_epoch=parsed.feature_epoch,
        registry_identity_binding_digest=parsed.registry_identity_binding_digest,
    )


def _is_json_mapping(value: JsonValue | None) -> TypeIs[Mapping[str, JsonValue]]:
    return isinstance(value, Mapping)


def _parse_review_route(nutrition: Mapping[str, JsonValue]) -> WeeklyOperationsReviewRoute:
    route = nutrition.get("operator_review")
    if not _is_json_mapping(route):
        raise WeeklyOperationsConfigError("Topic-59 review route")
    try:
        parsed = _ReviewRouteInput.model_validate(route)
    except ValidationError as error:
        raise WeeklyOperationsConfigError("Topic-59 review route") from error
    user_id = parsed.user_id.strip()
    chat_id = parsed.chat_id.strip()
    if not user_id or not chat_id or str(parsed.topic_id) != "59":
        raise WeeklyOperationsConfigError("Topic-59 review route")
    return WeeklyOperationsReviewRoute(user_id=user_id, chat_id=chat_id)
