"""Step definitions for tui_commands_coverage.feature.

Targets uncovered lines in cleveragents.tui.commands:
- Lines 22-31: TuiCommandRouter.handle() method
- Lines 34-42: TuiCommandRouter._persona_command() method
- Lines 46-48: TuiCommandRouter._session_command() method
- Lines 51-64: run_tui() function (headless and non-headless)
"""

import json
from dataclasses import dataclass, field
from io import StringIO
from pathlib import Path
from unittest.mock import MagicMock, patch

from behave import given, then, when

from cleveragents.tui.commands import TuiCommandRouter, run_tui


# ---------------------------------------------------------------------------
# Lightweight fakes for PersonaRegistry and PersonaState
# ---------------------------------------------------------------------------
@dataclass
class FakePersona:
    """Minimal persona stand-in."""

    name: str


@dataclass
class FakePersonaRegistry:
    """Registry that returns a canned list of personas."""

    _personas: list[FakePersona] = field(default_factory=list)
    personas_dir: Path = field(default_factory=lambda: Path("/tmp/fake-personas"))

    def list_personas(self) -> list[FakePersona]:
        return list(self._personas)


@dataclass
class FakePersonaState:
    """State that tracks set_active_persona calls."""

    _active: dict[str, FakePersona] = field(default_factory=dict)

    def set_active_persona(self, session_id: str, name: str) -> FakePersona:
        persona = FakePersona(name=name)
        self._active[session_id] = persona
        return persona

    def active_name(self, session_id: str) -> str:
        p = self._active.get(session_id)
        return p.name if p else "default"


# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------
@given("the TUI commands module is imported")
def step_tui_commands_imported(context):
    """Verify the commands module is importable."""
    assert TuiCommandRouter is not None
    assert run_tui is not None


# ---------------------------------------------------------------------------
# Router construction helpers
# ---------------------------------------------------------------------------
@given("a TuiCommandRouter with a mock registry and state")
def step_router_with_mock_deps(context):
    """Create a router with an empty registry and basic state."""
    context.registry = FakePersonaRegistry()
    context.state = FakePersonaState()
    context.router = TuiCommandRouter(
        persona_registry=context.registry,  # type: ignore[arg-type]
        persona_state=context.state,  # type: ignore[arg-type]
    )


@given("a TuiCommandRouter with a mock registry and state that supports set")
def step_router_with_set_support(context):
    """Create a router whose state can set an active persona."""
    context.registry = FakePersonaRegistry()
    context.state = FakePersonaState()
    context.router = TuiCommandRouter(
        persona_registry=context.registry,  # type: ignore[arg-type]
        persona_state=context.state,  # type: ignore[arg-type]
    )


@given("a TuiCommandRouter with a mock registry containing two personas")
def step_router_with_two_personas(context):
    """Create a router whose registry returns two personas."""
    context.registry = FakePersonaRegistry(
        _personas=[FakePersona(name="Alice"), FakePersona(name="Bob")]
    )
    context.state = FakePersonaState()
    context.router = TuiCommandRouter(
        persona_registry=context.registry,  # type: ignore[arg-type]
        persona_state=context.state,  # type: ignore[arg-type]
    )


@given("a TuiCommandRouter with an empty mock registry")
def step_router_with_empty_registry(context):
    """Create a router whose registry returns no personas."""
    context.registry = FakePersonaRegistry(_personas=[])
    context.state = FakePersonaState()
    context.router = TuiCommandRouter(
        persona_registry=context.registry,  # type: ignore[arg-type]
        persona_state=context.state,  # type: ignore[arg-type]
    )


# ---------------------------------------------------------------------------
# handle() invocation
# ---------------------------------------------------------------------------
@when('I call handle with raw input "{raw}"')
def step_call_handle(context, raw):
    """Invoke TuiCommandRouter.handle() with the given raw string."""
    context.handle_result = context.router.handle(raw, session_id="test-session")


