"""Boot-time subscription health probe: only a definitive auth-shaped 401/403 reads as dead
(transients stay silent so the reconnect pill can't cry wolf), probes cover only active
subscription lanes, and results are cached so a double boot-fetch can't double-spend probes."""

import asyncio
import json
from typing import Dict, List

import backend.apps.nine_router.subscription_health as sh


def test_classify_auth_dead_is_conservative():
    assert sh.classify_auth_dead(401, '{"error": "authentication token is expired"}')
    assert sh.classify_auth_dead(403, "Unauthorized access")
    assert sh.classify_auth_dead(401, "Invalid authentication credentials")
    assert not sh.classify_auth_dead(401, "weird opaque body")  # 401 without an auth marker stays silent
    assert not sh.classify_auth_dead(429, "rate limit expired")  # non-auth status never fires
    assert not sh.classify_auth_dead(500, "authentication service down")
    assert not sh.classify_auth_dead(200, "expired")


class FakeResponse:
    def __init__(self, status_code: int, payload: Dict):
        self.status_code = status_code
        self.text = json.dumps(payload)
        self.p_payload = payload

    def json(self) -> Dict:
        return self.p_payload


class FakeClient:
    """cc/ lane healthy, cx/ lane auth-dead, gemini/ lane rate-limited (must stay silent)."""

    def __init__(self, counter: List[int], **kwargs):
        self.counter = counter

    async def __aenter__(self) -> "FakeClient":
        return self

    async def __aexit__(self, *exc) -> None:
        return None

    async def get(self, url: str, **kw) -> FakeResponse:
        return FakeResponse(200, {"data": [
            {"id": "cc/claude-haiku"}, {"id": "cx/gpt-5.4"}, {"id": "gemini/gemini-2.5-flash"},
        ]})

    async def post(self, url: str, **kw) -> FakeResponse:
        self.counter[0] += 1
        model = kw.get("json", {}).get("model", "")
        if model.startswith("cx/"):
            return FakeResponse(401, {"error": {"message": "authentication token is expired, try signing in again"}})
        if model.startswith("gemini/"):
            return FakeResponse(429, {"error": {"message": "rate limited"}})
        return FakeResponse(200, {"content": []})


CONNS = [
    {"provider": "claude", "isActive": True},
    {"provider": "codex", "isActive": True},
    {"provider": "gemini-cli", "isActive": True},
    {"provider": "openrouter", "isActive": True},  # not a sub lane; never probed
    {"provider": "codex", "isActive": False},  # inactive; never probed
]


def test_probe_reports_only_definitive_death_and_caches(monkeypatch):
    counter = [0]
    monkeypatch.setattr(sh, "is_running", lambda: True)
    monkeypatch.setattr(sh.httpx, "AsyncClient", lambda **kw: FakeClient(counter, **kw))
    monkeypatch.setattr(sh, "p_cached_result", None)
    monkeypatch.setattr(sh, "p_cached_at", 0.0)
    dead = asyncio.run(sh.probe_subscription_health(CONNS))
    assert dead == [{"provider": "codex", "label": "ChatGPT"}]
    assert counter[0] == 3  # claude + codex + gemini-cli probed; openrouter/inactive skipped
    # Second call within the TTL serves the cache; no new probe spend.
    dead2 = asyncio.run(sh.probe_subscription_health(CONNS))
    assert dead2 == dead
    assert counter[0] == 3


def test_probe_disabled_or_router_down(monkeypatch):
    monkeypatch.setattr(sh, "is_running", lambda: False)
    assert asyncio.run(sh.probe_subscription_health(CONNS)) == []
    monkeypatch.setattr(sh, "is_running", lambda: True)
    monkeypatch.setenv("OPENSWARM_BOOT_HEALTH", "0")
    assert asyncio.run(sh.probe_subscription_health(CONNS)) == []
