"""Unit-тесты для DomClick scraper (#796).

Тестируем:
- Парсинг title-паттерна (rooms/area/floor) из текста карточки
- Парсинг цены из отдельного DOM-элемента
- Извлечение source_id из href
- Полный цикл _parse_html -> list[ScrapedLot] на mocked HTML
"""

from __future__ import annotations

import asyncio

import pytest

from app.services.scrapers.domclick import (
    DomClickScraper,
    _extract_area,
    _extract_floor,
    _extract_price_from_element,
    _extract_rooms,
    _extract_source_id,
)
from app.services.scrapers.domclick_exceptions import DomClickBlockedError

# ── Rooms / studio ───────────────────────────────────────────────────────────


@pytest.mark.parametrize(
    "text, expected",
    [
        ("2-комн. квартира 53,5 м² 5/15 эт.", 2),
        ("1-к. квартира 32,1 м² 3/9 эт.", 1),
        ("3-комн. кв. 78 м² 10/12 эт.", 3),
        ("4-комн. квартира 95 м² 2/5 эт.", 4),
        ("студия 24 м² 1/10 эт.", 0),
        ("Студия 30,5 м² 8/22 эт.", 0),
        ("Нет комнат — просто текст", None),
    ],
)
def test_extract_rooms(text: str, expected: int | None) -> None:
    assert _extract_rooms(text) == expected


# ── Area ─────────────────────────────────────────────────────────────────────


@pytest.mark.parametrize(
    "text, expected",
    [
        ("2-комн. квартира 53,5 м² 5/15 эт.", 53.5),
        ("студия 24 м² 1/10 эт.", 24.0),
        ("78м2 на этаже", 78.0),
        ("площадь 101.7 м²", 101.7),
        ("Нет площади", None),
    ],
)
def test_extract_area(text: str, expected: float | None) -> None:
    assert _extract_area(text) == expected


# ── Floor ─────────────────────────────────────────────────────────────────────


@pytest.mark.parametrize(
    "text, floor, total",
    [
        ("2-комн. квартира 53,5 м² 5/15 эт.", 5, 15),
        ("студия 24 м² 1/10 эт.", 1, 10),
        ("12/24 эт.", 12, 24),
        ("нет этажа", None, None),
    ],
)
def test_extract_floor(text: str, floor: int | None, total: int | None) -> None:
    assert _extract_floor(text) == (floor, total)


# ── Price (per-element) ───────────────────────────────────────────────────────
# Цена парсится из отдельного DOM-элемента (не из суммарного card_text),
# поэтому числа адреса не загрязняют парсинг цены.


@pytest.mark.parametrize(
    "text, expected",
    [
        ("5 200 000 ₽", 5_200_000),
        ("12 345 678 ₽", 12_345_678),
        ("3 990 000 руб.", 3_990_000),
        ("от 4 500 000 ₽", 4_500_000),
        ("без цены", None),
        ("0 ₽", None),
    ],
)
def test_extract_price_from_element(text: str, expected: int | None) -> None:
    assert _extract_price_from_element(text) == expected


# ── source_id from href ───────────────────────────────────────────────────────


@pytest.mark.parametrize(
    "href, expected",
    [
        ("/card/sale__flat__2075671636", "2075671636"),
        ("https://domclick.ru/card/sale__flat__123456789", "123456789"),
        ("/card/sale__flat__1", "1"),
        ("/card/something_else", None),
        ("", None),
    ],
)
def test_extract_source_id(href: str, expected: str | None) -> None:
    assert _extract_source_id(href) == expected


# ── _parse_html -> list[ScrapedLot] ──────────────────────────────────────────
# Цена в каждой карточке вынесена в отдельный <span> с символом рубля,
# чтобы не сливаться с адресным номером дома при конкатенации card_text.

