"""test_server_smoke.py — смоук для resilience-логики tradein-browser.

Полный браузер (camoufox subprocess) юнит-тестить непрактично, поэтому здесь
только смоук: (1) модуль импортируется; (2) /fetch и /login отдают 503 когда
браузер не поднят (прокси лежит) — критичный resilience-инвариант, защищающий
от crash-storm. camoufox в окружении теста НЕ нужен: launch замокан no-op'ом,
имитируя «прокси всё ещё недоступен».

Запуск (из tradein-mvp/browser/)::

    python -m pytest test_server_smoke.py -q

aiohttp.test_utils.make_mocked_request не поднимает реальный сервер/сокет —
хендлер вызывается напрямую, без event-loop-сервера.
"""

from __future__ import annotations

import asyncio
import importlib.util
import json
from pathlib import Path
from typing import Any

import pytest
from aiohttp.test_utils import make_mocked_request

# server.py — не пакет (отдельный сервис без __init__/pyproject). Грузим по пути.
_SERVER_PATH = Path(__file__).resolve().parent / "server.py"
_spec = importlib.util.spec_from_file_location("tradein_browser_server", _SERVER_PATH)
assert _spec is not None and _spec.loader is not None
server = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(server)


def _json_body(response: Any) -> dict[str, Any]:
    """Достаёт JSON-тело из aiohttp Response (body — bytes после json_response)."""
    return json.loads(response.body.decode())


def test_module_imports() -> None:
    """Смоук: модуль импортируется и экспонирует ключевые хендлеры."""
    assert callable(server.fetch_handler)
    assert callable(server.login_handler)
    assert callable(server.health_handler)


def test_health_reports_browser_not_ready(monkeypatch: pytest.MonkeyPatch) -> None:
    """/health всегда 200, browser_ready=False когда браузер не поднят."""
    monkeypatch.setattr(server, "_browser", None)
    request = make_mocked_request("GET", "/health")
    response = asyncio.run(server.health_handler(request))
    assert response.status == 200
    body = _json_body(response)
    assert body["status"] == "ok"
    assert body["browser_ready"] is False


def test_fetch_returns_503_when_browser_unavailable(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """/fetch отдаёт 503 (не 500/краш) когда браузер не поднят и launch не удался.

    Имитирует «прокси лежит»: _browser=None + _try_launch_browser no-op,
    оставляющий None. Никакого реального camoufox-launch'а / direct-fetch.
    """
    monkeypatch.setattr(server, "_browser", None)
    monkeypatch.setattr(server, "_fetch_lock", asyncio.Lock())

    async def _no_op_launch() -> bool:
        return False  # прокси всё ещё недоступен → браузер остаётся None

    monkeypatch.setattr(server, "_try_launch_browser", _no_op_launch)

    request = make_mocked_request(
        "POST", "/fetch", payload=b'{"url": "https://example.com"}'
    )

    async def _run() -> Any:
        # make_mocked_request не парсит тело — подменяем .json() на нужный payload.
        request.json = lambda: _coro({"url": "https://example.com"})  # type: ignore[method-assign]
        return await server.fetch_handler(request)

    response = asyncio.run(_run())
    assert response.status == 503
    body = _json_body(response)
    assert "browser unavailable" in body["error"]


def test_login_returns_503_when_browser_unavailable(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """/login тоже отдаёт 503 когда браузер не поднят (без credential-логирования)."""
    monkeypatch.setattr(server, "_browser", None)
    monkeypatch.setattr(server, "_fetch_lock", asyncio.Lock())

    async def _no_op_launch() -> bool:
        return False

    monkeypatch.setattr(server, "_try_launch_browser", _no_op_launch)

    login_body = {
        "url": "https://cian.example/login",
        "email": "u@example.com",
        "password": "secret",
        "email_selector": "#email",
        "password_selector": "#password",
        "submit_selector": "#submit",
    }
    request = make_mocked_request("POST", "/login")

    async def _run() -> Any:
        request.json = lambda: _coro(login_body)  # type: ignore[method-assign]
        return await server.login_handler(request)

    response = asyncio.run(_run())
    assert response.status == 503
    body = _json_body(response)
    assert "browser unavailable" in body["error"]


async def _coro(value: Any) -> Any:
    """Хелпер: оборачивает значение в awaitable для подмены request.json()."""
    return value
