from __future__ import annotations

import importlib

from click.testing import CliRunner
import httpx

from logos_adk.cli import cli
from logos_adk.types import Response, Usage


def test_serve_command_requires_at_least_one_agent():
    result = CliRunner().invoke(cli, ["serve"])

    assert result.exit_code == 1
    assert "At least one --agents config file is required" in result.output


def test_serve_command_builds_app_and_starts_uvicorn(tmp_path, monkeypatch):
    config = tmp_path / "agent.yaml"
    config.write_text("name: bot\nmodel: mock\n")
    seen: dict[str, object] = {}
    serve_module = importlib.import_module("logos_adk.cli.commands.serve")

    class FakeAgent:
        def __init__(self, name: str):
            self.name = name

    async def _fake_loader(config_path: str):
        seen["loaded"] = config_path
        return FakeAgent("bot")

    def _fake_create_app(agents, **kwargs):
        seen["agent_names"] = [agent.name for agent in agents]
        seen["kwargs"] = kwargs
        return "fake-app"

    def _fake_uvicorn_run(app, host: str, port: int, reload: bool = False):
        seen["uvicorn"] = {"app": app, "host": host, "port": port, "reload": reload}

    monkeypatch.setattr(serve_module, "load_agent_from_config_async", _fake_loader)
    monkeypatch.setattr("logos_adk.serve.create_app", _fake_create_app)
    monkeypatch.setattr("uvicorn.run", _fake_uvicorn_run)

    result = CliRunner().invoke(
        cli,
        [
            "serve",
            "--agents",
            str(config),
            "--host",
            "127.0.0.1",
            "--port",
            "9000",
            "--reload",
            "--api-key",
            "legacy-token",
            "--api-keys-json",
            '{"token-1":{"name":"ci","scopes":["agent:run"],"workspaces":["acme"]}}',
            "--require-workspace",
            "--no-rate-limit",
            "--chronos-autostart",
        ],
    )

    assert result.exit_code == 0
    assert seen["loaded"] == str(config)
    assert seen["agent_names"] == ["bot"]
    assert seen["kwargs"] == {
        "api_key": "legacy-token",
        "api_keys": {"token-1": {"name": "ci", "scopes": ["agent:run"], "workspaces": ["acme"]}},
        "require_workspace": True,
        "rate_limit_enabled": False,
        "rate_limit_per_minute": None,
        "rate_limit_burst": None,
        "rate_limit_backend": None,
        "rate_limit_dsn": None,
        "chronos_autostart": True,
    }
    assert seen["uvicorn"] == {
        "app": "fake-app",
        "host": "127.0.0.1",
        "port": 9000,
        "reload": True,
    }


def test_serve_command_test_flag_exercises_agents_before_start(tmp_path, monkeypatch):
    config = tmp_path / "agent.yaml"
    config.write_text("name: bot\nmodel: mock\n")
    calls: list[tuple[str, str | None]] = []
    serve_module = importlib.import_module("logos_adk.cli.commands.serve")

    class FakeAgent:
        name = "bot"

        async def run(self, prompt: str, session_id: str | None = None):
            calls.append((prompt, session_id))
            return Response(content="ok", tool_calls=[], usage=Usage(0, 0), raw={})

    async def _fake_loader(config_path: str):
        return FakeAgent()

    monkeypatch.setattr(serve_module, "load_agent_from_config_async", _fake_loader)
    monkeypatch.setattr("logos_adk.serve.create_app", lambda agents, **kwargs: "fake-app")
    monkeypatch.setattr("uvicorn.run", lambda *args, **kwargs: None)

    result = CliRunner().invoke(cli, ["serve", "--agents", str(config), "--test"])

    assert result.exit_code == 0
    assert calls == [("test", "cli-test")]
    assert "Testing agents..." in result.output
    assert "bot: OK" in result.output


def test_serve_command_rejects_invalid_api_keys_json(tmp_path):
    config = tmp_path / "agent.yaml"
    config.write_text("name: bot\nmodel: mock\n")

    result = CliRunner().invoke(
        cli,
        ["serve", "--agents", str(config), "--api-keys-json", "{not-json}"],
    )

    assert result.exit_code == 1
    assert "Invalid --api-keys-json" in result.output


def test_test_live_stream_uses_stream_endpoint(monkeypatch):
    seen: dict[str, object] = {}

    class FakeResponse:
        def raise_for_status(self):
            return None

        def iter_lines(self):
            return iter(['data: {"type":"text","delta":"Hel"}', 'data: {"type":"done"}'])

    def _fake_post(url: str, json=None, headers=None, timeout=None):
        seen["url"] = url
        seen["json"] = json
        return FakeResponse()

    monkeypatch.setattr("httpx.post", _fake_post)

    result = CliRunner().invoke(
        cli,
        ["test-live", "http://localhost:8000", "hello", "--stream"],
    )

    assert result.exit_code == 0
    assert seen["url"] == "http://localhost:8000/stream"
    assert seen["json"] == {"prompt": "hello"}
    assert "Hel" in result.output


def test_test_live_non_stream_prints_response_and_passes_agent(monkeypatch):
    seen: dict[str, object] = {}

    class FakeResponse:
        def raise_for_status(self):
            return None

        def json(self):
            return {"content": "Hello back", "usage": {"input_tokens": 1, "output_tokens": 2}}

    def _fake_post(url: str, json=None, headers=None, timeout=None):
        seen["url"] = url
        seen["json"] = json
        return FakeResponse()

    monkeypatch.setattr("httpx.post", _fake_post)

    result = CliRunner().invoke(
        cli,
        ["test-live", "http://localhost:8000", "hello", "--agent", "bot"],
    )

    assert result.exit_code == 0
    assert seen["url"] == "http://localhost:8000/run"
    assert seen["json"] == {"prompt": "hello", "agent_name": "bot"}
    assert "Response:" in result.output
    assert "Hello back" in result.output
    assert "[Tokens: 1 in, 2 out]" in result.output


def test_test_live_http_error_returns_exit_code_1(monkeypatch):
    class FakeResponse:
        def raise_for_status(self):
            raise httpx.HTTPError("boom")

    monkeypatch.setattr("httpx.post", lambda *args, **kwargs: FakeResponse())

    result = CliRunner().invoke(cli, ["test-live", "http://localhost:8000", "hello"])

    assert result.exit_code == 1
    assert "HTTP Error: boom" in result.output
