"""Finite-code model boundary for weekly owner drafts."""

from __future__ import annotations

import hashlib
import json
from collections.abc import Mapping
from typing import ClassVar, Protocol, TypeAlias

from pydantic import BaseModel, ConfigDict, ValidationError

from checkin_cli.weekly_operations_grounding import GroundedWeeklyFacts
from checkin_cli.weekly_operations_knowledge import (
    WeeklyEmphasis,
    WeeklyKnowledgeError,
    WeeklyPrincipleId,
    VerifiedWeeklyPublicKnowledge,
)

JsonScalar: TypeAlias = str | int | float | bool | None
JsonValue: TypeAlias = JsonScalar | dict[str, "JsonValue"] | list["JsonValue"]


class WeeklyExplanationModel(Protocol):
    def generate(self, request: Mapping[str, JsonValue]) -> Mapping[str, JsonValue]: ...


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

    grounding_digest: str
    principle_ids: list[WeeklyPrincipleId]
    emphasis: WeeklyEmphasis


def weekly_model_request(
    facts: GroundedWeeklyFacts,
    knowledge: VerifiedWeeklyPublicKnowledge,
    *,
    correction: bool,
) -> dict[str, JsonValue]:
    if not knowledge.verify():
        raise WeeklyKnowledgeError("unverified request knowledge")
    principles: list[JsonValue] = [
        {"principle_id": item.principle_id.value, "approved_text": item.approved_text}
        for item in knowledge.principles
    ]
    return {
        "schema": "nutricoach-weekly-principle-selection-v1",
        "correction": correction,
        "grounding_digest": facts.grounding_digest,
        "locked_facts": facts.machine_payload(),
        "principles": principles,
        "allowed_emphasis": [item.value for item in WeeklyEmphasis],
        "output_fields": ["grounding_digest", "principle_ids", "emphasis"],
    }


def parse_weekly_model_selection(
    raw: Mapping[str, JsonValue],
    facts: GroundedWeeklyFacts,
    knowledge: VerifiedWeeklyPublicKnowledge,
) -> WeeklyModelSelection | None:
    if not knowledge.verify():
        raise WeeklyKnowledgeError("unverified response knowledge")
    try:
        selected = WeeklyModelSelection.model_validate(raw)
    except ValidationError:
        return None
    allowed = {item.principle_id for item in knowledge.principles}
    if (
        selected.grounding_digest != facts.grounding_digest
        or not 1 <= len(selected.principle_ids) <= 2
        or len(set(selected.principle_ids)) != len(selected.principle_ids)
        or not set(selected.principle_ids) <= allowed
    ):
        return None
    return selected


def weekly_model_request_digest(request: dict[str, JsonValue]) -> str:
    encoded = json.dumps(request, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
    return hashlib.sha256(encoded.encode()).hexdigest()


def weekly_model_request_is_safe(value: JsonValue) -> bool:
    if value is None or type(value) in {str, int, float, bool}:
        return not isinstance(value, str) or len(value) <= 600
    if type(value) is dict:
        allowed_keys = {
            "schema", "correction", "grounding_digest", "locked_facts", "principles", "principle_id",
            "approved_text", "allowed_emphasis", "output_fields", *facts_keys(),
        }
        return set(value) <= allowed_keys and all(weekly_model_request_is_safe(item) for item in value.values())
    if type(value) is list:
        return len(value) <= 64 and all(weekly_model_request_is_safe(item) for item in value)
    return False


def facts_keys() -> frozenset[str]:
    return frozenset({
        "starts_on", "ends_on", "submitted_count", "late_count", "missed_count",
        "completed_days", "calendar_days", "adherence_percent", "weight_trend",
        "reminder_sent_count", "reminder_incident_count", "prior_week_comparison",
        "week_status", "decision_options", "decision", "actions",
        "source_aggregate_digest", "candidate_digest", "config_digest",
        "customer_authority_digest", "bound_summary_authority_digest",
        "owner_route_digest", "consent_digest",
    })
