"""Тесты page_reservation_parser (#1091, #1062) — парсер постановлений об изъятии/резервировании."""

from __future__ import annotations

from datetime import date

import pytest

from app.services.scrapers.page_reservation_parser import (
    ReservationRecord,
    extract_reservations,
)

# ── Фикстура-текст с типичным содержимым постановления ────────────────────────

_FULL_TEXT = """
ПОСТАНОВЛЕНИЕ
Правительства Свердловской области
от 12.03.2024 № 509-ПП

Об изъятии земельных участков для государственных нужд в целях реконструкции улицы Татищева
в городе Екатеринбурге.

В соответствии со статьёй 49 Земельного кодекса Российской Федерации Правительство
Свердловской области ПОСТАНОВЛЯЕТ:

1. Изъять для государственных нужд следующие земельные участки:
   66:41:0101001:123 — площадь 450 кв.м;
   66:41:0101001:456 — площадь 300 кв.м;
   66:41:0101002:789 — площадь 680 кв.м.

2. Министерству по управлению государственным имуществом Свердловской области обеспечить
   государственную регистрацию права собственности.

Губернатор Свердловской области
"""

_REZERV_TEXT = """
ПОСТАНОВЛЕНИЕ
Правительства Свердловской области
от 05 июня 2024 г.
№ 210-ПП

О резервировании земельных участков для строительства дороги.

Зарезервировать земельные участки:
  66:41:0203010:11
  66:41:0203010:22
в целях строительства объекта дорожной инфраструктуры.
"""

_GARBAGE_TEXT = "Это текст без кад-номеров и без постановлений."

_BAD_DATE_TEXT = """
№ 99-ПП от 99.99.9999
Изъять земельный участок 66:41:0101001:500.
"""


# ── Тесты extract_reservations ────────────────────────────────────────────────


def test_extracts_cad_nums() -> None:
    """Все три КН извлекаются из постановления об изъятии."""
    records = extract_reservations(_FULL_TEXT)
    cad_nums = {r.cad_num for r in records}
    assert cad_nums == {"66:41:0101001:123", "66:41:0101001:456", "66:41:0101002:789"}


def test_deduplicates_cad_nums() -> None:
    """Дублирующиеся КН в документе схлопываются в одну запись."""
    text_with_dup = _FULL_TEXT + "\n66:41:0101001:123 повторно упомянут."
    records = extract_reservations(text_with_dup)
    cad_nums = [r.cad_num for r in records]
    assert cad_nums.count("66:41:0101001:123") == 1


def test_kind_izyatie() -> None:
    """Определяет 'изъятие' из текста с «Изъять»."""
    records = extract_reservations(_FULL_TEXT)
    assert all(r.reservation_kind == "изъятие" for r in records)


def test_kind_rezervirovanie() -> None:
    """Определяет 'резервирование' из текста с «резервирование»."""
    records = extract_reservations(_REZERV_TEXT)
    assert all(r.reservation_kind == "резервирование" for r in records)


def test_act_number_parsed() -> None:
    """Номер постановления 509-ПП корректно извлекается."""
    records = extract_reservations(_FULL_TEXT)
    assert records[0].act_number == "509-ПП"


def test_act_date_numeric() -> None:
    """Дата «от 12.03.2024» парсится как date(2024, 3, 12)."""
    records = extract_reservations(_FULL_TEXT)
    assert records[0].act_date == date(2024, 3, 12)


def test_act_date_word_month() -> None:
    """Дата «05 июня 2024 г.» парсится как date(2024, 6, 5)."""
    records = extract_reservations(_REZERV_TEXT)
    assert records[0].act_date == date(2024, 6, 5)


def test_purpose_extracted() -> None:
    """Поле purpose содержит осмысленный текст (best-effort)."""
    records = extract_reservations(_FULL_TEXT)
    # Хотя бы у одной записи purpose не None.
    assert any(r.purpose is not None for r in records)


def test_raw_excerpt_first_record() -> None:
    """raw_excerpt есть у первой записи (±120 символов)."""
    records = extract_reservations(_FULL_TEXT)
    first = records[0]
    assert first.raw_excerpt is not None
    assert "66:41:0101001:123" in (first.raw_excerpt or "")


def test_garbage_text_returns_empty() -> None:
    """Мусорный текст без КН → пустой список."""
    records = extract_reservations(_GARBAGE_TEXT)
    assert records == []


def test_empty_string_returns_empty() -> None:
    """Пустая строка → пустой список."""
    assert extract_reservations("") == []


def test_bad_date_does_not_raise() -> None:
    """Нечитаемая дата (99.99.9999) → act_date=None, не падает."""
    records = extract_reservations(_BAD_DATE_TEXT)
    assert len(records) == 1
    assert records[0].act_date is None
    assert records[0].cad_num == "66:41:0101001:500"


def test_default_kind_applied() -> None:
    """Если нет маркеров изъятия/резервирования — берётся default_kind."""
    text = "Земельный участок 66:41:0101001:777 подлежит ограничению."
    records = extract_reservations(text, default_kind="резервирование")
    assert records[0].reservation_kind == "резервирование"


def test_record_is_frozen() -> None:
    """ReservationRecord — frozen dataclass."""
    r = ReservationRecord(
        cad_num="66:41:0101001:1",
        reservation_kind="изъятие",
        act_number="509-ПП",
        act_date=None,
        purpose=None,
        raw_excerpt=None,
    )
    with pytest.raises((AttributeError, TypeError)):
        r.cad_num = "other"  # type: ignore[misc]
