"""test_server_proxy_override.py — динамический прокси из тела /fetch (#2164 P4).

Проверяет:
  1. _resolve_proxy_override — берёт body["proxy"] (kind http/https), None если нет /
     если proxy_kind несовместим (socks5 → camoufox не умеет);
  2. политика relaunch «только при реальной смене прокси» через _ensure_browser:
       - инстанса нет → launch с proxy_override;
       - тот же прокси → НЕ релончим (launch не зовётся повторно);
       - другой прокси → close + relaunch с новым;
       - proxy_override=None + инстанс на env → НЕ релончим (golden-parity).

camoufox НЕ запускается: _launch_browser мокается заглушкой, которая ставит
_browsers/_launched_proxy как реальный launch.

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

    python -m pytest test_server_proxy_override.py -q
"""

from __future__ import annotations

import asyncio
import importlib.util
from pathlib import Path

import pytest

_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)


@pytest.fixture(autouse=True)
def _reset_state(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(server, "_browsers", {})
    monkeypatch.setattr(server, "_browser_cms", {})
    monkeypatch.setattr(server, "_page_counters", {})
    monkeypatch.setattr(server, "_locks", {})
    monkeypatch.setattr(server, "_retry_tasks", {})
    monkeypatch.setattr(server, "_last_goto_at", {})
    monkeypatch.setattr(server, "_launched_proxy", {})
    monkeypatch.setattr(server, "_locks_guard", asyncio.Lock())


# ── _resolve_proxy_override ──────────────────────────────────────────────────────


def test_resolve_proxy_override_http() -> None:
    body = {"proxy": "http://u:p@pool:8080", "proxy_kind": "http"}
    assert server._resolve_proxy_override(body, "avito") == "http://u:p@pool:8080"


def test_resolve_proxy_override_missing_returns_none() -> None:
    assert server._resolve_proxy_override({"url": "x"}, "avito") is None
    assert server._resolve_proxy_override({"proxy": ""}, "avito") is None


def test_resolve_proxy_override_kind_defaults_http_when_absent() -> None:
    """proxy без proxy_kind трактуем как http (в пуле kind=http)."""
    assert server._resolve_proxy_override({"proxy": "http://pool:8080"}, "cian") == (
        "http://pool:8080"
    )


def test_resolve_proxy_override_socks5_ignored() -> None:
    """socks5 camoufox не умеет → None (env-fallback)."""
    body = {"proxy": "socks5://u:p@pool:1080", "proxy_kind": "socks5"}
    assert server._resolve_proxy_override(body, "avito") is None


# ── relaunch policy: _ensure_browser ─────────────────────────────────────────────


def _install_fake_launch(monkeypatch: pytest.MonkeyPatch) -> list[tuple[str, str | None]]:
    """Мок _launch_browser: пишет _browsers/_launched_proxy как реальный, пишет calls."""
    calls: list[tuple[str, str | None]] = []

    async def _fake_launch(provider: str, proxy_override: str | None = None) -> None:
        calls.append((provider, proxy_override))
        server._browsers[provider] = object()
        server._launched_proxy[provider] = (
            proxy_override if proxy_override is not None else server._provider_proxy(provider)
        )

    monkeypatch.setattr(server, "_launch_browser", _fake_launch)
    return calls


def test_ensure_browser_launches_with_override(monkeypatch: pytest.MonkeyPatch) -> None:
    calls = _install_fake_launch(monkeypatch)

    async def _run() -> bool:
        return await server._ensure_browser("avito", proxy_override="http://p1:8080")

    ok = asyncio.run(_run())
    assert ok is True
    assert calls == [("avito", "http://p1:8080")]
    assert server._launched_proxy["avito"] == "http://p1:8080"


def test_ensure_browser_same_proxy_no_relaunch(monkeypatch: pytest.MonkeyPatch) -> None:
    calls = _install_fake_launch(monkeypatch)

    async def _run() -> None:
        await server._ensure_browser("avito", proxy_override="http://p1:8080")
        await server._ensure_browser("avito", proxy_override="http://p1:8080")

    asyncio.run(_run())
    # Только один launch — второй вызов с тем же прокси НЕ релончит.
    assert calls == [("avito", "http://p1:8080")]


def test_ensure_browser_changed_proxy_relaunches(monkeypatch: pytest.MonkeyPatch) -> None:
    calls = _install_fake_launch(monkeypatch)

    async def _run() -> None:
        await server._ensure_browser("avito", proxy_override="http://p1:8080")
        await server._ensure_browser("avito", proxy_override="http://p2:8080")

    asyncio.run(_run())
    assert calls == [("avito", "http://p1:8080"), ("avito", "http://p2:8080")]
    assert server._launched_proxy["avito"] == "http://p2:8080"


def test_ensure_browser_none_override_env_no_relaunch(monkeypatch: pytest.MonkeyPatch) -> None:
    """proxy_override=None (флаг off) + инстанс на env → НЕ релончим (golden-parity)."""
    for var in ("BROWSER_PROXY_AVITO", "AVITO_PROXY_URL"):
        monkeypatch.setenv(var, "http://env:8080")
    calls = _install_fake_launch(monkeypatch)

    async def _run() -> None:
        await server._ensure_browser("avito")  # lazy launch на env
        await server._ensure_browser("avito")  # второй раз — env тот же

    asyncio.run(_run())
    assert calls == [("avito", None)]
    assert server._launched_proxy["avito"] == "http://env:8080"


def test_ensure_browser_env_then_pool_relaunches(monkeypatch: pytest.MonkeyPatch) -> None:
    """env-launched инстанс + пришёл pool-proxy → relaunch (реальная смена)."""
    for var in ("BROWSER_PROXY_AVITO", "AVITO_PROXY_URL"):
        monkeypatch.setenv(var, "http://env:8080")
    calls = _install_fake_launch(monkeypatch)

    async def _run() -> None:
        await server._ensure_browser("avito")  # env
        await server._ensure_browser("avito", proxy_override="http://pool:8080")  # смена

    asyncio.run(_run())
    assert calls == [("avito", None), ("avito", "http://pool:8080")]
