"""Unit-тесты гуманизации сетевых охранных зон §3 (#2119 C2/C3).

Pure / без БД. Покрывает:
- refine_net_kind: type_zone-база + дочистка general по name_by_doc (тепло/газ/электро).
- human_label: regex-кейсы (ВЛ 0,4 кВ, КЛ 10 кВ, теплосеть с кад.номером, газопровод,
  нераспознанное), last-mile-подача ≤1 кВ, извлечение ТП.
- _shape_network_zones: сортировка по distance + лимит _NETWORK_ZONE_PER_KIND per-kind.

Строки name_by_doc — реальные образцы с прода (cad_zouit, 2026-07-03).
"""

from __future__ import annotations

from app.services.site_finder import connection_capacity_lookup as ccl
from app.services.site_finder.network_zone_humanize import human_label, refine_net_kind

# type_zone-константы (как в проде / SQL-белом списке).
_TZ_THERMAL = "Охранная зона тепловых сетей"
_TZ_GENERAL = "Охранная зона инженерных коммуникаций"
_TZ_ELECTRIC = (
    "Охранная зона объектов электроэнергетики (объектов электросетевого хозяйства "
    "и объектов по производству электрической энергии)"
)
_TZ_PIPELINE = (
    "Охранная зона трубопроводов (газопроводов, нефтепроводов и "
    "нефтепродуктопроводов, аммиакопроводов)"
)
_TZ_COMMS = "Охранная зона линий и сооружений связи"


# ── refine_net_kind: база по type_zone ────────────────────────────────────────


def test_refine_thermal_type_zone_stays_thermal() -> None:
    """Специфичный type_zone «тепловых сетей» → thermal независимо от name."""
    assert refine_net_kind(_TZ_THERMAL, "Тепловая сеть от ЦТП Баумана") == "thermal"


def test_refine_electric_type_zone_stays_electricity() -> None:
    assert refine_net_kind(_TZ_ELECTRIC, "Охранная зона ВЛ 0,4кВ ТП 3164") == "electricity"


def test_refine_pipeline_type_zone_stays_pipeline_or_gas() -> None:
    """type_zone «трубопроводов (газопроводов…)» c газом в имени → gas."""
    assert refine_net_kind(_TZ_PIPELINE, "Охранная зона газопровода высокого давления") == "gas"


def test_refine_comms_type_zone_stays_communication() -> None:
    assert refine_net_kind(_TZ_COMMS, "Охранная зона сооружения связи ООО «ВЕРТИКАЛЬ-УРАЛ»") == (
        "communication"
    )


def test_refine_non_network_returns_none() -> None:
    assert refine_net_kind("Санитарно-защитная зона", "что угодно") is None
    assert refine_net_kind(None, None) is None


# ── refine_net_kind: дочистка general по name_by_doc ───────────────────────────


def test_refine_general_thermal_by_name() -> None:
    """«Охранная зона инженерных коммуникаций» с теплосетью в name → thermal (прод-кейс)."""
    name = (
        "Охранная зона объекта теплоснабжения «Трубопровод (тепловые сети) "
        "с кадастровым номером 66:41:0108079:76"
    )
    assert refine_net_kind(_TZ_GENERAL, name) == "thermal"


def test_refine_general_gas_by_name() -> None:
    """general с газопроводом в name → gas (прод-кейс догазификации)."""
    name = (
        "Охранная зона газораспределительной сети в отношении объекта: Газопровод низкого давления"
    )
    assert refine_net_kind(_TZ_GENERAL, name) == "gas"


def test_refine_general_electricity_by_vl_regex() -> None:
    """general с «ВЛ 0,4 кВ» в name → electricity (даже без слова электросеть)."""
    assert refine_net_kind(_TZ_GENERAL, "Охранная зона ВЛ 0,4 кВ от ТП 71796") == "electricity"


def test_refine_general_electricity_by_transformer() -> None:
    """general «Трансформаторная подстанция» без «ВЛ N кВ» → electricity (прод-кейс)."""
    name = (
        'Охранная зона объекта "Трансформаторная подстанция", кадастровый номер 66:41:0404019:333'
    )
    assert refine_net_kind(_TZ_GENERAL, name) == "electricity"


def test_refine_general_gas_distribution_not_electricity() -> None:
    """«газораспределительн» → gas раньше, «распределительн»-электро не перехватывает."""
    assert refine_net_kind(_TZ_GENERAL, "Охранная зона газораспределительной сети") == "gas"


def test_refine_general_unrecognized_stays_general() -> None:
    """general без распознанного вида → остаётся general («вид не уточнён»)."""
    assert refine_net_kind(_TZ_GENERAL, "Охранная зона неизвестного объекта") == "general"
    assert refine_net_kind(_TZ_GENERAL, None) == "general"


# ── human_label: thermal ──────────────────────────────────────────────────────


def test_label_thermal_with_cad_num() -> None:
    """thermal с кад.номером трубопровода в name → подпись включает кад.№."""
    name = (
        "Охранная зона объекта теплоснабжения с кадастровым номером "
        "66:41:0106021:815 (Тепловая сеть)"
    )
    assert human_label("thermal", name) == (
        "Тепловая сеть (по охранной зоне), кад. № 66:41:0106021:815"
    )


def test_label_thermal_without_cad_num() -> None:
    name = "Охранная зона объекта теплоснабжения «Тепловая сеть от ЦТП Баумана, 7а»"
    assert human_label("thermal", name) == "Тепловая сеть (по охранной зоне)"


# ── human_label: electricity (ВЛ/КЛ N кВ + ТП) ─────────────────────────────────


