"""Unit tests для noise_loader — OSM-источники шума + инж.сети (#44).

Mock-based / pure — без Overpass и БД. Покрывает:
- расширенный набор инж.-сетевых категорий (#44: gas/water/sewerage/transformer/
  heat_substation)
- _build_overpass_query: node / way / nwr варианты
- el_type dispatch для point-like utility (nwr) vs линейных (way)
"""

from __future__ import annotations

from app.services.site_finder.noise_loader import (
    _NOISE_QUERIES,
    _UTILITY_POINT_VALUES,
    _build_overpass_query,
    _way_to_linestring_wkt,
)

# ── расширенные инж.-сетевые категории (#44) ─────────────────────────────────


def _road_classes() -> set[str | None]:
    return {road_class for _k, _v, src, road_class in _NOISE_QUERIES if src == "utility"}


def test_new_utility_categories_present() -> None:
    """#44: добавлены недостающие инж.сети — газ/вода/канализация/трансформатор/ЦТП."""
    classes = _road_classes()
    for expected in (
        "gas_pipeline",
        "water_main",
        "sewerage",
        "transformer",
        "heat_substation",
    ):
        assert expected in classes, f"road_class {expected!r} должен быть в _NOISE_QUERIES"


def test_existing_utility_categories_retained() -> None:
    """Старые категории не потеряны при расширении (regression guard)."""
    classes = _road_classes()
    for kept in ("power_line", "pipeline", "water_works", "wastewater_plant", "substation"):
        assert kept in classes


def test_pipeline_queries_use_pipeline_key() -> None:
    """pipeline=gas/water/sewerage маппится на OSM tag `pipeline`, не man_made."""
    pipeline_entries = [(k, v) for k, v, src, _rc in _NOISE_QUERIES if k == "pipeline"]
    values = {v for _k, v in pipeline_entries}
    assert values == {"gas", "water", "sewerage"}


def test_utility_point_values_cover_new_nodes() -> None:
    """Точечные/площадные инж.объекты помечены для nwr-запроса."""
    for v in ("substation", "transformer", "heat_substation", "water_works"):
        assert v in _UTILITY_POINT_VALUES


# ── _build_overpass_query ─────────────────────────────────────────────────────


def test_build_query_node() -> None:
    q = _build_overpass_query("aeroway", "aerodrome", "node")
    assert q.startswith("[out:json][timeout:30];")
    assert 'node["aeroway"="aerodrome"]' in q
    assert "out body;" in q


def test_build_query_way_uses_out_geom() -> None:
    """way → out geom (нужны координаты узлов для LineString)."""
    q = _build_overpass_query("highway", "motorway", "way")
    assert 'way["highway"="motorway"]' in q
    assert "out geom;" in q


def test_build_query_nwr_for_utility_points() -> None:
    """nwr ловит и node, и way (точка/площадка), с out geom."""
    q = _build_overpass_query("power", "transformer", "nwr")
    assert 'nwr["power"="transformer"]' in q
    assert "out geom;" in q


def test_build_query_bbox_present() -> None:
    """Bbox ЕКБ присутствует во всех вариантах запроса."""
    q = _build_overpass_query("power", "substation", "nwr")
    # south,west,north,east из EKB_BBOX
    assert "(56.7,60.5,56.95,60.75)" in q


# ── _way_to_linestring_wkt (используется для way-результатов nwr) ────────────


def test_way_to_linestring_valid() -> None:
    nodes = [{"lat": 56.8, "lon": 60.6}, {"lat": 56.81, "lon": 60.61}]
    wkt = _way_to_linestring_wkt(nodes)
    assert wkt == "LINESTRING(60.6 56.8, 60.61 56.81)"


def test_way_to_linestring_too_few_points() -> None:
    assert _way_to_linestring_wkt([{"lat": 56.8, "lon": 60.6}]) is None
