"""Session-attach fixture builders for terminal/transport matrix coverage.

Spec refs:
- docs/proposals/execution-architecture-refactor.md#tests-required/session-runtime-tests
- docs/proposals/execution-architecture-refactor.md#execution-contract-specific-design-rules/lifecycle-semantics
- docs/proposals/execution-architecture-refactor.md#execution-contract-specific-design-rules/persona-provenance-and-subagent-inheritance
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Literal

from anima.core.schemas import AgentState

AttachSignal = Literal[
    "attach_ok",
    "terminal_state",
    "socket_missing",
    "transport_missing",
]


@dataclass(frozen=True)
class SessionAttachMatrixCase:
    """One attach case spanning lifecycle state + socket + transport observability."""

    case_id: str
    state: AgentState
    socket_present: bool
    transport_present: bool
    expected_signal: AttachSignal


@dataclass(frozen=True)
class DaemonExplicitAttachabilityFixture:
    """Fixture for daemon + explicit-completion attachability across no-tool turns."""

    lifecycle: Literal["daemon"]
    completion_policy: Literal["explicit"]
    no_tool_turn_prompts: tuple[str, ...]
    expected_attachable: bool


@dataclass(frozen=True)
class TalkChatParityRule:
    """Shared attach rule that both talk and chat adapters must honor."""

    rule_id: str
    expected_signal: AttachSignal
    rationale: str


@dataclass(frozen=True)
class ParentSpawnabilityCase:
    """Post-launch parent spawnability fixture for inline/synthesized personas."""

    case_id: str
    parent_source: Literal["inline", "synthesized"]
    materialized: bool
    expected_outcome: Literal["materialized_child_success", "spawnability_error"]
    expected_error_code: str | None


def _expected_attach_signal(
    *, state: AgentState, socket_present: bool, transport_present: bool
) -> AttachSignal:
    if state in {AgentState.completed, AgentState.failed, AgentState.killed}:
        return "terminal_state"
    if not socket_present:
        return "socket_missing"
    if not transport_present:
        return "transport_missing"
    return "attach_ok"


def make_session_attach_matrix_cases() -> tuple[SessionAttachMatrixCase, ...]:
    """Build running/terminal x socket/transport matrix for attach semantics."""
    states = (
        AgentState.running,
        AgentState.completed,
        AgentState.failed,
        AgentState.killed,
    )
    matrix: list[SessionAttachMatrixCase] = []
    for state in states:
        for socket_present in (False, True):
            for transport_present in (False, True):
                matrix.append(
                    SessionAttachMatrixCase(
                        case_id=(
                            f"{state.value}-socket-"
                            f"{'present' if socket_present else 'missing'}-transport-"
                            f"{'present' if transport_present else 'missing'}"
                        ),
                        state=state,
                        socket_present=socket_present,
                        transport_present=transport_present,
                        expected_signal=_expected_attach_signal(
                            state=state,
                            socket_present=socket_present,
                            transport_present=transport_present,
                        ),
                    )
                )
    return tuple(matrix)


def make_daemon_explicit_attachability_fixture(
    no_tool_turn_count: int = 3,
) -> DaemonExplicitAttachabilityFixture:
    """Build fixture for daemon + explicit completion across no-tool turns."""
    prompts = tuple(f"turn-{idx}: no tools" for idx in range(1, no_tool_turn_count + 1))
    return DaemonExplicitAttachabilityFixture(
        lifecycle="daemon",
        completion_policy="explicit",
        no_tool_turn_prompts=prompts,
        expected_attachable=True,
    )


def make_talk_chat_parity_rules() -> tuple[TalkChatParityRule, ...]:
    """Build shared attach rules consumed by both talk/chat fixture tests."""
    return (
        TalkChatParityRule(
            rule_id="terminal-state-outranks-transport-failure",
            expected_signal="terminal_state",
            rationale=(
                "terminal attach semantics require reporting completed/failed/killed "
                "before missing socket/transport failures"
            ),
        ),
        TalkChatParityRule(
            rule_id="running-missing-socket-is-socket-error",
            expected_signal="socket_missing",
            rationale=(
                "running attach path must surface socket readiness failure when transport "
                "endpoint is absent"
            ),
        ),
    )


def make_parent_spawnability_cases() -> tuple[ParentSpawnabilityCase, ...]:
    """Build inline/synthesized parent spawnability outcomes after launch."""
    return (
        ParentSpawnabilityCase(
            case_id="inline-materialized-child-success",
            parent_source="inline",
            materialized=True,
            expected_outcome="materialized_child_success",
            expected_error_code=None,
        ),
        ParentSpawnabilityCase(
            case_id="inline-unmaterialized-structured-failure",
            parent_source="inline",
            materialized=False,
            expected_outcome="spawnability_error",
            expected_error_code="inline_persona_requires_materialization",
        ),
        ParentSpawnabilityCase(
            case_id="synthesized-materialized-child-success",
            parent_source="synthesized",
            materialized=True,
            expected_outcome="materialized_child_success",
            expected_error_code=None,
        ),
        ParentSpawnabilityCase(
            case_id="synthesized-unmaterialized-structured-failure",
            parent_source="synthesized",
            materialized=False,
            expected_outcome="spawnability_error",
            expected_error_code="synthesized_persona_requires_materialization",
        ),
    )


__all__ = [
    "AttachSignal",
    "DaemonExplicitAttachabilityFixture",
    "ParentSpawnabilityCase",
    "SessionAttachMatrixCase",
    "TalkChatParityRule",
    "make_daemon_explicit_attachability_fixture",
    "make_parent_spawnability_cases",
    "make_session_attach_matrix_cases",
    "make_talk_chat_parity_rules",
]
