"""Computer-use provider plugins: ``computer_use.backend`` selects exactly one provider per profile, only that one
is imported, the built-in ``cua`` stays the default, and an unknown name fails loudly instead of falling back."""

from __future__ import annotations

import json
import sys
from pathlib import Path

import pytest

import hermes_yaml as yaml
from hermes_constants import reset_hermes_home_override, set_hermes_home_override

_PLUGIN = '''
from pathlib import Path
from tools.computer_use.backend import ActionResult, ComputerUseBackend, ComputerUseProvider

TAG = Path(__file__).parent.name
CALLS = []

class FakeBackend(ComputerUseBackend):
    def __init__(self, mode): self.mode = mode
    def start(self): CALLS.append(("start", self.mode))
    def stop(self): CALLS.append(("stop", None))
    def is_available(self): return True
    def capture(self, mode="som", app=None, pid=None, window_id=None): raise NotImplementedError
    def click(self, **kw):
        CALLS.append(("click", kw.get("element")))
        return ActionResult(ok=True, action="click")
    def drag(self, **kw): return ActionResult(ok=True, action="drag")
    def scroll(self, **kw): return ActionResult(ok=True, action="scroll")
    def type_text(self, text, **kw): return ActionResult(ok=True, action="type")
    def key(self, keys, **kw): return ActionResult(ok=True, action="key")
    def list_apps(self): return [{"backend": TAG, "file": __file__}]
    def focus_app(self, app, raise_window=False): return ActionResult(ok=True, action="focus_app")
    def set_value(self, value, element=None): return ActionResult(ok=True, action="set_value")

class FakeProvider(ComputerUseProvider):
    name = TAG
    def create_backend(self, *, permission_mode): return FakeBackend(permission_mode)

def register(ctx):
    ctx.register_computer_use_provider(FakeProvider())
'''


def _home(home: Path, plugins: tuple, backend: str | None) -> Path:
    for plugin in plugins:
        (plugin_dir := home / "plugins" / plugin).mkdir(parents=True, exist_ok=True)
        (plugin_dir / "plugin.yaml").write_text(yaml.safe_dump({"name": plugin, "description": f"{plugin} driver"}))
        (plugin_dir / "__init__.py").write_text(_PLUGIN)
    home.mkdir(parents=True, exist_ok=True)
    (home / "config.yaml").write_text(yaml.safe_dump({"computer_use": {"backend": backend}} if backend else {}))
    return home


def _list_apps(session_id: str = "s1") -> list:
    from tools.computer_use.tool import handle_computer_use
    out = json.loads(handle_computer_use({"action": "list_apps"}, session_id=session_id))
    assert "apps" in out, out
    return out["apps"]


@pytest.fixture(autouse=True)
def _clean():
    from tools.computer_use.tool import reset_backend_for_tests
    reset_backend_for_tests()
    yield
    reset_backend_for_tests()
    for name in [m for m in sys.modules if m.startswith("_hermes_user_computer_use.")]:
        del sys.modules[name]


def test_selected_plugin_receives_tool_calls_and_unselected_stays_dormant(tmp_path, grant_computer_use_approvals):
    home = _home(tmp_path / "home", ("cu-alpha", "cu-beta"), "cu-beta")
    tok = set_hermes_home_override(str(home))
    try:
        from tools.computer_use.tool import check_computer_use_requirements, handle_computer_use
        assert check_computer_use_requirements() is True
        assert _list_apps()[0]["backend"] == "cu-beta"
        assert json.loads(handle_computer_use({"action": "click", "element": 3}, session_id="s1"))["ok"] is True
        calls = sys.modules["_hermes_user_computer_use.cu-beta"].CALLS
        assert ("start", "standard") in calls and ("click", 3) in calls
        assert "_hermes_user_computer_use.cu-alpha" not in sys.modules  # installed, not selected: never imported
        from hermes_cli.tools_config_providers import _computer_use_provider_rows  # `hermes tools` / Desktop rows
        assert [r["computer_use_backend"] for r in _computer_use_provider_rows()] == ["cu-alpha", "cu-beta"]
        assert "_hermes_user_computer_use.cu-alpha" not in sys.modules  # listing does not import either
    finally:
        reset_hermes_home_override(tok)


def test_unknown_backend_errors_naming_configured_and_available(tmp_path):
    home = _home(tmp_path / "home", (), "nope")
    tok = set_hermes_home_override(str(home))
    try:
        from tools.computer_use.tool import check_computer_use_requirements, handle_computer_use
        out = json.loads(handle_computer_use({"action": "list_apps"}, session_id="s1"))
        assert "'nope'" in out["error"] and "available: cua" in out["error"]
        assert "hint" not in out  # the cua-driver install hint would mislead here
        assert check_computer_use_requirements() is True  # the tool stays visible so the call can say why
    finally:
        reset_hermes_home_override(tok)


@pytest.mark.parametrize("backend", [None, "cua"])
def test_builtin_cua_is_default_and_plugin_never_self_activates(tmp_path, backend):
    home = _home(tmp_path / "home", ("cu-alpha",), backend)  # installed but not selected
    tok = set_hermes_home_override(str(home))
    try:
        from plugins.computer_use import get_active_provider
        from tools.computer_use.cua_backend import CuaDriverBackend
        from tools.computer_use.tool import _new_backend
        assert get_active_provider().name == "cua"
        assert isinstance(_new_backend("standard"), CuaDriverBackend)
        assert "_hermes_user_computer_use.cu-alpha" not in sys.modules
    finally:
        reset_hermes_home_override(tok)


def test_each_profile_gets_its_own_configured_backend(tmp_path):
    home_a = _home(tmp_path / "profiles" / "a", ("cu-alpha",), "cu-alpha")
    home_b = _home(tmp_path / "profiles" / "b", ("cu-beta",), "cu-beta")
    for home, expected in ((home_a, "cu-alpha"), (home_b, "cu-beta"), (home_a, "cu-alpha")):
        tok = set_hermes_home_override(str(home))
        try:
            app = _list_apps(session_id="shared")[0]
            assert app["backend"] == expected and str(home) in app["file"]
        finally:
            reset_hermes_home_override(tok)
