"""CLI integration tests for `python -m ratatoskr.tier3`.

The define/patch/delete wire calls route through the worldtree-sdk adapter
(`ratatoskr.wt`); the adapter's body-building + error-mapping are unit-tested in
`test_wt.py` (against a fake `client.agents`). These tests exercise the CLI
end-to-end — argv → the real SDK over a respx-mocked HTTP layer → exit code +
stdout + the local tier3 index side effects.

The define/patch response echoes `role` (Worldtree spec 1.2 / b128), read off the
SDK's open-world dict; `LocalAgentEntry.role` is the v2-schema field.
"""

from pathlib import Path

import httpx
import pytest
import respx

from ratatoskr.tier3 import main

# The consumer-agent response echoes `role` (b128), not the former `model`.
_FULL_AGENT_RESP = {
    "agent_id": "ratatoskr:wizard",
    "user_id": "ratatoskr",
    "agent_name": "wizard",
    "system_prompt": "You are a wizard.",
    "role": "qwen3.6-35-a3b",
    "created_at": "2026-05-25T03:20:09.703601+00:00",
    "updated_at": "2026-05-25T03:20:09.703601+00:00",
}


@pytest.fixture
def _isolated_local_agents(
    tmp_path: "Path", monkeypatch: pytest.MonkeyPatch
) -> "Path":
    """Isolate the v0.8.0 local-tier-3 index from the operator's real file."""
    path = tmp_path / "local_agents.json"
    monkeypatch.setenv("RATATOSKR_LOCAL_AGENTS", str(path))
    return path


