"""Trainer assignment and session values for weekly operations."""

from __future__ import annotations

from datetime import date, time
from enum import StrEnum
from typing import ClassVar

from pydantic import AliasChoices, BaseModel, ConfigDict, Field, model_validator


class TrainerIntensity(StrEnum):
    BELOW = "below"
    AS_PLANNED = "as_planned"
    ABOVE = "above"


_CARB_LOAD: dict[TrainerIntensity, str] = {
    TrainerIntensity.BELOW: "low",
    TrainerIntensity.AS_PLANNED: "medium",
    TrainerIntensity.ABOVE: "high",
}


class TrainerSessionPayload(BaseModel):
    """Typed payload for one bounded trainer-session record."""

    model_config: ClassVar[ConfigDict] = ConfigDict(
        frozen=True, extra="forbid", populate_by_name=True
    )

    session_done: bool = Field(validation_alias=AliasChoices("session_done", "done"))
    workout_summary: str = Field(default="", max_length=4000)
    performance_1to5: int = Field(
        ge=1,
        le=5,
        validation_alias=AliasChoices("performance_1to5", "performance"),
    )
    intensity_vs_plan: TrainerIntensity = Field(
        validation_alias=AliasChoices("intensity_vs_plan", "intensity"),
    )
    pain_summary: str = Field(
        max_length=2000,
        validation_alias=AliasChoices("pain_summary", "pain"),
    )
    operator_note: str = Field(default="", max_length=2000)

    @property
    def done(self) -> bool:
        return self.session_done

    @property
    def performance(self) -> int:
        return self.performance_1to5

    @property
    def intensity(self) -> TrainerIntensity:
        return self.intensity_vs_plan

    @property
    def pain(self) -> str:
        return self.pain_summary

    @property
    def carb_load(self) -> str | None:
        return _CARB_LOAD[self.intensity_vs_plan] if self.session_done else None


class TrainerAssignment(BaseModel):
    """One exact trainer Telegram route assigned to a customer."""

    model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True, extra="forbid")

    user_id: str = Field(min_length=1, max_length=64)
    chat_id: str = Field(min_length=1, max_length=64)
    topic_id: str = Field(min_length=1, max_length=64)

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

    @property
    def space_key(self) -> tuple[str, str]:
        return (self.chat_id, self.topic_id)


class TrainerScheduleError(ValueError):
    """A dated trainer schedule entry is internally inconsistent."""


class CustomerTrainingScheduleEntry(BaseModel):
    """One dated training session used by deterministic adaptive planning."""

    model_config: ClassVar[ConfigDict] = ConfigDict(frozen=True, extra="forbid")

    date: date
    weekday: int = Field(ge=0, le=6)
    time: time
    load_category: str = Field(min_length=1, max_length=32)

    @model_validator(mode="after")
    def require_matching_weekday(self) -> CustomerTrainingScheduleEntry:
        if self.date.weekday() != self.weekday:
            raise TrainerScheduleError("training schedule weekday does not match date")
        if self.load_category not in {"low", "medium", "high", "rest"}:
            raise TrainerScheduleError("training schedule load category is invalid")
        return self

    @property
    def kst_day(self) -> date:
        return self.date

    @property
    def training_time(self) -> time:
        return self.time
