"""Тесты числового экстракта предельных параметров ПЗЗ (#1067 C8b).

Строки limit_params — РЕАЛЬНЫЕ otherDescription с живого ЕКБ-геопортала urbanCard (probe
2026-06-06: зоны Ц-1, ЦС-1, ЦС-6). Проверяют что regex берёт числа из «– NUN кв.м/%/этаж»,
игнорит «не подлежат установлению», тянет per-ВРИ повторы и десятичную запятую.
"""

from __future__ import annotations

from app.services.scrapers.ekb_geoportal_client import ZoneRegulation
from app.services.site_finder.zone_regulation import (
    _headline_scalars,
    parse_limit_params,
    upsert_zone_regulation,
)

# Реальный otherDescription зоны Ц-1 (среднеэтажная 1500 / многоэтажная 3000 + прочее).
_C1 = (
    "минимальная площадь земельного участка - 1500 кв. м;\n"
    "минимальные отступы от границ земельных участков ... не подлежат установлению;\n"
    "предельное количество этажей или предельная высота зданий, строений, сооружений не "
    "подлежат установлению;\n"
    "максимальный процент застройки в границах земельного участка, определяемый как отношение "
    "суммарной площади земельного участка, которая может быть застроена, ко всей площади "
    "земельного участка – 100%; Иные предельные параметры:\n"
    "коэффициент строительного использования земельного участка – 4;\n"
    "процент застройки подземного пространства – 100%;\n"
    "минимальная площадь земельного участка - 3000 кв. м;\n"
    "максимальный процент застройки в границах земельного участка ... для многоэтажной жилой "
    "застройки – 100%;\n"
)

# Зона ЦС-1: КСИ 2,4 / 2,5 / 2 (десятичная запятая), этажность 5, застройка 80%, площадь 5000.
_CS1 = (
    "максимальный процент застройки в границах земельного участка, определяемый как отношение "
    "суммарной площади земельного участка ... ко всей площади земельного участка – 80%;\n"
    "коэффициент строительного использования земельного участка (за исключением ...) – 2,4;\n"
    "коэффициент строительного использования земельного участка (только в отношении ...) - 2,5;\n"
    "коэффициент строительного использования земельного участка (только в отношении ...) – 2;\n"
    'предельная этажность (только в отношении ... "гостиничное обслуживание") – 5 этажей;\n'
    "максимальная общая площадь ... – 5 000 кв.м;\n"
)

# Зона ЦС-6: всё «не подлежат установлению» — числовой экстракт пуст.
_CS6 = (
    "предельные (минимальные и (или) максимальные) размеры земельных участков, в том числе их "
    "площадь, не подлежат установлению;\n"
    "максимальный процент застройки в границах земельного участка ... не подлежит установлению;\n"
    "Иные предельные параметры разрешенного строительства ... не подлежат установлению\n"
)


def test_c1_min_area_per_vri() -> None:
    """Обе min-площади (среднеэтажная 1500 + многоэтажная 3000) тянутся как список."""
    p = parse_limit_params([_C1])
    assert p["min_parcel_area_m2"] == [1500.0, 3000.0]


def test_c1_far_and_building_and_underground() -> None:
    p = parse_limit_params([_C1])
    assert p["max_far"] == [4.0]
    assert p["max_building_pct"] == [100.0, 100.0]  # per-ВРИ повтор
    assert p["underground_pct"] == [100.0]


def test_c1_no_floors_or_height_when_not_established() -> None:
    """«не подлежат установлению» для этажей/высоты → ключи отсутствуют."""
    p = parse_limit_params([_C1])
    assert "max_floors" not in p
    assert "max_height_m" not in p


def test_cs1_decimal_comma_far() -> None:
    """КСИ с десятичной запятой (2,4 / 2,5 / 2) парсятся в float."""
    p = parse_limit_params([_CS1])
    assert p["max_far"] == [2.4, 2.5, 2.0]


def test_cs1_floors_and_thousand_separated_area() -> None:
    p = parse_limit_params([_CS1])
    assert p["max_floors"] == [5.0]
    assert p["max_building_pct"] == [80.0]


def test_cs6_all_not_established_is_empty() -> None:
    assert parse_limit_params([_CS6]) == {}


def test_empty_inputs() -> None:
    assert parse_limit_params(None) == {}
    assert parse_limit_params([]) == {}
    assert parse_limit_params(["", None]) == {}  # type: ignore[list-item]


def test_headline_scalars_most_permissive() -> None:
    """min_parcel_area = наименьший минимум; прочие = max; max_floors = int."""
    p = parse_limit_params([_C1, _CS1])
    s = _headline_scalars(p)
    assert s["min_parcel_area_m2"] == 1500.0  # min из [1500, 3000]
    assert s["max_far"] == 4.0  # max из [4, 2.4, 2.5, 2]
    assert s["max_building_pct"] == 100.0
    assert s["max_floors"] == 5
    assert isinstance(s["max_floors"], int)
    assert s["max_height_m"] is None  # нет ни одного → None


def test_headline_scalars_empty() -> None:
    s = _headline_scalars({})
    assert all(v is None for v in s.values())
    assert set(s) == {
        "max_far",
        "max_building_pct",
        "underground_pct",
        "min_parcel_area_m2",
        "max_floors",
        "max_height_m",
    }


def test_upsert_skips_when_no_zone_index() -> None:
    """Регламент без zone_index не пишется (None), БД не трогается."""
    reg = ZoneRegulation(
        zone_index=None,
        zone_full_name=None,
        main_vri=[],
        conditional_vri=[],
        auxiliary_vri=[],
        limit_params=[],
    )

    class _Boom:
        def begin_nested(self):  # pragma: no cover - не должен вызываться
            raise AssertionError("db не должна трогаться при отсутствии zone_index")

    assert upsert_zone_regulation(_Boom(), reg) is None  # type: ignore[arg-type]
