"""Core lifecycle domain logic — pure state machine rules and validation.

Extracted from gzkit.lifecycle. Contains transition tables, validation
functions, and the InvalidTransitionError exception. No I/O operations.
"""

from __future__ import annotations

from pydantic import BaseModel, ConfigDict, Field


class InvalidTransitionError(Exception):
    """Raised when an invalid lifecycle state transition is attempted."""

    def __init__(
        self,
        content_type: str,
        from_state: str,
        to_state: str,
        allowed: list[str] | None = None,
    ) -> None:
        """Initialize with transition details."""
        self.content_type = content_type
        self.from_state = from_state
        self.to_state = to_state
        self.allowed = allowed or []
        allowed_str = ", ".join(self.allowed) if self.allowed else "none"
        super().__init__(
            f"Invalid transition for {content_type}: "
            f"{from_state!r} -> {to_state!r} "
            f"(allowed from {from_state!r}: {allowed_str})"
        )


class TransitionRule(BaseModel):
    """A single allowed state transition."""

    model_config = ConfigDict(frozen=True, extra="forbid")

    from_state: str = Field(..., description="Source state")
    to_state: str = Field(..., description="Target state")


# ---------------------------------------------------------------------------
# Per-content-type transition tables
# ---------------------------------------------------------------------------

ADR_TRANSITIONS: list[TransitionRule] = [
    TransitionRule(from_state="Pool", to_state="Draft"),
    TransitionRule(from_state="Draft", to_state="Proposed"),
    TransitionRule(from_state="Proposed", to_state="Accepted"),
    TransitionRule(from_state="Accepted", to_state="Completed"),
    TransitionRule(from_state="Completed", to_state="Validated"),
    # Backward transitions for rejection/rework
    TransitionRule(from_state="Proposed", to_state="Draft"),
    # Deprecation/supersession from any active state
    TransitionRule(from_state="Draft", to_state="Superseded"),
    TransitionRule(from_state="Proposed", to_state="Superseded"),
    TransitionRule(from_state="Accepted", to_state="Superseded"),
    TransitionRule(from_state="Draft", to_state="Deprecated"),
    TransitionRule(from_state="Proposed", to_state="Deprecated"),
    TransitionRule(from_state="Accepted", to_state="Deprecated"),
]

OBPI_TRANSITIONS: list[TransitionRule] = [
    TransitionRule(from_state="Draft", to_state="Active"),
    TransitionRule(from_state="Active", to_state="Completed"),
    TransitionRule(from_state="Draft", to_state="Abandoned"),
    TransitionRule(from_state="Active", to_state="Abandoned"),
]

PRD_TRANSITIONS: list[TransitionRule] = [
    TransitionRule(from_state="Draft", to_state="Review"),
    TransitionRule(from_state="Review", to_state="Approved"),
    TransitionRule(from_state="Review", to_state="Draft"),
    TransitionRule(from_state="Approved", to_state="Superseded"),
]

CONSTITUTION_TRANSITIONS: list[TransitionRule] = [
    TransitionRule(from_state="Draft", to_state="Ratified"),
    TransitionRule(from_state="Ratified", to_state="Amended"),
    TransitionRule(from_state="Amended", to_state="Superseded"),
    TransitionRule(from_state="Ratified", to_state="Superseded"),
]

RULE_TRANSITIONS: list[TransitionRule] = [
    TransitionRule(from_state="Active", to_state="Deprecated"),
]

SKILL_TRANSITIONS: list[TransitionRule] = [
    TransitionRule(from_state="draft", to_state="active"),
    TransitionRule(from_state="active", to_state="deprecated"),
    TransitionRule(from_state="deprecated", to_state="retired"),
]

# Master mapping of content type name → transition rules
TRANSITION_TABLES: dict[str, list[TransitionRule]] = {
    "ADR": ADR_TRANSITIONS,
    "OBPI": OBPI_TRANSITIONS,
    "PRD": PRD_TRANSITIONS,
    "Constitution": CONSTITUTION_TRANSITIONS,
    "Rule": RULE_TRANSITIONS,
    "Skill": SKILL_TRANSITIONS,
}


def get_allowed_transitions(content_type: str, from_state: str) -> list[str]:
    """Return the list of states reachable from ``from_state`` for the given content type."""
    rules = TRANSITION_TABLES.get(content_type, [])
    return [r.to_state for r in rules if r.from_state == from_state]


def is_valid_transition(content_type: str, from_state: str, to_state: str) -> bool:
    """Check whether a transition is allowed without raising."""
    return to_state in get_allowed_transitions(content_type, from_state)


def get_all_states(content_type: str) -> list[str]:
    """Return all unique states defined for a content type's transition table."""
    rules = TRANSITION_TABLES.get(content_type, [])
    seen: set[str] = set()
    ordered: list[str] = []
    for rule in rules:
        for state in (rule.from_state, rule.to_state):
            if state not in seen:
                seen.add(state)
                ordered.append(state)
    return ordered
