"""Tests for _save_yandex_history_items — house_id linking + new columns.

Легаси `app.services.scrapers.yandex_valuation` удалён (#2277 финальный шаг
scraper_kit-миграции) — `result` типы переведены на kit-эквивалент
`scraper_kit.providers.yandex.valuation`, тот же тип, что `estimator.py` реально
принимает в `_save_yandex_history_items`.
"""

from __future__ import annotations

import os

# Settings requires DATABASE_URL at init time. Set dummy DSN before any app import.
os.environ.setdefault("DATABASE_URL", "postgresql://test:test@localhost/test_db")

from datetime import date
from unittest.mock import MagicMock, patch

import pytest
from scraper_kit.providers.yandex.valuation import (
    ValuationHistoryItem,
    ValuationHouseMeta,
    YandexValuationResult,
)

from app.services.estimator import _save_yandex_history_items


def _make_result(items: list[ValuationHistoryItem]) -> YandexValuationResult:
    return YandexValuationResult(
        address="Россия, Свердловская область, Екатеринбург, улица Куйбышева, 106",
        offer_category="APARTMENT",
        offer_type="SELL",
        page=1,
        source_url=(
            "https://realty.yandex.ru/otsenka-kvartiry-po-adresu-onlayn/?address=test&page=1"
        ),
        house=ValuationHouseMeta(year_built=2005, total_floors=25, has_lift=True),
        history_items=items,
    )


def _two_items() -> list[ValuationHistoryItem]:
    return [
        ValuationHistoryItem(
            area_m2=50.0,
            rooms=2,
            floor=3,
            start_price=9_000_000,
            last_price=9_000_000,
            publish_date=date(2024, 5, 10),
            removed_date=None,
            exposure_days=30,
            status="В продаже",
        ),
        ValuationHistoryItem(
            area_m2=55.0,
            rooms=2,
            floor=5,
            start_price=10_000_000,
            last_price=9_500_000,
            publish_date=date(2024, 3, 1),
            removed_date=date(2024, 6, 1),
            exposure_days=92,
            status="Снято",
        ),
    ]


def test_save_resolves_house_once_per_page():
    """match_or_create_house must be called exactly once per page (not per item)."""
    db = MagicMock()
    result = _make_result(_two_items())
    with patch(
        "app.services.estimator.match_or_create_house",
        return_value=(12345, 0.9, "fingerprint"),
    ) as m:
        saved = _save_yandex_history_items(db, result)
    assert m.call_count == 1, "match_or_create_house must be called once per page"
    assert saved == 2


def test_save_row_contains_house_id_and_confidence():
    """Each INSERT row dict carries house_id and source_confidence."""
    db = MagicMock()
    result = _make_result(_two_items())
    with patch(
        "app.services.estimator.match_or_create_house",
        return_value=(54321, 0.9, "fingerprint"),
    ):
        _save_yandex_history_items(db, result)

    # db.execute called once with list-of-dicts (executemany, one round-trip)
    assert db.execute.call_count == 1
    rows = db.execute.call_args_list[0].args[1]
    assert isinstance(rows, list) and len(rows) == 2
    for row in rows:
        assert row["house_id"] == 54321
        assert row["confidence"] == pytest.approx(0.9)
        assert row["notes"] == "match_method=fingerprint"
        assert row["total_floors"] == 25


def test_save_row_contains_removed_date():
    """Item with removed_date set → row dict has removed_date populated."""
    db = MagicMock()
    items = [
        ValuationHistoryItem(
            area_m2=60.0,
            rooms=2,
            floor=4,
            start_price=11_000_000,
            last_price=10_500_000,
            publish_date=date(2024, 1, 15),
            removed_date=date(2024, 5, 20),
            exposure_days=125,
            status="Снято",
        ),
    ]
    result = _make_result(items)
    with patch(
        "app.services.estimator.match_or_create_house",
        return_value=(99, 1.0, "new"),
    ):
        _save_yandex_history_items(db, result)

    # args[1] is now the list-of-dicts passed to executemany
    rows = db.execute.call_args_list[0].args[1]
    assert rows[0]["removed_date"] == date(2024, 5, 20)


def test_save_row_removed_date_none_when_active():
    """Active listing (status В продаже) → removed_date=None in row."""
    db = MagicMock()
    items = [
        ValuationHistoryItem(
            area_m2=42.0,
            rooms=1,
            floor=2,
            start_price=7_500_000,
            last_price=7_500_000,
            publish_date=date(2024, 6, 1),
            removed_date=None,
            exposure_days=10,
            status="В продаже",
        ),
    ]
    result = _make_result(items)
    with patch(
        "app.services.estimator.match_or_create_house",
        return_value=(7, 1.0, "new"),
    ):
        _save_yandex_history_items(db, result)

    rows = db.execute.call_args_list[0].args[1]
    assert rows[0]["removed_date"] is None


def test_save_handles_match_failure_gracefully():
    """If match_or_create_house raises, save continues with house_id=None, confidence=0."""
    db = MagicMock()
    items = [
        ValuationHistoryItem(
            area_m2=42.0,
            rooms=1,
            floor=2,
            start_price=7_500_000,
            last_price=7_500_000,
            publish_date=date(2024, 6, 1),
            removed_date=None,
            exposure_days=10,
            status="В продаже",
        ),
    ]
    result = _make_result(items)
    with patch(
        "app.services.estimator.match_or_create_house",
        side_effect=RuntimeError("simulated lookup failure"),
    ):
        saved = _save_yandex_history_items(db, result)

    assert saved == 1
    rows = db.execute.call_args_list[0].args[1]
    assert rows[0]["house_id"] is None
    assert rows[0]["confidence"] == pytest.approx(0.0)
    assert rows[0]["notes"] is None


def test_save_match_called_with_address_and_year():
    """match_or_create_house receives correct address and year_built from result.house."""
    db = MagicMock()
    result = _make_result(_two_items())
    with patch(
        "app.services.estimator.match_or_create_house",
        return_value=(1, 1.0, "new"),
    ) as m:
        _save_yandex_history_items(db, result)

    kwargs = m.call_args.kwargs
    assert kwargs["address"] == result.address
    assert kwargs["year_built"] == result.house.year_built
    assert kwargs["ext_source"] == "yandex_valuation"
