"""Smoke tests for the Navi Code TUI."""

from __future__ import annotations

from pathlib import Path

import pytest

from clients.terminal.tui.events import WsEvent
from clients.terminal.tui.tui_app import NaviCodeTui


@pytest.fixture(autouse=True)
def tmp_state_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
    """Override the state dir so tests never touch ~/.navi_code."""
    from clients.terminal import config

    original = config.settings.state_dir
    config.settings.state_dir = tmp_path
    import clients.terminal.tui.settings as settings_module

    settings_module._tui_settings = None
    yield tmp_path
    config.settings.state_dir = original
    settings_module._tui_settings = None


@pytest.mark.anyio
async def test_tui_mounts_widgets() -> None:
    """The TUI app mounts chat, status, and input widgets."""
    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        assert pilot.app.query_one("ChatPanel") is not None
        assert pilot.app.query_one("StatusPanel") is not None
        assert pilot.app.query_one("SessionsPanel") is not None
        assert pilot.app.query_one("InputBox") is not None


@pytest.mark.anyio
async def test_user_message_appears_in_chat() -> None:
    """Typing a message and submitting adds it to the chat panel."""
    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        input_box = pilot.app.query_one("InputBox")
        input_box._input.value = "hello"
        await pilot.press("enter")
        await pilot.pause()
        chat = pilot.app.query_one("ChatPanel")
        assert any(
            item.kind == "user_message" and item.content == "hello" for item in chat._model.items
        )


@pytest.mark.anyio
async def test_input_text_is_rendered_with_visible_color() -> None:
    """Typed text in the input field is rendered with a visible foreground color."""
    async with NaviCodeTui(new_session=True).run_test(size=(80, 24)) as pilot:
        await pilot.pause()
        input_box = pilot.app.query_one("InputBox")
        input_box._input.focus()
        await pilot.press("h", "i", " ", "n", "a", "v", "i")
        await pilot.pause()
        assert input_box._input.value == "hi navi"
        strip = input_box._input.render_line(0)
        segments = list(strip)
        rendered_text = "".join(seg.text for seg in segments)
        assert "hi navi" in rendered_text
        # Ensure foreground and background are not identical so text is visible.
        for seg in segments:
            if "hi navi" in seg.text and seg.style:
                assert seg.style.color != seg.style.bgcolor


@pytest.mark.anyio
async def test_ws_event_renders_in_chat() -> None:
    """A synthetic WebSocket stream_delta is added to the assistant response."""
    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        chat = pilot.app.query_one("ChatPanel")
        chat.handle_ws_event({"type": "stream_start"})
        chat.handle_ws_event({"type": "stream_delta", "delta": "hi"})
        await pilot.pause()
        assert chat._model._current_assistant is not None
        assert chat._model._current_assistant.content == "hi"


@pytest.mark.anyio
async def test_unknown_slash_command_shows_error() -> None:
    """Unknown slash command produces an error item in chat."""
    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        input_box = pilot.app.query_one("InputBox")
        input_box._input.value = "/notacommand"
        await pilot.press("enter")
        await pilot.pause()
        chat = pilot.app.query_one("ChatPanel")
        assert any(item.kind == "error" for item in chat._model.items)


@pytest.mark.anyio
async def test_chat_panel_clear_resets_conversation() -> None:
    """ChatPanel.clear() removes all items and current assistant state."""
    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        chat = pilot.app.query_one("ChatPanel")
        chat.handle_ws_event({"type": "stream_start"})
        chat.handle_ws_event({"type": "stream_delta", "delta": "hi"})
        await pilot.pause()
        assert chat._model.items
        chat.clear()
        assert not chat._model.items
        assert chat._model._current_assistant is None


@pytest.mark.anyio
async def test_status_panel_shows_backend_and_theme() -> None:
    """Status panel displays backend URL and current theme."""
    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        status = pilot.app.query_one("StatusPanel")
        backend_text = str(status._backend.render())
        theme_text = str(status._theme.render())
        assert "Backend:" in backend_text
        assert "Theme: gnexus-dark" in theme_text


@pytest.mark.anyio
async def test_streaming_state_tracks_ws_events() -> None:
    """stream_start enters streaming mode; stream_end/stream_stopped/error leave it."""
    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        app = pilot.app
        input_box = app.query_one("InputBox")

        app.on_ws_event(WsEvent({"type": "stream_start"}))
        await pilot.pause()
        assert app._streaming is True
        assert "Esc to stop" in input_box._input.placeholder

        app.on_ws_event(WsEvent({"type": "stream_delta", "delta": "hi"}))
        await pilot.pause()
        assert app._streaming is True

        app.on_ws_event(WsEvent({"type": "stream_stopped"}))
        await pilot.pause()
        assert app._streaming is False
        assert input_box._input.placeholder == "Ask anything..."


@pytest.mark.anyio
async def test_escape_stops_active_stream(monkeypatch: pytest.MonkeyPatch) -> None:
    """Pressing Esc while streaming calls api.stop_session for the current session."""
    stopped: list[str] = []

    async def fake_stop_session(session_id: str) -> dict:
        stopped.append(session_id)
        return {"ok": True}

    import clients.terminal.tui.tui_app as tui_app_module

    monkeypatch.setattr(tui_app_module.api, "stop_session", fake_stop_session)

    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        app = pilot.app
        # Simulate an active run for the resolved session.
        app.on_ws_event(WsEvent({"type": "stream_start"}))
        await pilot.pause()
        assert app._streaming is True
        current_session = app._ctx.session_id
        assert current_session

        await pilot.press("escape")
        await pilot.pause(0.1)
        assert stopped == [current_session]


@pytest.mark.anyio
async def test_stream_stopped_renders_status() -> None:
    """stream_stopped resets the assistant buffer and shows a status message."""
    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        chat = pilot.app.query_one("ChatPanel")
        chat.handle_ws_event({"type": "stream_start"})
        chat.handle_ws_event({"type": "stream_delta", "delta": "partial"})
        chat.handle_ws_event({"type": "stream_stopped"})
        await pilot.pause()
        assert chat._model._current_assistant is None
        assert any(
            item.kind == "status" and "stopped" in item.content.lower()
            for item in chat._model.items
        )


@pytest.mark.anyio
async def test_escape_does_nothing_when_not_streaming(monkeypatch: pytest.MonkeyPatch) -> None:
    """Esc without an active stream does not call stop_session."""
    calls: list[str] = []

    async def fake_stop_session(session_id: str) -> dict:
        calls.append(session_id)
        return {"ok": True}

    import clients.terminal.tui.tui_app as tui_app_module

    monkeypatch.setattr(tui_app_module.api, "stop_session", fake_stop_session)

    async with NaviCodeTui(new_session=True).run_test() as pilot:
        await pilot.pause()
        await pilot.press("escape")
        await pilot.pause(0.1)
        assert calls == []