_MOCK_HTML = """\
<!DOCTYPE html>
<html>
<body>
  <!-- Карточка 1: комнатность, площадь, этаж, адрес, цена в отдельных span -->
  <a href="/card/sale__flat__2075671636">
    <span>2-комн. квартира 53,5 м² 5/15 эт.</span>
    <span>ул. Малышева, 1</span>
    <span>5 200 000 ₽</span>
  </a>

  <!-- Карточка 2: студия -->
  <a href="/card/sale__flat__1111111111">
    <span>Студия 28 м² 3/10 эт.</span>
    <span>пр-т Ленина, 50</span>
    <span>3 100 000 ₽</span>
  </a>

  <!-- Карточка 3: без цены -> должна быть отфильтрована -->
  <a href="/card/sale__flat__9999999999">
    <span>1-комн. квартира 32 м² 2/9 эт.</span>
  </a>

  <!-- Не карточка (без sale__flat__) -> должна быть отфильтрована -->
  <a href="/card/sale__apartment__123">
    <span>что-то другое</span>
  </a>

  <!-- Дубликат первой карточки -> один раз на страницу -->
  <a href="/card/sale__flat__2075671636">
    <span>2-комн. квартира 53,5 м² 5/15 эт.</span>
    <span>5 200 000 ₽</span>
  </a>
</body>
</html>
"""


def test_parse_html_returns_lots() -> None:
    scraper = DomClickScraper()
    lots = scraper._parse_html(_MOCK_HTML)

    # Карточки 1 и 2 парсятся; карточка 3 (без цены) отфильтрована; дубль — дедуплицирован
    assert len(lots) == 2


def test_parse_html_card1_fields() -> None:
    scraper = DomClickScraper()
    lots = scraper._parse_html(_MOCK_HTML)

    card1 = next(lot for lot in lots if lot.source_id == "2075671636")
    assert card1.source == "domklik"
    assert card1.source_url == "https://domclick.ru/card/sale__flat__2075671636"
    assert card1.rooms == 2
    assert card1.area_m2 == 53.5
    assert card1.floor == 5
    assert card1.total_floors == 15
    assert card1.price_rub == 5_200_000
    assert card1.lat is None
    assert card1.lon is None
    assert card1.listing_segment == "vtorichka"


def test_parse_html_studio() -> None:
    scraper = DomClickScraper()
    lots = scraper._parse_html(_MOCK_HTML)

    studio = next(lot for lot in lots if lot.source_id == "1111111111")
    assert studio.rooms == 0  # студия
    assert studio.area_m2 == 28.0
    assert studio.price_rub == 3_100_000


def test_parse_html_no_price_filtered() -> None:
    scraper = DomClickScraper()
    lots = scraper._parse_html(_MOCK_HTML)

    source_ids = {lot.source_id for lot in lots}
    assert "9999999999" not in source_ids


def test_parse_html_dedup_same_href() -> None:
    """Дубликат href на одной странице должен давать одну карточку."""
    scraper = DomClickScraper()
    lots = scraper._parse_html(_MOCK_HTML)

    ids_2075 = [lot for lot in lots if lot.source_id == "2075671636"]
    assert len(ids_2075) == 1


def test_scraper_source_name() -> None:
    scraper = DomClickScraper()
    assert scraper.name == "domklik"
    assert scraper.source == "domklik"
    assert scraper.base_url == "https://domclick.ru"
    # request_delay_sec установлен из get_scraper_delay (DB или fallback 8.0)
    assert scraper.request_delay_sec >= 0.0


def test_fetch_around_raises() -> None:
    scraper = DomClickScraper()
    with pytest.raises(NotImplementedError, match="geo-radius"):
        asyncio.run(scraper.fetch_around(56.8, 60.6))


def test_build_url_no_rooms() -> None:
    scraper = DomClickScraper()
    url = scraper._build_url(city_id=4, rooms=None, page=1)
    assert "city_id=4" in url
    assert "rooms" not in url
    assert "p=1" in url


def test_build_url_with_rooms() -> None:
    scraper = DomClickScraper()
    url = scraper._build_url(city_id=4, rooms=2, page=3)
    assert "rooms=2" in url
    assert "p=3" in url


def test_parse_failures_counter() -> None:
    """parse_failures счётчик начинается с 0."""
    scraper = DomClickScraper()
    assert scraper.parse_failures == 0


def test_blocked_page_raises_domclick_blocked_error() -> None:
    """HTML с datadome в начале вызывает DomClickBlockedError."""
    scraper = DomClickScraper()
    blocked_html = (
        "<html><head><title>datadome protection</title></head><body>blocked</body></html>"
    )
    with pytest.raises(DomClickBlockedError):
        scraper._parse_html(blocked_html)