def test_label_electricity_vl_04kv_last_mile() -> None:
    """ВЛ 0,4 кВ (≤1 кВ) — last-mile: напряжение как второстепенный факт, ТП — якорь."""
    name = "Охранная зона ВЛ 0,4кВ ТП 3164 руб.№16 от оп. 3 до уч. 66:41:0106128:17"
    assert human_label("electricity", name) == (
        "Ввод от ТП 3164 (по охранной зоне, 0,4 кВ — last-mile)"
    )


def test_label_electricity_kl_10kv_magistral() -> None:
    """КЛ 10 кВ (>1 кВ) — магистральный ввод: напряжение в основной подписи."""
    name = "Охранная зона КЛ 10 кВ от ТП 5521 до РП 12"
    assert human_label("electricity", name) == "Ввод 10 кВ от ТП 5521 (по охранной зоне)"


def test_label_electricity_vl_6kv_with_ktpn() -> None:
    """ВЛ 6 кВ + КТПН — берём первый якорный ТП/КТПН, напряжение магистральное."""
    name = (
        "Охранная зона Линейное ответвление от ВЛ 6 кВ ТП 3736-3747 "
        "с отпайками, ТП 32505 (КТПН п/№ 589)"
    )
    assert human_label("electricity", name) == "Ввод 6 кВ от ТП 3736-3747 (по охранной зоне)"


def test_label_electricity_no_voltage_but_tp() -> None:
    """Нет напряжения, но есть ТП → «Электросеть от ТП M»."""
    assert human_label("electricity", "Охранная зона объекта от ТП 999") == (
        "Электросеть от ТП 999 (по охранной зоне)"
    )


def test_label_electricity_bare() -> None:
    """Нет ни напряжения, ни ТП → generic электро-подпись."""
    assert human_label("electricity", "Охранная зона объекта электросетевого хозяйства") == (
        "Объект электросетевого хозяйства (по охранной зоне)"
    )


# ── human_label: gas / pipeline / general / прочие ─────────────────────────────


def test_label_gas_with_cad_num() -> None:
    name = "Охранная зона газопровода высокого давления, кадастровый номер 66:41:0110003:7983"
    assert human_label("gas", name) == (
        "Трубопровод (газо-/нефтепровод) по охранной зоне, кад. № 66:41:0110003:7983"
    )


def test_label_pipeline_without_cad_num() -> None:
    assert human_label("pipeline", "Охранная зона нефтепровода") == (
        "Трубопровод (газо-/нефтепровод) по охранной зоне"
    )


def test_label_general_unrecognized() -> None:
    assert human_label("general", "Охранная зона инженерных коммуникаций") == (
        "Инженерные коммуникации (вид не уточнён)"
    )


def test_label_communication_and_water() -> None:
    assert human_label("communication", "Охранная зона сооружения связи") == (
        "Линия связи (по охранной зоне)"
    )
    assert human_label("water", "Охранная зона водопровода") == (
        "Сеть водоснабжения/водоотведения (по охранной зоне)"
    )


def test_label_none_fallback() -> None:
    assert human_label(None, "что угодно") == "Охранная зона"


# ── _shape_network_zones: сортировка + лимит per-kind ──────────────────────────


def _row(type_zone: str, name: str, dist: int, reg: str = "reg") -> dict:
    """Имитация .mappings()-строки cad_zouit (dict доступ по ключам)."""
    return {
        "type_zone": type_zone,
        "name_by_doc": name,
        "reg_numb_border": reg,
        "distance_m": dist,
    }


def test_shape_limits_two_per_kind_sorted() -> None:
    """До 2 ближайших НА net_kind; в пределах вида — по distance ASC."""
    rows = [
        _row(_TZ_THERMAL, "Тепловая сеть A", 200, "r1"),
        _row(_TZ_THERMAL, "Тепловая сеть B", 50, "r2"),
        _row(_TZ_THERMAL, "Тепловая сеть C", 120, "r3"),  # третья thermal — отбрасывается
        _row(_TZ_ELECTRIC, "Охранная зона ВЛ 10 кВ ТП 5", 80, "r4"),
    ]
    out = ccl._shape_network_zones(rows)
    thermal = [z for z in out if z["net_kind"] == "thermal"]
    electric = [z for z in out if z["net_kind"] == "electricity"]
    assert len(thermal) == 2  # лимит 2 из 3
    assert [z["distance_m"] for z in thermal] == [50, 120]  # ближайшие две, отсортированы
    assert len(electric) == 1


def test_shape_intersecting_zone_distance_zero() -> None:
    """distance_m=0 (зона пересекает участок) сохраняется и идёт первой."""
    rows = [
        _row(_TZ_THERMAL, "Тепловая сеть далеко", 250, "r1"),
        _row(_TZ_GENERAL, "объект теплоснабжения кад. 66:41:0108079:76", 0, "r2"),
    ]
    out = ccl._shape_network_zones(rows)
    assert out[0]["distance_m"] == 0
    # general с теплом в name дочистился до thermal.
    assert out[0]["net_kind"] == "thermal"


def test_shape_outer_sort_by_distance_then_kind() -> None:
    """Внешний порядок: distance ASC, затем net_kind (стабильно)."""
    rows = [
        _row(_TZ_ELECTRIC, "ВЛ 10 кВ ТП 1", 100, "r1"),
        _row(_TZ_THERMAL, "Тепловая сеть", 100, "r2"),
        _row(_TZ_GENERAL, "Газопровод низкого давления", 30, "r3"),
    ]
    out = ccl._shape_network_zones(rows)
    assert out[0]["distance_m"] == 30  # gas ближе всех
    assert out[0]["net_kind"] == "gas"
    # при равном distance=100 — electricity раньше thermal (алфавит).
    assert [z["net_kind"] for z in out[1:]] == ["electricity", "thermal"]


def test_shape_empty() -> None:
    assert ccl._shape_network_zones([]) == []
