from __future__ import annotations

import importlib.util
from pathlib import Path

import pytest

import remote_dispatch


def _module():
    path = Path(__file__).parents[2] / "workstation" / "bin" / "pi_remote_dispatch.py"
    spec = importlib.util.spec_from_file_location("pi_remote_dispatch_test", path)
    assert spec and spec.loader
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def test_info_only_invocations_stay_local() -> None:
    module = _module()
    assert module.info_only([])
    assert module.info_only(["--list-models"])
    assert module.info_only(["--version"])
    assert module.info_only(["--help"])
    assert not module.info_only(["-p", "turn", "--session-id", "one"])


def test_registry_failure_is_closed_and_recorded(tmp_path: Path, monkeypatch, capsys) -> None:
    module = _module()
    monkeypatch.setenv("OD_CONTAINMENT_LOG", str(tmp_path / "containment.jsonl"))
    monkeypatch.setattr(module.remote_dispatch, "load_registry", lambda: (_ for _ in ()).throw(
        remote_dispatch.OffloadUnavailable("registry unreadable")
    ))

    assert module.main(["-p", "turn", "--session-id", "one", "--session-dir", str(tmp_path / "session")]) == remote_dispatch.EXIT_NO_NODE
    assert "registry unreadable" in capsys.readouterr().err
    assert "registry unreadable" in (tmp_path / "containment.jsonl").read_text()


def test_no_reachable_seat_is_closed(tmp_path: Path, monkeypatch, capsys) -> None:
    module = _module()
    monkeypatch.setattr(module.remote_dispatch, "load_registry", lambda: {"hosts": []})

    assert module.main(["-p", "turn", "--session-id", "one", "--session-dir", str(tmp_path / "session")]) == remote_dispatch.EXIT_NO_NODE
    assert "no reachable host" in capsys.readouterr().err


def test_extra_mirror_failure_is_closed(tmp_path: Path, monkeypatch, capsys) -> None:
    module = _module()
    monkeypatch.setattr(module.remote_dispatch, "load_registry", lambda: {"hosts": [{"name": "seat", "state": "reachable", "roles": ["agent-seat"], "access": {"tailscale_ip": {"host": "box", "user": "user"}}}]})
    monkeypatch.setattr(module.remote_dispatch, "select_host", lambda hosts: (hosts[0], hosts[0]["access"]["tailscale_ip"], type("Claim", (), {"release": lambda self: None})()))
    monkeypatch.setattr(module.remote_dispatch, "repo_root", lambda cwd: tmp_path)
    monkeypatch.setattr(module.remote_dispatch, "git_mirror_files", lambda root: b"")
    monkeypatch.setattr(module.subprocess, "run", lambda *args, **kwargs: type("Done", (), {"returncode": 0, "stdout": "/home/user\n", "stderr": ""})())
    monkeypatch.setattr(module.remote_dispatch, "sync_extra_dir", lambda *args, **kwargs: "extra mirror push failed: denied")

    assert module.main(["-p", "turn", "--session-id", "one", "--session-dir", str(tmp_path / "session")]) == remote_dispatch.EXIT_MIRROR
    assert "extra mirror push failed" in capsys.readouterr().err


def test_remote_command_carries_guard_chain(tmp_path: Path, monkeypatch) -> None:
    """The remote pi-agent invocation must run with the same PATH-shim guard chain
    local non-Claude agents get (git-guard shim ahead on PATH, agent-launched scope
    marked, pin self-healed) — see docs/plans covering remote pi hook parity."""
    module = _module()
    monkeypatch.setattr(module.remote_dispatch, "load_registry", lambda: {"hosts": [{"name": "seat", "state": "reachable", "roles": ["agent-seat"], "access": {"tailscale_ip": {"host": "box", "user": "user"}}}]})
    monkeypatch.setattr(module.remote_dispatch, "select_host", lambda hosts: (hosts[0], hosts[0]["access"]["tailscale_ip"], type("Claim", (), {"release": lambda self: None})()))
    monkeypatch.setattr(module.remote_dispatch, "repo_root", lambda cwd: tmp_path)
    monkeypatch.setattr(module.remote_dispatch, "git_mirror_files", lambda root: b"")
    monkeypatch.setattr(module.remote_dispatch, "git_mirror", lambda root: None)
    monkeypatch.setattr(module.remote_dispatch, "sync_extra_dir", lambda *args, **kwargs: None)
    monkeypatch.setattr(module.remote_dispatch.Session, "pull_back", lambda self: None)
    monkeypatch.setattr(
        module.subprocess, "run",
        lambda *args, **kwargs: type("Done", (), {"returncode": 0, "stdout": "/home/user\n", "stderr": ""})(),
    )

    captured: dict = {}

    def fake_popen(argv, **kwargs):
        captured["argv"] = argv
        return type("Child", (), {"wait": lambda self: 0})()

    monkeypatch.setattr(module.subprocess, "Popen", fake_popen)

    rc = module.main(["-p", "turn", "--session-id", "one", "--session-dir", str(tmp_path / "session")])
    assert rc == 0
    remote_cmd = captured["argv"][-1]
    assert "export PATH=" in remote_cmd
    guard_path = "/home/user/.claude/bin"
    agent_path = "/home/user/.local/bin"
    assert f"{guard_path}:{agent_path}:$PATH" in remote_cmd
    assert remote_cmd.index(guard_path) < remote_cmd.index(agent_path) < remote_cmd.index("$PATH")
    assert "AGENT_BUILD_SCOPE_ACTIVE=1" in remote_cmd
    assert "install-git-guard-real" in remote_cmd
    assert "exec pi-agent" in remote_cmd
