from __future__ import annotations

import asyncio
import importlib.util
import sys
from pathlib import Path
from types import SimpleNamespace
from typing import Any

import pytest

HERE = Path(__file__).parent
SPEC = importlib.util.spec_from_file_location(
    "transport_probe", HERE / "telegram_transport_probe.py"
)
assert SPEC and SPEC.loader
m = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = m
SPEC.loader.exec_module(m)


class FakeBot:
    def __init__(
        self, username: str = m.EXPECTED_BOT, failure: Exception | None = None
    ):
        self.username = username
        self.failure = failure
        self.calls: list[str] = []

    async def __aenter__(self):
        self.calls.append("enter")
        return self

    async def __aexit__(self, *_args):
        self.calls.append("exit")

    async def get_me(self):
        self.calls.append("get_me")
        if self.failure:
            raise self.failure
        return SimpleNamespace(username=self.username)


def test_gate_calls_only_get_me_and_returns_bound_readiness():
    bot = FakeBot()
    result = asyncio.run(m.getme_gate("secret", lambda _token: bot))
    assert bot.calls == ["enter", "get_me", "exit"]
    assert result == {
        "schema": "task26-telegram-transport-readiness-v1",
        "status": "READY_TELEGRAM_TRANSPORT",
        "successor": m.SUCCESSOR,
        "method": "Bot.get_me",
        "consumes_updates": False,
        "mutates_cursor": False,
        "writes_state": False,
    }


def test_gate_fails_closed_on_bad_gateway_or_other_transport_error():
    bot = FakeBot(failure=RuntimeError("Bad Gateway"))
    with pytest.raises(m.ProbeFailure, match="transport failure"):
        asyncio.run(m.getme_gate("secret", lambda _token: bot))


def test_gate_fails_closed_on_timeout_without_polling_or_sleep():
    class SlowBot(FakeBot):
        async def get_me(self) -> Any:
            self.calls.append("get_me")
            await asyncio.Event().wait()

    bot = SlowBot()
    with pytest.raises(m.ProbeFailure, match="timeout"):
        asyncio.run(m.getme_gate("secret", lambda _token: bot, timeout=0.01))
    assert bot.calls == ["enter", "get_me", "exit"]


def test_gate_rejects_empty_token_and_wrong_bot_identity():
    with pytest.raises(m.ProbeFailure, match="token unavailable"):
        asyncio.run(m.getme_gate("", lambda _token: FakeBot()))
    with pytest.raises(m.ProbeFailure, match="identity mismatch"):
        asyncio.run(m.getme_gate("secret", lambda _token: FakeBot("other_bot")))
