"""#1871 P1 (ghost-anchor) — same-building anchor с пустым радиусом → confidence='low'.

Прод-дефект «Ghost-anchor»: same-building anchor подменяет headline, но радиусных
аналогов ноль (listings_clean пуст, напр. ST_DWithin не нашёл соседей, anchor
держится на одном yandex_valuation). Раньше confidence наследовался от anchor
('high'/'medium'), а n_analogs перезаписывался на anchor["n"] (>0) — поэтому
defensive-гард `_enforce_zero_analog_low` (срабатывает только на n_analogs==0) его
НЕ ловил. UI рисовал «Высокая · 0 объектов» = сфабрикованная уверенность.

Фикс: inline-гард в `if anchor is not None:` блоке понижает confidence до 'low' и
дописывает caveat, когда listings_clean пуст. Полный estimate-путь со всеми I/O
застаблен через harness из test_same_building_anchor.py (_run_estimate / _make_payload).
"""

from __future__ import annotations

import importlib.util
import os
from pathlib import Path
from typing import Any

# Settings требует DATABASE_URL при инициализации (fail-fast, C-3).
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost/test_db")

import pytest

# Переиспользуем harness _run_estimate / _make_payload из anchor-теста (полный
# estimate путь со всеми I/O застабленными).
_ANCHOR_TEST = Path(__file__).parent / "test_same_building_anchor.py"
_spec = importlib.util.spec_from_file_location("_anchor_harness_ghost", _ANCHOR_TEST)
assert _spec is not None and _spec.loader is not None
_h = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_h)

# Caveat-маркер из inline-гарда estimator.py (#1871 P1).
_CAVEAT_MARKER = "сопоставимых предложений поблизости не найдено"

# 5 same-building комплов с очень tight spread → anchor сам по себе дал бы
# confidence='high' (n>=5, CV≈0.005). Это и есть ghost-сценарий: высокая
# уверенность на якоре дома при НУЛЕВОМ радиусе.
_SB_COMPS_TIGHT: list[dict[str, Any]] = [
    {"price_per_m2": 300_000, "area_m2": 60.0, "rooms": 2},
    {"price_per_m2": 302_000, "area_m2": 61.0, "rooms": 2},
    {"price_per_m2": 298_000, "area_m2": 59.0, "rooms": 2},
    {"price_per_m2": 301_000, "area_m2": 60.5, "rooms": 2},
    {"price_per_m2": 299_000, "area_m2": 59.5, "rooms": 2},
]


def _payload():  # type: ignore[no-untyped-def]
    return _h._make_payload(area=60.0, rooms=2)


def test_ghost_anchor_empty_radius_forces_low_confidence() -> None:
    """Anchor (tight spread, n=5) + радиус пуст → confidence='low', caveat дописан.

    Без гарда anchor отдал бы 'high' (UI: «Высокая · 0 объектов» = ghost).
    """
    est = _h._run_estimate(
        anchor_comps=_SB_COMPS_TIGHT,
        anchor_tier="A",
        radius_analogs=[],
        payload=_payload(),
    )
    # Anchor построил headline (фикс #691 не фабрикует число из воздуха — anchor реален).
    assert est.median_price_rub > 0
    # Ghost-гард понизил confidence до 'low' несмотря на tight-spread anchor.
    assert est.confidence == "low"
    # Caveat про «только аналоги из того же дома» дописан.
    assert _CAVEAT_MARKER in (est.confidence_explanation or "")


def test_ghost_anchor_caveat_appended_not_replacing_anchor_explanation() -> None:
    """Caveat дописывается К якорному explanation, не затирая «по N аналогам из дома»."""
    est = _h._run_estimate(
        anchor_comps=_SB_COMPS_TIGHT,
        anchor_tier="A",
        radius_analogs=[],
        payload=_payload(),
    )
    exp = est.confidence_explanation or ""
    # Якорный base-текст сохранён (#695) + caveat дописан (#1871 P1).
    assert "Оценка построена по" in exp
    assert _CAVEAT_MARKER in exp


def test_anchor_with_radius_analogs_not_downgraded_by_ghost_guard() -> None:
    """Контроль: тот же anchor + НЕПУСТОЙ радиус → ghost-гард НЕ срабатывает,
    caveat отсутствует (честный путь, есть сопоставимые предложения поблизости)."""
    est = _h._run_estimate(
        anchor_comps=_SB_COMPS_TIGHT,
        anchor_tier="A",
        # radius_analogs=None → harness подставляет _RADIUS_ANALOGS (3 лота).
        payload=_payload(),
    )
    assert _CAVEAT_MARKER not in (est.confidence_explanation or "")


if __name__ == "__main__":  # pragma: no cover
    raise SystemExit(pytest.main([__file__, "-q"]))
