"""Write-path tests: external_valuations.house_id резолвится при сохранении (#2236).

Cian и Yandex valuation-кэши теперь принимают house_id (уже разрезолвленный
estimate-путём через match_house_readonly) и пишут его в external_valuations.
Best-effort: house_id=None → колонка NULL, сохранение НЕ падает.

DB — MagicMock, записываем параметры db.execute (никакого реального Postgres,
как в test_yandex_valuation_save / test_backfill_house_coords).

Легаси `app.services.scrapers.yandex_valuation` / `cian_valuation` удалены (#2277 /
#2397 финальный шаг E, 0 runtime-импортёров) — `_yandex_result()` и cian-импорты
переведены на kit-эквиваленты `scraper_kit.providers.yandex.valuation` /
`scraper_kit.providers.cian.valuation` (те же типы, что `estimator.py` реально
использует).
"""

from __future__ import annotations

import os

# Settings требует DATABASE_URL на import. Ставим фиктивный DSN заранее.
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost/test_db")

from unittest.mock import AsyncMock, MagicMock, patch

from scraper_kit.providers.cian.valuation import CianValuationResult, _save_to_cache

# ---------------------------------------------------------------------------
# Cian valuation — _save_to_cache прокидывает house_id в INSERT params
# ---------------------------------------------------------------------------


def _cian_result() -> CianValuationResult:
    return CianValuationResult(
        sale_price_rub=10_000_000,
        sale_accuracy=85.0,
        sale_price_from=9_000_000,
        sale_price_to=11_000_000,
        rent_price_rub=40_000,
        rent_accuracy=70.0,
        chart=[],
        chart_change_pct=None,
        chart_change_direction=None,
        external_house_id=54016,
        filters_hash="abc",
        raw_state=None,
    )


def _cian_save(db: MagicMock, house_id: int | None) -> dict:
    _save_to_cache(
        db,
        cache_key="ck",
        address="Екатеринбург, улица Малышева, 51",
        total_area=50.0,
        rooms_count=2,
        floor=3,
        total_floors=9,
        repair_type="cosmetic",
        deal_type="sale",
        result=_cian_result(),
        house_id=house_id,
    )
    # _save_to_cache делает один db.execute(INSERT ...) + commit
    return db.execute.call_args_list[0].args[1]


def test_cian_save_persists_house_id():
    """Резолвленный house_id попадает в INSERT-параметры."""
    db = MagicMock()
    params = _cian_save(db, house_id=777)
    assert params["hid"] == 777
    db.commit.assert_called_once()


def test_cian_save_null_house_id_no_exception():
    """house_id=None → hid=None в params, сохранение не падает."""
    db = MagicMock()
    params = _cian_save(db, house_id=None)
    assert params["hid"] is None
    db.commit.assert_called_once()


# ---------------------------------------------------------------------------
# Yandex valuation — _get_or_fetch_yandex_valuation_cached пишет house_id
# ---------------------------------------------------------------------------


def _make_db_cache_miss() -> MagicMock:
    """MagicMock db, у которого cache-lookup даёт miss (first() → None)."""
    db = MagicMock()
    db.execute.return_value.mappings.return_value.first.return_value = None
    return db


def _yandex_result():
    from scraper_kit.providers.yandex.valuation import (
        ValuationHouseMeta,
        YandexValuationResult,
    )

    return YandexValuationResult(
        address="Екатеринбург, улица Куйбышева, 106",
        offer_category="APARTMENT",
        offer_type="SELL",
        page=1,
        source_url="https://realty.yandex.ru/otsenka/?address=test&page=1",
        house=ValuationHouseMeta(year_built=2005, total_floors=25),
        history_items=[],
    )


def _insert_params(db: MagicMock) -> dict:
    """Найти params INSERT INTO external_valuations среди вызовов db.execute."""
    for call in db.execute.call_args_list:
        args = call.args
        if len(args) >= 2 and "INSERT INTO external_valuations" in str(args[0]):
            return args[1]
    raise AssertionError("INSERT INTO external_valuations not found in db.execute calls")


async def test_yandex_save_persists_house_id():
    """Fresh fetch пишет house_id в INSERT external_valuations."""
    from app.services import estimator

    db = _make_db_cache_miss()
    scraper = AsyncMock()
    scraper.fetch_house_history = AsyncMock(return_value=_yandex_result())
    cm = MagicMock()
    cm.__aenter__ = AsyncMock(return_value=scraper)
    cm.__aexit__ = AsyncMock(return_value=False)

    with patch.object(estimator, "YandexValuationScraper", return_value=cm):
        res = await estimator._get_or_fetch_yandex_valuation_cached(
            db, address="Екатеринбург, улица Куйбышева, 106", house_id=4242
        )

    assert res is not None
    params = _insert_params(db)
    assert params["hid"] == 4242


async def test_yandex_save_null_house_id_no_exception():
    """house_id=None → hid=None, INSERT проходит без исключения."""
    from app.services import estimator

    db = _make_db_cache_miss()
    scraper = AsyncMock()
    scraper.fetch_house_history = AsyncMock(return_value=_yandex_result())
    cm = MagicMock()
    cm.__aenter__ = AsyncMock(return_value=scraper)
    cm.__aexit__ = AsyncMock(return_value=False)

    with patch.object(estimator, "YandexValuationScraper", return_value=cm):
        res = await estimator._get_or_fetch_yandex_valuation_cached(
            db, address="Екатеринбург, улица Куйбышева, 106"
        )

    assert res is not None
    params = _insert_params(db)
    assert params["hid"] is None