# ---------------------------------------------------------------------------
# handle() assertions
# ---------------------------------------------------------------------------
@then('the handle result should be "{expected}"')
def step_handle_result_exact(context, expected):
    assert context.handle_result == expected, (
        f"Expected {expected!r}, got {context.handle_result!r}"
    )


@then("the handle result should contain persona listing")
def step_handle_result_persona_listing(context):
    """An empty registry returns 'No personas'."""
    assert context.handle_result == "No personas" or isinstance(
        context.handle_result, str
    )


@then('the handle result should start with "{prefix}"')
def step_handle_result_starts_with(context, prefix):
    assert context.handle_result.startswith(prefix), (
        f"Expected result to start with {prefix!r}, got {context.handle_result!r}"
    )


@then('the handle result should contain "{substring}"')
def step_handle_result_contains(context, substring):
    assert substring in context.handle_result, (
        f"Expected {substring!r} in result, got {context.handle_result!r}"
    )


# ---------------------------------------------------------------------------
# run_tui() headless scenario
# ---------------------------------------------------------------------------
@when("I call run_tui in headless mode with mocked dependencies")
def step_run_tui_headless(context):
    """Call run_tui(headless=True) with fully mocked container."""
    fake_personas_dir = Path("/tmp/fake-tui-headless-personas")

    mock_registry = MagicMock()
    mock_registry.list_personas.return_value = []
    mock_registry.personas_dir = fake_personas_dir
    mock_registry.ensure_default.return_value = FakePersona(name="default")
    mock_registry.get_last_persona.return_value = None

    mock_state = MagicMock()
    mock_state.active_name.return_value = "default"

    mock_container = MagicMock()
    mock_container.persona_registry.return_value = mock_registry
    mock_container.persona_state.return_value = mock_state

    captured = StringIO()
    with (
        patch("cleveragents.tui.commands.get_container", return_value=mock_container),
        patch("cleveragents.tui.commands.PersonaRegistry", return_value=mock_registry),
        patch("cleveragents.tui.commands.PersonaState", return_value=mock_state),
        patch("builtins.print", side_effect=lambda *a, **kw: captured.write(str(a[0]))),
    ):
        context.run_tui_rc = run_tui(headless=True)
    context.run_tui_output = captured.getvalue()


@when("I call run_tui in non-headless mode with mocked dependencies")
def step_run_tui_non_headless(context):
    """Call run_tui(headless=False) with a mocked app."""
    mock_registry = MagicMock()
    mock_state = MagicMock()

    mock_container = MagicMock()
    mock_container.persona_registry.return_value = mock_registry
    mock_container.persona_state.return_value = mock_state

    mock_app = MagicMock()
    mock_app.run = MagicMock()

    with (
        patch("cleveragents.tui.commands.get_container", return_value=mock_container),
        patch("cleveragents.tui.commands.PersonaRegistry", return_value=mock_registry),
        patch("cleveragents.tui.commands.PersonaState", return_value=mock_state),
        patch(
            "cleveragents.tui.commands.CleverAgentsTuiApp",
            return_value=mock_app,
        ),
    ):
        context.run_tui_rc = run_tui(headless=False)
    context.mock_app = mock_app


# ---------------------------------------------------------------------------
# run_tui() assertions
# ---------------------------------------------------------------------------
@then("the run_tui return code should be 0")
def step_verify_run_tui_rc(context):
    assert context.run_tui_rc == 0, f"Expected return code 0, got {context.run_tui_rc}"


@then("the printed JSON should contain textual_available key")
def step_verify_json_textual_available(context):
    payload = json.loads(context.run_tui_output)
    assert "textual_available" in payload, (
        f"Expected 'textual_available' key in {payload}"
    )


@then("the printed JSON should contain help key")
def step_verify_json_help(context):
    payload = json.loads(context.run_tui_output)
    assert "help" in payload, f"Expected 'help' key in {payload}"


@then("the mocked app run method should have been called")
def step_verify_app_run_called(context):
    context.mock_app.run.assert_called_once()
