"""Тесты для WAF cooldown guard (#2443, #2445 D1) на ad-hoc catalog-scrape эндпоинтах.

POST /api/v1/admin/scrape/kn-catalog-objects и /kn-catalog-flats бьют по тому же
/сервисы/* BrowserSession path family, что вызвал DOM.РФ WAF hard-ban 2026-05-24
(#2443). Beat schedule для обоих тасков отключён по этой причине — эти эндпоинты
должны отказывать в запуске БЕЗ явного i_understand_waf_risk=true, чтобы случайный
smoke-тест/re-trigger не углубил бан.
"""

from __future__ import annotations

from unittest.mock import MagicMock, patch

from fastapi.testclient import TestClient

from app.main import app

OBJECTS_ENDPOINT = "/api/v1/admin/scrape/kn-catalog-objects"
FLATS_ENDPOINT = "/api/v1/admin/scrape/kn-catalog-flats"


def _mock_apply_async_result(task_id: str = "fake-task-id") -> MagicMock:
    result = MagicMock()
    result.id = task_id
    return result


def test_kn_catalog_objects_refuses_without_override() -> None:
    """Без i_understand_waf_risk (default False) — 400, task НЕ dispatched."""
    with patch(
        "app.workers.tasks.scrape_kn_catalog_objects.scrape_kn_catalog_objects.apply_async"
    ) as mock_apply_async:
        resp = TestClient(app).post(OBJECTS_ENDPOINT, json={"region_code": 66})

    assert resp.status_code == 400
    detail = resp.json()["detail"]
    assert "2443" in detail
    assert "i_understand_waf_risk" in detail
    mock_apply_async.assert_not_called()


def test_kn_catalog_objects_dispatches_with_override() -> None:
    """С i_understand_waf_risk=true — таск dispatched как раньше."""
    with patch(
        "app.workers.tasks.scrape_kn_catalog_objects.scrape_kn_catalog_objects.apply_async",
        return_value=_mock_apply_async_result("obj-task-1"),
    ) as mock_apply_async:
        resp = TestClient(app).post(
            OBJECTS_ENDPOINT,
            json={"region_code": 66, "max_objects": 3, "i_understand_waf_risk": True},
        )

    assert resp.status_code == 200
    body = resp.json()
    assert body["task_id"] == "obj-task-1"
    assert body["max_objects"] == 3
    mock_apply_async.assert_called_once()
    call_kwargs = mock_apply_async.call_args[1]["kwargs"]
    assert call_kwargs["region_code"] == 66
    assert call_kwargs["max_objects"] == 3


def test_kn_catalog_flats_refuses_without_override() -> None:
    """Без i_understand_waf_risk (default False) — 400, task НЕ dispatched."""
    with patch(
        "app.workers.tasks.scrape_kn_catalog_flats.scrape_kn_catalog_flats.apply_async"
    ) as mock_apply_async:
        resp = TestClient(app).post(FLATS_ENDPOINT, json={"region_code": 66})

    assert resp.status_code == 400
    detail = resp.json()["detail"]
    assert "2443" in detail
    assert "i_understand_waf_risk" in detail
    mock_apply_async.assert_not_called()


def test_kn_catalog_flats_dispatches_with_override() -> None:
    """С i_understand_waf_risk=true — таск dispatched как раньше."""
    with patch(
        "app.workers.tasks.scrape_kn_catalog_flats.scrape_kn_catalog_flats.apply_async",
        return_value=_mock_apply_async_result("flat-task-1"),
    ) as mock_apply_async:
        resp = TestClient(app).post(
            FLATS_ENDPOINT,
            json={"region_code": 66, "max_flats": 3, "i_understand_waf_risk": True},
        )

    assert resp.status_code == 200
    body = resp.json()
    assert body["task_id"] == "flat-task-1"
    assert body["max_flats"] == 3
    mock_apply_async.assert_called_once()
    call_kwargs = mock_apply_async.call_args[1]["kwargs"]
    assert call_kwargs["region_code"] == 66
    assert call_kwargs["max_flats"] == 3


def test_kn_catalog_objects_explicit_false_still_refuses() -> None:
    """i_understand_waf_risk=false явно (не только default) — тоже 400."""
    with patch(
        "app.workers.tasks.scrape_kn_catalog_objects.scrape_kn_catalog_objects.apply_async"
    ) as mock_apply_async:
        resp = TestClient(app).post(
            OBJECTS_ENDPOINT, json={"region_code": 66, "i_understand_waf_risk": False}
        )

    assert resp.status_code == 400
    mock_apply_async.assert_not_called()
