from io import StringIO
from types import SimpleNamespace

import onefirewall_ai.repl as repl
from onefirewall_ai.agent import loop


class _DummyStatus:
    def __init__(self):
        self.started = False

    def start(self):
        self.started = True

    def stop(self):
        self.started = False


def test_spinner_start_clears_current_line(monkeypatch):
    writes: list[str] = []

    class _FakeStdout:
        def write(self, value):
            writes.append(value)

        def flush(self):
            writes.append("<flush>")

    monkeypatch.setattr(loop.sys, "stdout", _FakeStdout())
    monkeypatch.setattr(loop.console, "status", lambda *_args, **_kwargs: _DummyStatus())

    spinner = loop._Spinner("Thinking")
    spinner.start()
    spinner.stop()

    assert writes[0] == "\r\033[K"


def test_run_repl_stops_spinner_after_initial_prompt(monkeypatch, tmp_path):
    class DummySpinner:
        active = 0
        starts = 0
        stops = 0

        def __init__(self, _message):
            self.running = False

        def start(self):
            if not self.running:
                self.running = True
                DummySpinner.active += 1
                DummySpinner.starts += 1

        def stop(self):
            if self.running:
                self.running = False
                DummySpinner.active -= 1
                DummySpinner.stops += 1

        def set_message(self, _message):
            return None

    class FakeLive:
        def __init__(self, *_args, **_kwargs):
            pass

        def __enter__(self):
            return self

        def __exit__(self, *_args):
            return None

        def update(self, _value):
            return None

    class FakePromptSession:
        def __init__(self, *args, **kwargs):
            pass

        def prompt(self, *args, **kwargs):
            raise EOFError()

    class FakeRouter:
        def __init__(self, strategy):
            self.strategy = strategy

        def initialize(self, creds):
            return None

        def get_best_provider(self):
            return SimpleNamespace(endpoint="https://example.com", api_key="key", big_model="model")

    def fake_stream_gateway(**_kwargs):
        return SimpleNamespace(content="Done", tool_calls=[])

    monkeypatch.setattr(repl, "init_home", lambda: None)
    monkeypatch.setattr(repl, "HOME_DIR", tmp_path)
    monkeypatch.setattr(
        repl,
        "load_access",
        lambda: {"api_key": "key", "endpoint": "https://example.com", "model": "test-model", "strategy": "balanced"},
    )
    monkeypatch.setattr(repl, "PromptSession", FakePromptSession)
    monkeypatch.setattr(repl, "Live", FakeLive)
    monkeypatch.setattr(loop, "_Spinner", DummySpinner)
    monkeypatch.setattr(loop, "_build_system_prompt", lambda: "")
    monkeypatch.setattr(loop, "_recover_empty_agent_reply", lambda _user_text, content, _tool_calls: content)
    monkeypatch.setattr(loop, "_stream_gateway", fake_stream_gateway)
    monkeypatch.setattr(loop, "_wrap_agent_mode_prompt", lambda text: text)
    monkeypatch.setattr(repl.console, "print", lambda *args, **kwargs: None)

    import onefirewall_ai.agent.router as router_module
    import onefirewall_ai.agent.tools as tools_module

    monkeypatch.setattr(router_module, "SmartRouter", FakeRouter)
    monkeypatch.setattr(tools_module, "get_tool_schemas", lambda: [])

    repl.run_repl(initial_prompt="hello", session_id="test_session", agent_mode=True)

    assert DummySpinner.starts >= 1
    assert DummySpinner.active == 0
    assert DummySpinner.stops >= 1


def test_run_repl_prints_recovered_empty_reply(monkeypatch, tmp_path):
    printed: list[str] = []

    class DummySpinner:
        def __init__(self, _message):
            self.running = False

        def start(self):
            self.running = True

        def stop(self):
            self.running = False

        def set_message(self, _message):
            return None

    class FakeLive:
        def __init__(self, *_args, **_kwargs):
            pass

        def __enter__(self):
            return self

        def __exit__(self, *_args):
            return None

        def update(self, _value):
            return None

    class FakePromptSession:
        def __init__(self, *args, **kwargs):
            pass

        def prompt(self, *args, **kwargs):
            raise EOFError()

    class FakeRouter:
        def __init__(self, strategy):
            self.strategy = strategy

        def initialize(self, creds):
            return None

        def get_best_provider(self):
            return SimpleNamespace(endpoint="https://example.com", api_key="key", big_model="model")

    def fake_stream_gateway(**_kwargs):
        return SimpleNamespace(content="", tool_calls=[])

    monkeypatch.setattr(repl, "init_home", lambda: None)
    monkeypatch.setattr(repl, "HOME_DIR", tmp_path)
    monkeypatch.setattr(
        repl,
        "load_access",
        lambda: {"api_key": "key", "endpoint": "https://example.com", "model": "test-model", "strategy": "balanced"},
    )
    monkeypatch.setattr(repl, "PromptSession", FakePromptSession)
    monkeypatch.setattr(repl, "Live", FakeLive)
    monkeypatch.setattr(loop, "_Spinner", DummySpinner)
    monkeypatch.setattr(loop, "_build_system_prompt", lambda: "")
    monkeypatch.setattr(loop, "_recover_empty_agent_reply", lambda _user_text, _content, _tool_calls: "Recovered reply")
    monkeypatch.setattr(loop, "_stream_gateway", fake_stream_gateway)
    monkeypatch.setattr(loop, "_wrap_agent_mode_prompt", lambda text: text)
    monkeypatch.setattr(repl.console, "print", lambda *args, **kwargs: printed.append(" ".join(str(arg) for arg in args)))

    import onefirewall_ai.agent.router as router_module
    import onefirewall_ai.agent.tools as tools_module

    monkeypatch.setattr(router_module, "SmartRouter", FakeRouter)
    monkeypatch.setattr(tools_module, "get_tool_schemas", lambda: [])

    repl.run_repl(initial_prompt="hello", session_id="test_session_recovered", agent_mode=True)

    assert any("Recovered reply" in entry for entry in printed)
