"""Subject state enum SOT + visual descriptor lookup (Area #2 Q2 + Q6).

closed-world enum only — LLM producer (shot_staging v13) emit subject_state,
consumer = enum check via is_immobilized_state / get_visual_descriptor.
regex / substring / NL fallback 금지 (Gate 1).

is_immobilized_state silent False (consumer side fail-fast = schema validation).
validate_subject_state = defense in depth (fixture / corrupt CP / migration tool).
"""
from __future__ import annotations

from app.core.errors import AppError


SUBJECT_STATES: tuple[str, ...] = ("alive", "unconscious", "dead", "severely_injured")
IMMOBILIZED_STATES: frozenset[str] = frozenset({"unconscious", "dead", "severely_injured"})

SUBJECT_STATE_VISUAL_DESCRIPTOR: dict[str, str] = {
    "dead": "lying motionless, pale/ashen skin, eyes fully closed, slack facial muscles, no signs of life",
    "severely_injured": "visible bruises and cuts, bloodied areas on face or clothing, pained or grimacing expression, disheveled appearance",
    "unconscious": "eyes closed, slack facial features, limp posture, head tilted to one side",
}


def is_immobilized_state(state: str) -> bool:
    return state in IMMOBILIZED_STATES


def validate_subject_state(state: str, where: str) -> None:
    if state not in SUBJECT_STATES:
        raise AppError(
            "step.contract_violation.subject_state.enum",
            f"{where}: invalid subject_state={state!r}, expected one of {SUBJECT_STATES}",
        )


def get_visual_descriptor(state: str) -> str:
    if state not in SUBJECT_STATE_VISUAL_DESCRIPTOR:
        raise AppError(
            "step.contract_violation.subject_state.visual_descriptor_not_defined",
            f"subject_state={state!r} has no visual descriptor (valid: {sorted(SUBJECT_STATE_VISUAL_DESCRIPTOR.keys())})",
        )
    return SUBJECT_STATE_VISUAL_DESCRIPTOR[state]
