"""HUD mode reaches the model as a per-turn note, not as a platform hint.

The floating HUD sits over whatever the user is really working in, so a request
typed there is usually about that other app — and usually wants to be carried
out IN that app. Without the note the model answers from its own browser and
panes, which is the wrong half of the screen.

The same desktop session can be driven from the app window on one turn and the
HUD on the next, so this cannot live in the system prompt — that has to stay
byte-stable for the life of a conversation. It rides the model-bound message
instead, beside the reaction and speech-interrupted notes.
"""

import threading
import types

import pytest

from agent.prompt_builder import hud_surface_note
from tui_gateway import server

FULL_KIT = {"read_window_below", "computer_use", "browser_navigate"}


def _session(*, tools=FULL_KIT, **extra):
    return {
        "agent": types.SimpleNamespace(valid_tool_names=set(tools)),
        "session_key": "session-key",
        "history": [],
        "history_lock": threading.Lock(),
        "history_version": 0,
        "running": False,
        "transport": None,
        "attached_images": [],
        **extra,
    }


class TestNoteContents:
    """Every tool the note names has to be one this agent actually has."""

    def test_points_at_the_window_below_and_at_working_in_it(self):
        note = hud_surface_note(FULL_KIT)

        assert "read_window_below" in note
        assert "computer_use" in note
        assert "browser_navigate" in note

    def test_no_note_at_all_without_the_tool_it_rests_on(self):
        assert hud_surface_note({"computer_use", "browser_navigate"}) == ""

    def test_identifies_the_app_even_when_it_cannot_drive_it(self):
        note = hud_surface_note({"read_window_below"})

        assert "read_window_below" in note
        assert "computer_use" not in note

    def test_browser_preference_needs_a_browser_to_prefer_over(self):
        note = hud_surface_note({"read_window_below", "computer_use"})

        assert "computer_use" in note
        assert "browser_navigate" not in note

    def test_no_tools_at_all(self):
        assert hud_surface_note(None) == ""


class TestTurnRouting:
    def test_hud_turn_gets_the_note(self):
        assert server._hud_surface_note(_session(client_surface="hud")) == hud_surface_note(FULL_KIT)

    def test_app_window_turn_gets_nothing(self):
        assert server._hud_surface_note(_session(client_surface="")) == ""

    def test_session_that_never_reported_a_surface_gets_nothing(self):
        """Every other client (TUI, dashboard, messaging) omits the field."""
        assert server._hud_surface_note(_session()) == ""

    def test_survives_an_agent_with_no_toolset_at_all(self):
        session = _session(client_surface="hud")
        session["agent"] = types.SimpleNamespace()

        assert server._hud_surface_note(session) == ""


class TestPrepending:
    """Notes ride the model input; the persisted prompt stays what was typed."""

    def test_plain_text_turn(self):
        assert server._prepend_note("go to x", "[Note: hi]") == "[Note: hi]\n\ngo to x"

    def test_multimodal_turn_keeps_its_parts(self):
        parts = [{"type": "text", "text": "go to x"}, {"type": "image_url", "image_url": {}}]

        assert server._prepend_note(parts, "[Note: hi]") == [
            {"type": "text", "text": "[Note: hi]"},
            *parts,
        ]

    def test_nothing_to_say_leaves_the_message_untouched(self):
        parts = [{"type": "text", "text": "go to x"}]

        assert server._prepend_note("go to x", "") == "go to x"
        assert server._prepend_note(parts, "") is parts


class TestSurfaceRecording:
    """``prompt.submit`` stamps the window each message was typed into."""

    @pytest.fixture
    def busy_session(self):
        # A running session takes the busy path, which returns before any of
        # the agent/DB machinery — enough to observe what submit recorded.
        session = _session(running=True)
        server._sessions["sid"] = session
        yield session
        server._sessions.pop("sid", None)

    def _submit(self, **params):
        return server._methods["prompt.submit"](
            "r1", {"session_id": "sid", "text": "what is this?", "queued": True, **params}
        )

    def test_hud_submit_is_recorded(self, busy_session):
        self._submit(surface="hud")

        assert busy_session["client_surface"] == "hud"

    def test_the_next_app_window_submit_clears_it(self, busy_session):
        """A stale 'hud' would tell the model the user is still floating."""
        self._submit(surface="hud")
        self._submit()

        assert busy_session["client_surface"] == ""

    def test_an_unknown_surface_is_not_hud(self, busy_session):
        self._submit(surface="pet-overlay")

        assert busy_session["client_surface"] == ""
