from pathlib import Path

import pytest
from typer.testing import CliRunner

from yee88 import cli
from yee88.config import ConfigError
from yee88.settings import TakopiSettings
from yee88.settings import TelegramTopicsSettings
from yee88.telegram.api_models import Chat, User


def _settings() -> TakopiSettings:
    return TakopiSettings.model_validate(
        {
            "transport": "telegram",
            "transports": {"telegram": {"bot_token": "token", "chat_id": 123}},
        }
    )


def test_doctor_ok(monkeypatch) -> None:
    settings = _settings()
    monkeypatch.setattr(cli, "load_settings", lambda: (settings, Path("x")))
    monkeypatch.setattr(cli, "resolve_plugins_allowlist", lambda _settings: None)
    monkeypatch.setattr(cli, "list_backend_ids", lambda allowlist=None: ["codex"])

    async def _fake_checks(*_args, **_kwargs):
        return [cli.DoctorCheck("telegram token", "ok", "@bot")]

    monkeypatch.setattr(cli, "_doctor_telegram_checks", _fake_checks)

    runner = CliRunner()
    result = runner.invoke(cli.create_app(), ["doctor"])

    assert result.exit_code == 0
    assert "yee88 doctor" in result.output
    assert "telegram token: ok" in result.output


def test_doctor_errors_exit_nonzero(monkeypatch) -> None:
    settings = _settings()
    monkeypatch.setattr(cli, "load_settings", lambda: (settings, Path("x")))
    monkeypatch.setattr(cli, "resolve_plugins_allowlist", lambda _settings: None)
    monkeypatch.setattr(cli, "list_backend_ids", lambda allowlist=None: ["codex"])

    async def _fake_checks(*_args, **_kwargs):
        return [cli.DoctorCheck("telegram token", "error", "bad token")]

    monkeypatch.setattr(cli, "_doctor_telegram_checks", _fake_checks)

    runner = CliRunner()
    result = runner.invoke(cli.create_app(), ["doctor"])

    assert result.exit_code == 1
    assert "telegram token: error" in result.output


class _FakeBot:
    def __init__(self, me: User | None, chat: Chat | None) -> None:
        self._me = me
        self._chat = chat
        self.closed = False

    async def get_me(self) -> User | None:
        return self._me

    async def get_chat(self, chat_id: int) -> Chat | None:
        _ = chat_id
        return self._chat

    async def close(self) -> None:
        self.closed = True


@pytest.mark.anyio
async def test_doctor_telegram_checks_invalid_token(monkeypatch) -> None:
    bot = _FakeBot(me=None, chat=None)
    monkeypatch.setattr(cli, "TelegramClient", lambda _token: bot)
    topics = TelegramTopicsSettings(enabled=True)

    checks = await cli._doctor_telegram_checks(
        "token",
        123,
        topics,
        (),
    )

    assert [check.label for check in checks] == [
        "telegram token",
        "chat_id",
        "topics",
    ]
    assert checks[0].status == "error"
    assert checks[1].detail == "skipped (token invalid)"
    assert checks[2].detail == "skipped (token invalid)"
    assert bot.closed is True


@pytest.mark.anyio
async def test_doctor_telegram_checks_chat_and_topics_error(monkeypatch) -> None:
    bot = _FakeBot(
        me=User(id=1, username="bot", first_name=None, last_name=None),
        chat=None,
    )
    monkeypatch.setattr(cli, "TelegramClient", lambda _token: bot)

    async def _raise_topics(*_args, **_kwargs) -> None:
        raise ConfigError("bad topics")

    monkeypatch.setattr(cli, "_validate_topics_setup_for", _raise_topics)
    topics = TelegramTopicsSettings(enabled=True)

    checks = await cli._doctor_telegram_checks(
        "token",
        321,
        topics,
        (),
    )

    assert checks[0].detail == "@bot"
    assert checks[1].status == "error"
    assert "unreachable" in (checks[1].detail or "")
    assert checks[2].detail == "bad topics"
    assert bot.closed is True
