"""Классификация «сетевых обременений» из ЗОУИТ (#1070).

Pure-function классификатор: по тексту ``type_zone`` ЗОУИТ (cad_zouit / НСПД-дамп)
определяет, является ли зона *охранной зоной инженерной сети* — и если да, к какому
виду сети относится (тепло/электро/газ/водоснабжение/связь/общая инженерная).

Контекст (#1070 ООО «Брик»): теплосеть 66:41:0000000:90310 проходит через пятно
застройки → реальное обременение. Эпик #1067 (слой ИРД для analyze) НЕ содержал явного
среза «сетевые обременения». ЗОУИТ-данные cad_zouit уже текут в analyze
(``quarter_dump_lookup._get_cad_zouit_overlaps``, #232/#243) и классифицируются в
gate_verdict, но как ОБЩИЕ ЗОУИТ-overlaps без выделенного «сетевого» среза.

Этот модуль выделяет сетевые охранные зоны в отдельную типизированную категорию, чтобы
analyze/фронт могли показать «сетевое обременение (теплосеть)» как самостоятельный флаг.

VERIFY против PROD (cad_zouit, 2026-06-14): сетевые охранные зоны РЕАЛЬНО присутствуют —
~1930 строк из 3485:
  • «Охранная зона инженерных коммуникаций»                — 1281
  • «Охранная зона тепловых сетей»                         —  548
  • «Охранная зона объектов электроэнергетики …»           —   69
  • «Охранная зона трубопроводов (газопроводов …)»         —   23
  • «Охранная зона линий и сооружений связи»               —    9

Зачем pure-function (без БД): тестируемо без фикстур, переиспользуемо и в
``_get_cad_zouit_overlaps`` (теггинг overlap-строк), и в ``gate_verdict`` (выделенный
warning-код), без дублирования keyword-таблиц.
"""

from __future__ import annotations

from typing import Literal

# Виды сетевого обременения. 'general' — «инженерные коммуникации» без указания вида сети.
NetworkKind = Literal[
    "thermal",  # теплоснабжение
    "electricity",  # электросети / объекты электроэнергетики
    "gas",  # газопроводы (входит в «трубопроводы …»)
    "water",  # водоснабжение / водоотведение / канализация
    "communication",  # линии и сооружения связи
    "pipeline",  # трубопроводы (нефте-/нефтепродукто-/аммиако- — не газ, но сетевое)
    "general",  # «охранная зона инженерных коммуникаций» (вид сети не уточнён)
]

# Человекочитаемые подписи (RU) для UI/детали обременения.
NETWORK_KIND_LABELS: dict[str, str] = {
    "thermal": "теплоснабжение",
    "electricity": "электроснабжение",
    "gas": "газоснабжение",
    "water": "водоснабжение/водоотведение",
    "communication": "связь",
    "pipeline": "трубопровод",
    "general": "инженерные коммуникации",
}

# Упорядоченные правила «подстрока type_zone → вид сети». Порядок ВАЖЕН: более
# специфичные виды проверяются раньше «general» (инженерные коммуникации).
# Все подстроки lowercase; матч по `substring in type_zone.lower()`.
# Сверено с distinct type_zone из cad_zouit PROD (2026-06-14).
_NETWORK_RULES: tuple[tuple[NetworkKind, tuple[str, ...]], ...] = (
    ("thermal", ("тепл",)),  # «… тепловых сетей»
    ("electricity", ("электроэнерг", "электросет", "лэп", "линий электропередач")),
    ("gas", ("газопров", "газоснаб")),
    ("water", ("водопров", "водоснаб", "водоотвед", "канализ")),
    ("communication", ("линий и сооружений связи", "сооружений связи", "линий связи")),
    # «Охранная зона трубопроводов (газопроводов, нефтепроводов …)» — если газ не
    # сматчился выше, относим к pipeline (нефте-/нефтепродукто-/аммиако-проводы).
    ("pipeline", ("трубопров", "нефтепров", "нефтепродуктопров", "аммиакопров")),
    # Самое общее — «охранная зона инженерных коммуникаций» (вид сети не уточнён).
    ("general", ("инженерных коммуникаций", "инженерной инфраструктур", "инженерных сет")),
)

# Зона должна быть именно ОХРАННОЙ (обременение), а не, например, «зона публичного
# сервитута» без сетевой привязки. Требуем маркер охранной/защитной зоны ИЛИ явный
# сетевой вид в тексте — иначе «инженерные коммуникации» в произвольной зоне дало бы
# ложные срабатывания. На практике cad_zouit использует «Охранная зона …».
_PROTECTION_MARKERS: tuple[str, ...] = ("охранная зона", "охранн")


def classify_network_zone(type_zone: str | None) -> NetworkKind | None:
    """Определить вид сетевого обременения по тексту ``type_zone`` ЗОУИТ.

    Args:
        type_zone: текст типа зоны (cad_zouit.type_zone / НСПД-дамп). None/пусто → None.

    Returns:
        ``NetworkKind`` если зона — охранная зона инженерной сети; иначе ``None``
        (зона не сетевая: СЗЗ, ЗРЗ, сервитут без сети, природные ЗОУИТ и т.п.).
    """
    if not type_zone:
        return None
    tz = type_zone.lower()
    is_protection = any(m in tz for m in _PROTECTION_MARKERS)
    # ЗСО (зона санитарной охраны ИСТОЧНИКА водоснабжения) — это зона вокруг водозабора,
    # НЕ охранная зона водопроводной СЕТИ → не сетевое обременение. Иначе water-needles
    # («водоснаб») ложно отнесли бы ЗСО к сети и флипнули verdict на blocker.
    is_sanitary_source = "санитарн" in tz
    for kind, needles in _NETWORK_RULES:
        if any(n in tz for n in needles):
            # «general» (инженерные коммуникации) принимаем только при маркере охранной
            # зоны — иначе слишком широкая ловля. Специфичные виды (тепло/газ/…) сами по
            # себе достаточно сетевые, но в cad_zouit они тоже всегда «Охранная зона …».
            if kind == "general" and not is_protection:
                continue
            # water-зона санитарной охраны источника — не сеть, пропускаем.
            if kind == "water" and is_sanitary_source:
                continue
            return kind
    return None


def network_kind_label(kind: NetworkKind | None) -> str | None:
    """RU-подпись вида сети для UI/детали (None → None)."""
    if kind is None:
        return None
    return NETWORK_KIND_LABELS.get(kind, kind)


def is_network_obremenenie(type_zone: str | None) -> bool:
    """True если ЗОУИТ — сетевое обременение (любой вид сети)."""
    return classify_network_zone(type_zone) is not None


__all__ = [
    "NETWORK_KIND_LABELS",
    "NetworkKind",
    "classify_network_zone",
    "is_network_obremenenie",
    "network_kind_label",
]
