"""Offline smoke for Stage 4a API endpoints. Mocks scrapers + uses FastAPI TestClient.

Тестируем routing/schema только, реальные scrape/save мокаем.
"""

import os
import sys
from unittest.mock import MagicMock

# Используем psycopg (v3) driver — psycopg2 не установлен в этом проекте
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")

# WeasyPrint требует GTK-библиотеки (нет на Windows/CI без GTK).
# trade_in.py импортирует generate_trade_in_pdf на уровне модуля —
# заглушаем weasyprint до любого импорта из app.
_wp_mock = MagicMock()
sys.modules.setdefault("weasyprint", _wp_mock)
sys.modules.setdefault("weasyprint.CSS", _wp_mock)
sys.modules.setdefault("weasyprint.HTML", _wp_mock)

import pytest  # noqa: E402  — импорт после weasyprint-мока (намеренно, см. выше)
from fastapi import FastAPI  # noqa: E402
from fastapi.testclient import TestClient  # noqa: E402


@pytest.fixture
def app() -> FastAPI:
    """Mount только нужные роутеры с overridden DB dep."""
    from app.api.v1 import admin as admin_module
    from app.api.v1 import trade_in as trade_in_module
    from app.core.db import get_db

    application = FastAPI()
    application.include_router(admin_module.router, prefix="/api/v1/admin")
    application.include_router(trade_in_module.router, prefix="/api/v1/trade-in")

    def override_db():
        mock_db = MagicMock()
        yield mock_db

    application.dependency_overrides[get_db] = override_db
    return application


def test_scrape_avito_house_endpoint_exists(app):
    """Endpoint mounted на /api/v1/admin/scrape/avito-house — POST + query param."""
    client = TestClient(app)
    # Без mocks scraper упадёт — но мы тестим только что 422 (missing query) != 404.
    r = client.post("/api/v1/admin/scrape/avito-house")
    assert r.status_code == 422  # missing 'house_url' query → validation error, не 404


def test_scrape_avito_detail_endpoint_exists(app):
    client = TestClient(app)
    r = client.post("/api/v1/admin/scrape/avito-detail")
    assert r.status_code == 422


def test_scrape_avito_imv_endpoint_exists(app):
    client = TestClient(app)
    r = client.post("/api/v1/admin/scrape/avito-imv")
    assert r.status_code == 422  # missing required query params


def test_get_estimate_houses_returns_empty_when_no_coords(app):
    """Estimate без lat/lon → пустой массив."""
    from app.core.db import get_db

    fake_target = MagicMock()
    fake_target.lat = None
    fake_target.lon = None

    def override_db_no_coords():
        m = MagicMock()
        m.execute.return_value.fetchone.return_value = fake_target
        yield m

    app.dependency_overrides[get_db] = override_db_no_coords
    client = TestClient(app)
    # #690: /houses теперь скоупится по владельцу (X-Authenticated-User). Мок отдаёт
    # непустой row (fake_target) → guard доходит до auth-проверки и без заголовка
    # вернул бы 401. admin байпасит ownership → эндпоинт исполняется как раньше.
    r = client.get(
        "/api/v1/trade-in/estimate/00000000-0000-0000-0000-000000000000/houses",
        headers={"X-Authenticated-User": "admin"},
    )
    assert r.status_code == 200
    assert r.json() == []


def test_get_imv_benchmark_unavailable(app):
    """Если IMV нет в БД для estimate — возвращаем available=False или 404 (no estimate)."""
    from app.core.db import get_db

    def override_db_empty():
        m = MagicMock()
        # Linked SELECT возвращает None, fallback SELECT тоже None
        m.execute.return_value.fetchone.return_value = None
        yield m

    app.dependency_overrides[get_db] = override_db_empty
    client = TestClient(app)
    r = client.get("/api/v1/trade-in/estimate/00000000-0000-0000-0000-000000000000/imv-benchmark")
    # endpoint вернёт 404 потому что fallback тоже не находит address → нужен estimate row
    # Этот тест проверяет что эндпоинт mounted; 404 ОК
    assert r.status_code in (200, 404)


def test_avito_city_sweep_single_run_guard(app, monkeypatch):
    """#single-run: второй avito sweep при уже running → 409.

    Параллельные sweep'ы делят один прокси-IP → удвоенный rate → Avito бан
    (incident 2026-05-31: runs #26+#27 одновременно → оба banned).
    """
    from app.api.v1 import admin as admin_module

    monkeypatch.setattr(admin_module, "has_running_run", lambda db, source: True)
    client = TestClient(app)
    r = client.post(
        "/api/v1/admin/scrape/avito-city-sweep",
        json={"pages_per_anchor": 1, "request_delay_sec": 5.0},
    )
    assert r.status_code == 409
    assert "уже выполняется" in r.json()["detail"]
