"""Pure unit tests for extracted CLI helper functions.

Spec source: user task for step `testability.add-extraction-unit-tests`,
requiring deterministic tests for monitor_serve.py and talk_cmd.py helpers.
"""

from anima.core.schemas import AgentState, AgentStateData, TokenUsage
from anima.shell.cli_commands._shared import shortest_unique_agent_id_prefixes
from anima.shell.cli_commands.monitor_serve import (
    count_agent_states,
    format_agent_row,
    format_summary_row,
)
from anima.shell.cli_commands.talk_helpers import parse_talk_response
from anima.shell.cli_commands.talk_cmd import (
    build_socket_path,
    categorize_agent_state,
    validate_interactive_state,
)


def _agent(
    instance_id: str,
    state: AgentState,
    *,
    persona_name: str = "demo-persona",
    created_at: str | None = "2026-03-02T10:11:12Z",
    pid: int | None = 4242,
    token_usage: TokenUsage | None = None,
) -> AgentStateData:
    return AgentStateData(
        instance_id=instance_id,
        persona_name=persona_name,
        trace_id=f"trace-{instance_id}",
        state=state,
        created_at=created_at,
        pid=pid,
        token_usage=token_usage or TokenUsage(),
    )


def test_count_agent_states_empty() -> None:
    assert count_agent_states([]) == {}


def test_count_agent_states_mixed() -> None:
    agents = [
        _agent("inst-agent-a", AgentState.running),
        _agent("inst-agent-b", AgentState.pending),
        _agent("inst-agent-c", AgentState.running),
        _agent("inst-agent-d", AgentState.failed),
    ]

    assert count_agent_states(agents) == {"running": 2, "pending": 1, "failed": 1}


def test_format_agent_row_minimal() -> None:
    agent = _agent(
        "inst-shortid",
        AgentState.pending,
        persona_name="",
        created_at=None,
        pid=None,
        token_usage=TokenUsage(),
    )

    row = format_agent_row(agent, {"pending": "dim"})

    # With instance_id, the row shows "inst-shortid" not just "shortid"
    assert row[0].endswith("shortid")  # Contains the instance_id suffix
    assert row[1] == "[dim]pending[/dim]"
    assert row[2] == "N/A"


def test_format_agent_row_with_tokens() -> None:
    agent = _agent(
        "inst-agent-123456789",
        AgentState.running,
        persona_name="very-long-persona-name",
        token_usage=TokenUsage(prompt_tokens=120, completion_tokens=30),
    )

    row = format_agent_row(agent, {"running": "green"})

    assert "inst-agent-123456789" == row[0]  # shows full instance_id
    assert row[1] == "[green]running[/green]"
    assert row[2] == "very-long-pe"
    assert row[5] == "150"


def test_shortest_unique_prefixes_distinguish_shared_prefix_ids() -> None:
    """Spec source: ux-polish.fix-id-truncation requires distinct displayed IDs."""
    instance_ids = [
        "inst_abcdefghijklmnopqrstuv_01",
        "inst_abcdefghijklmnopqrstuv_02",
        "inst_abcdefghijklmnopqzzzzz_03",
    ]

    display_map = shortest_unique_agent_id_prefixes(instance_ids)
    display_ids = [display_map[instance_id] for instance_id in instance_ids]

    assert len(set(display_ids)) == len(instance_ids)
    assert all(len(display_id) >= 12 for display_id in display_ids)


def test_monitor_rows_do_not_duplicate_when_prefixes_collide() -> None:
    """Spec source: ux-polish.fix-id-truncation forbids truncation-caused duplicate rows."""
    shared_prefix_agents = [
        _agent("inst_abcdefghijklmnopqrstuv_01", AgentState.running),
        _agent("inst_abcdefghijklmnopqrstuv_02", AgentState.running),
    ]
    display_map = shortest_unique_agent_id_prefixes(
        [agent.instance_id for agent in shared_prefix_agents]
    )

    rows = [
        format_agent_row(
            agent,
            {"running": "green"},
            display_agent_id=display_map[agent.instance_id],
        )
        for agent in shared_prefix_agents
    ]

    display_ids = [row[0] for row in rows]
    assert len(set(display_ids)) == len(rows)


def test_format_summary_row() -> None:
    summary = format_summary_row({"running": 2, "pending": 1, "failed": 3}, total=6)

    assert summary == (
        "[green]running:2[/green] | [dim]pending:1[/dim] | [red]failed:3[/red]"
    )


def test_categorize_agent_state_interactive() -> None:
    assert categorize_agent_state(AgentState.awaiting_approval) == "interactive"


def test_categorize_agent_state_terminal() -> None:
    assert categorize_agent_state(AgentState.completed) == "terminal"


def test_categorize_agent_state_pending() -> None:
    assert categorize_agent_state(AgentState.pending) == "non_interactive"


def test_parse_talk_response_simple() -> None:
    parsed = parse_talk_response('{"response":"hello","turn_number":7}')
    assert parsed == {"response": "hello", "turn_number": "7"}


def test_parse_talk_response_structured() -> None:
    parsed = parse_talk_response('{"response":{"text":"ok"},"turn_number":null}')
    assert parsed == {"response": "{'text': 'ok'}", "turn_number": None}


def test_build_socket_path() -> None:
    assert (
        build_socket_path("/tmp/anima-state", "agent-xyz")
        == "/tmp/anima-state/agents/agent-xyz/mcp.sock"
    )


def test_validate_interactive_state_running() -> None:
    assert validate_interactive_state(AgentState.running) == (True, "")


def test_validate_interactive_state_failed() -> None:
    assert validate_interactive_state(AgentState.failed) == (
        False,
        "Agent is in terminal state: failed",
    )