class TestCli:
    @respx.mock
    def test_cli_define_happy(
        self,
        capsys: pytest.CaptureFixture[str],
        monkeypatch: pytest.MonkeyPatch,
        _isolated_local_agents: "Path",
    ) -> None:
        """cli_define_happy [happy,tracer]: argv → 201 mock → stdout confirmation;
        local index updated with the new entry (role echoed).
        """
        from ratatoskr.local_agents import load_local_agents

        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        respx.post("https://w.example/agents/define").mock(
            return_value=httpx.Response(201, json=_FULL_AGENT_RESP)
        )
        rc = main([
            "define",
            "--name", "wizard",
            "--system-prompt", "You are a wizard.",
            "--role", "qwen3.6-35-a3b",
        ])
        out = capsys.readouterr()
        assert rc == 0
        assert out.out.strip() == "defined ratatoskr:wizard (qwen3.6-35-a3b)"
        entries = load_local_agents()
        assert len(entries) == 1
        assert entries[0].agent_id == "ratatoskr:wizard"
        assert entries[0].role == "qwen3.6-35-a3b"

    @respx.mock
    def test_cli_define_body_shape(
        self, monkeypatch: pytest.MonkeyPatch, _isolated_local_agents: "Path"
    ) -> None:
        """cli_define_body_shape [trace]: outbound JSON is exactly the three keys
        (the adapter sends AgentDefineInput, no layer fields)."""
        import json as _json

        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        route = respx.post("https://w.example/agents/define").mock(
            return_value=httpx.Response(201, json=_FULL_AGENT_RESP)
        )
        main([
            "define", "--name", "wizard",
            "--system-prompt", "You are a wizard.", "--role", "qwen3.6-35-a3b",
        ])
        body = _json.loads(route.calls[0].request.content)
        assert body == {
            "agent_name": "wizard",
            "role": "qwen3.6-35-a3b",
            "system_prompt": "You are a wizard.",
        }

    @respx.mock
    def test_cli_patch_happy(
        self,
        capsys: pytest.CaptureFixture[str],
        monkeypatch: pytest.MonkeyPatch,
        _isolated_local_agents: "Path",
    ) -> None:
        """cli_patch_happy [happy]: argv → 200 mock → stdout confirmation;
        local index refreshed with the post-patch state.
        """
        from ratatoskr.local_agents import load_local_agents

        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        respx.patch("https://w.example/agents/ratatoskr:wizard").mock(
            return_value=httpx.Response(200, json=_FULL_AGENT_RESP)
        )
        rc = main(["patch", "ratatoskr:wizard", "--system-prompt", "new"])
        out = capsys.readouterr()
        assert rc == 0
        assert out.out.strip() == "patched ratatoskr:wizard"
        entries = load_local_agents()
        assert len(entries) == 1
        assert entries[0].agent_id == "ratatoskr:wizard"
        assert entries[0].role == "qwen3.6-35-a3b"

    @respx.mock
    def test_cli_delete_happy(
        self,
        capsys: pytest.CaptureFixture[str],
        monkeypatch: pytest.MonkeyPatch,
        _isolated_local_agents: "Path",
    ) -> None:
        """cli_delete_happy [happy]: argv → 204 mock → stdout confirmation;
        local index entry removed (v0.8.0 hook).
        """
        from ratatoskr.local_agents import (
            LocalAgentEntry,
            add_local_agent,
            load_local_agents,
        )

        # Pre-populate so we can verify removal (v2 schema: role, not model).
        add_local_agent(LocalAgentEntry(
            agent_id="ratatoskr:wizard",
            agent_name="wizard",
            role="m",
            description="d",
            defined_at="t",
        ))
        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        respx.delete("https://w.example/agents/ratatoskr:wizard").mock(
            return_value=httpx.Response(204)
        )
        rc = main(["delete", "ratatoskr:wizard"])
        out = capsys.readouterr()
        assert rc == 0
        assert out.out.strip() == "deleted ratatoskr:wizard"
        assert load_local_agents() == []

    def test_cli_missing_auth(
        self, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """cli_missing_auth [error]: no api-key → stderr [auth_error] + exit 11."""
        monkeypatch.delenv("WORLDTREE_API_KEY", raising=False)
        rc = main([
            "define", "--name", "wizard", "--system-prompt", "x", "--role", "m",
        ])
        err = capsys.readouterr().err
        assert rc == 11
        assert "[auth_error]" in err

    @respx.mock
    def test_cli_api_failed(
        self, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """cli_api_failed [error]: 500 → stderr [api_failed] + exit 20."""
        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        respx.post("https://w.example/agents/define").mock(
            return_value=httpx.Response(500, content=b"upstream out")
        )
        rc = main([
            "define", "--name", "wizard", "--system-prompt", "x", "--role", "m",
        ])
        err = capsys.readouterr().err
        assert rc == 20
        assert "[api_failed]" in err

    @respx.mock
    def test_cli_quota_exceeded(
        self, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """cli_quota_exceeded [error]: 429 → stderr [quota_exceeded] + exit 20."""
        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        respx.post("https://w.example/agents/define").mock(
            return_value=httpx.Response(
                429,
                headers={"Retry-After": "0"},
                json={"detail": {"error_code": "agent_quota_exceeded"}},
            )
        )
        rc = main([
            "define", "--name", "wizard", "--system-prompt", "x", "--role", "m",
        ])
        err = capsys.readouterr().err
        assert rc == 20
        assert "[quota_exceeded]" in err

    @respx.mock
    def test_cli_network_error(
        self, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """cli_network_error [error]: a transport failure surfaces as the SDK's
        ConnectFailed (not a raw httpx error) → stderr [network_error] + exit 21."""
        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        respx.post("https://w.example/agents/define").mock(
            side_effect=httpx.ConnectError("refused")
        )
        rc = main([
            "define", "--name", "wizard", "--system-prompt", "x", "--role", "m",
        ])
        err = capsys.readouterr().err
        assert rc == 21
        assert "[network_error]" in err

    def test_cli_patch_no_fields(
        self, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """cli_patch_no_fields [error]: patch with no flags → [usage_error] + exit 10."""
        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        rc = main(["patch", "ratatoskr:wizard"])
        err = capsys.readouterr().err
        assert rc == 10
        assert "[usage_error]" in err

    @respx.mock
    def test_cli_define_partial_response_degrades(
        self,
        capsys: pytest.CaptureFixture[str],
        monkeypatch: pytest.MonkeyPatch,
        _isolated_local_agents: "Path",
    ) -> None:
        """cli_define_partial [error]: a 201 missing `role` + a NULL system_prompt
        degrades (role '?', description-safe) and exits 0 — never a KeyError/
        AttributeError traceback (heid-bug-hunt open-world invariant)."""
        from ratatoskr.local_agents import load_local_agents

        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        respx.post("https://w.example/agents/define").mock(
            return_value=httpx.Response(201, json={
                "agent_id": "ratatoskr:wizard", "agent_name": "wizard",
                "system_prompt": None,  # present-but-null: .get(...,'') would NOT default
            })
        )
        rc = main([
            "define", "--name", "wizard", "--system-prompt", "x", "--role", "m",
        ])
        out = capsys.readouterr()
        assert rc == 0
        assert out.out.strip() == "defined ratatoskr:wizard (?)"
        # Well-formed identity → still indexed (role degraded to '?').
        entries = load_local_agents()
        assert len(entries) == 1
        assert entries[0].role == "?"

    @respx.mock
    def test_cli_define_no_agent_id_is_api_failure(
        self, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """cli_define_no_agent_id [error]: a 2xx with no usable agent_id → [api_failed]
        + exit 20 (controlled), not an uncaught traceback."""
        monkeypatch.setenv("WORLDTREE_API_URL", "https://w.example")
        monkeypatch.setenv("WORLDTREE_API_KEY", "k")
        respx.post("https://w.example/agents/define").mock(
            return_value=httpx.Response(201, json={"role": "m"})  # no agent_id
        )
        rc = main([
            "define", "--name", "wizard", "--system-prompt", "x", "--role", "m",
        ])
        err = capsys.readouterr().err
        assert rc == 20
        assert "[api_failed]" in err
