"""Cian valuation time-budget — slow Cian must degrade gracefully, not block /estimate.

Prod latency incident: the Cian Valuation call was the ONLY ungated enrichment in
the estimate pipeline — it times out at ~25s on every cache-miss, blocking the whole
request past the gateway/patience window. The Yandex valuation right above it was
already wrapped in `_with_budget`; this mirrors that guard for Cian.

Contracts locked here:
  1. `estimate_cian_valuation_timeout_s` exists and defaults to 8.0s.
  2. A Cian-valuation timeout (TimeoutError surfaced by the _with_budget wrapper)
     degrades to an AggregatedEstimate WITHOUT 'cian_valuation' in sources_used and
     no 5xx — the same graceful None path as a network error.

Style mirrors test_estimator_null_floor_timeout.py.
"""

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 unittest.mock import AsyncMock, MagicMock, patch

import anyio


def _make_fake_geo():
    from app.services.geocoder import GeocodeResult

    return GeocodeResult(
        lat=56.838,
        lon=60.595,
        full_address="Свердловская обл., Екатеринбург, ул. Учителей, 18",
        provider="nominatim",
    )


def _make_payload_full():
    """Floor present so the floor-gated Cian source is actually awaited."""
    from app.schemas.trade_in import TradeInEstimateInput

    return TradeInEstimateInput(
        address="ЕКБ, ул. Учителей, 18",
        area_m2=38.8,
        rooms=1,
        floor=4,
        total_floors=16,
    )


def test_cian_valuation_timeout_setting_default() -> None:
    """The new budget setting exists and defaults to 8.0s."""
    from app.core.config import settings

    assert hasattr(settings, "estimate_cian_valuation_timeout_s")
    assert settings.estimate_cian_valuation_timeout_s == 8.0


def test_estimate_cian_timeout_degrades_no_5xx() -> None:
    """Cian valuation raising TimeoutError → estimate without it, no exception.

    The _with_budget() guard wraps the Cian call; a TimeoutError must map to the
    same graceful None path as a network error. Estimator returns an
    AggregatedEstimate; 'cian_valuation' absent from sources_used.
    """
    from app.services.estimator import estimate_quality

    db = MagicMock()
    payload = _make_payload_full()

    # Cian call hangs/raises a timeout — simulate the slow-upstream case.
    cian_mock = AsyncMock(side_effect=TimeoutError("cian slow"))

    async def _run() -> None:
        with (
            patch("app.services.estimator.geocode", new=AsyncMock(return_value=_make_fake_geo())),
            patch("app.services.estimator.get_house_metadata", new=AsyncMock(return_value=None)),
            patch("app.services.estimator._fetch_analogs", return_value=([], False, "W")),
            patch("app.services.estimator._fetch_deals", return_value=[]),
            patch(
                "app.services.estimator._get_or_fetch_imv_cached", new=AsyncMock(return_value=None)
            ),
            patch(
                "app.services.estimator._get_or_fetch_yandex_valuation_cached",
                new=AsyncMock(return_value=None),
            ),
            patch("app.services.estimator.estimate_via_cian_valuation", new=cian_mock),
            patch("app.services.estimator._get_asking_sold_ratio", return_value=(None, None)),
        ):
            result = await estimate_quality(payload, db)

        assert result.estimate_id is not None  # no 5xx — degraded gracefully
        assert "cian_valuation" not in result.sources_used

    anyio.run(_run)
