"""Parity-proof: admin.py Yandex debug-роуты (#2305, Group A) — legacy vs scraper_kit.

`app/api/v1/admin.py::scrape_yandex_detail` / `scrape_yandex_valuation` теперь
зовут `scraper_kit.providers.yandex.{detail,valuation}` вместо
`app.services.scrapers.yandex_{detail,valuation}` (тот же `/scrape` root-роут —
`scraper_kit.providers.yandex.serp.YandexRealtyScraper`). Каждая функция ниже —
чистое, offline-тестируемое parsing-ядро (см. `tests/support/README.md`:
"Live network/DB в parity-тестах избегайте").

- `_parse_card_fields` — `scrape_yandex_detail` → `YandexDetailScraper.fetch_detail`
  извлекает (rooms, area, living, kitchen, ceiling, floor, total_floors) этой функцией.
- `YandexValuationScraper._parse_house_meta` (@staticmethod) — `scrape_yandex_valuation`
  → `fetch_house_history` извлекает house-level метаданные этой функцией.
- `_entity_to_lot` — `/scrape` root-роут → `YandexRealtyScraper.fetch_around`
  конвертирует gate-API entity → ScrapedLot этой функцией (также доказывает
  ScrapedLot-контракт, общий с `save_listings`/`scripts/ingest_domclick_jsonl.py`).
"""

from __future__ import annotations

import os

os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost/test_db")

from scraper_kit.providers.yandex.detail import _parse_card_fields as kit_parse_card_fields
from scraper_kit.providers.yandex.serp import _entity_to_lot as kit_entity_to_lot
from scraper_kit.providers.yandex.valuation import (
    YandexValuationScraper as KitYandexValuationScraper,
)

from app.services.scrapers.yandex_detail import _parse_card_fields as legacy_parse_card_fields
from app.services.scrapers.yandex_realty import _entity_to_lot as legacy_entity_to_lot
from app.services.scrapers.yandex_valuation import (
    YandexValuationScraper as LegacyYandexValuationScraper,
)
from tests.support.parity import assert_parity

_CARD = {
    "roomsTotal": 2,
    "area": {"value": 54.3},
    "livingSpace": {"value": 32.1},
    "kitchenSpace": {"value": 9.5},
    "ceilingHeight": 2.7,
    "floorsOffered": [5],
    "floorsTotal": 9,
}
_CARD_STUDIO = {
    "house": {"studio": True},
    "area": {"value": 25.0},
    "floorsOffered": [1],
    "floorsTotal": 5,
}

_VALUATION_BODY_TEXT = (
    "Дом 2015 года. 9 этажей. 2,7 м потолки. Лифт есть. 120 объектов продажи. Панорама двора."
)

# gate-API entity (realty.yandex.ru), без creationDate/author — исключает
# time-dependent поля (days_on_market) из фикстуры (deterministic offline).
_YANDEX_ENTITY = {
    "offerId": "12345",
    "url": "//realty.yandex.ru/offer/12345",
    "price": {"value": 8500000, "valuePerPart": 157000},
    "area": {"value": 54.3},
    "livingSpace": {"value": 32.1},
    "kitchenSpace": {"value": 9.5},
    "roomsTotal": 2,
    "floorsOffered": [5],
    "floorsTotal": 9,
    "ceilingHeight": 2.7,
    "building": {"builtYear": 2015, "buildingType": "MONOLIT"},
    "location": {
        "point": {"latitude": 56.83, "longitude": 60.6},
        "geocoderAddress": "Екатеринбург, ул. Тестовая, 1",
    },
    "mainImages": ["//avatars.mds.yandex.net/img1.jpg"],
}


def test_parity_yandex_detail_card_fields() -> None:
    """`scrape_yandex_detail` (admin.py) → fetch_detail → _parse_card_fields."""
    assert_parity(
        legacy_fn=legacy_parse_card_fields,
        kit_fn=kit_parse_card_fields,
        fixtures=[(_CARD,), (_CARD_STUDIO,)],
    )


def test_yandex_valuation_house_meta_are_class_distinct() -> None:
    """Sanity: legacy/kit ValuationHouseMeta — разные классы, обычный `==` даёт False."""
    legacy_result = LegacyYandexValuationScraper._parse_house_meta(_VALUATION_BODY_TEXT)
    kit_result = KitYandexValuationScraper._parse_house_meta(_VALUATION_BODY_TEXT)

    assert type(legacy_result) is not type(kit_result)
    assert legacy_result.model_dump() == kit_result.model_dump()


def test_parity_yandex_valuation_house_meta() -> None:
    """`scrape_yandex_valuation` (admin.py) → fetch_house_history → _parse_house_meta."""
    assert_parity(
        legacy_fn=LegacyYandexValuationScraper._parse_house_meta,
        kit_fn=KitYandexValuationScraper._parse_house_meta,
        fixtures=[_VALUATION_BODY_TEXT],
    )


def test_yandex_entity_to_lot_are_class_distinct() -> None:
    """Sanity: legacy/kit ScrapedLot — разные классы (pydantic), обычный `==` даёт False."""
    legacy_result = legacy_entity_to_lot(_YANDEX_ENTITY)
    kit_result = kit_entity_to_lot(_YANDEX_ENTITY)

    assert legacy_result is not None
    assert kit_result is not None
    assert type(legacy_result) is not type(kit_result)
    assert legacy_result.model_dump() == kit_result.model_dump()


def test_parity_yandex_serp_entity_to_lot() -> None:
    """`/scrape` root-роут (admin.py) → YandexRealtyScraper.fetch_around → _entity_to_lot.

    Также доказывает ScrapedLot-контракт, общий с `save_listings`
    (используется и `/scrape`, и `scripts/ingest_domclick_jsonl.py`).
    """
    assert_parity(
        legacy_fn=legacy_entity_to_lot,
        kit_fn=kit_entity_to_lot,
        fixtures=[(_YANDEX_ENTITY,)],
    )


def test_parity_harness_catches_yandex_entity_divergence() -> None:
    """Harness реально ЛОВИТ расхождение — мутируем price_rub в kit-результате."""
    import pytest

    from tests.support.parity import ParityMismatchError

    legacy_result = legacy_entity_to_lot(_YANDEX_ENTITY)
    kit_result = kit_entity_to_lot(_YANDEX_ENTITY)
    assert legacy_result is not None
    assert kit_result is not None

    # ScrapedLot — pydantic BaseModel (НЕ dataclass) → model_copy, не dataclasses.replace.
    mutated_kit_result = kit_result.model_copy(update={"price_rub": kit_result.price_rub + 1})

    with pytest.raises(ParityMismatchError) as exc_info:
        assert_parity(
            legacy_fn=lambda: legacy_result,
            kit_fn=lambda: mutated_kit_result,
            fixtures=[()],
        )
    assert "price_rub" in str(exc_info.value)
