"""Unit tests for #2044 (BE-2) optional radius_m input.

The schema field + estimate_quality wiring (base_radius_m / fallback_radius_m)
landed in e23dabe4; these tests lock the contract: radius_m is an optional,
bounded field whose None value preserves the byte-identical two-tier default
radii, while an explicit value overrides both search radii.

NOTE: importing app.services.estimator pulls app.core.config.Settings which
requires DATABASE_URL. Set it BEFORE importing app modules.
"""

import os

os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")

import pytest
from pydantic import ValidationError

from app.schemas.trade_in import TradeInEstimateInput
from app.services import estimator


def test_radius_m_defaults_to_none() -> None:
    payload = TradeInEstimateInput(address="ул. Тестовая, 1", area_m2=50, rooms=2)
    assert payload.radius_m is None


def test_radius_m_accepts_valid_int() -> None:
    payload = TradeInEstimateInput(address="ул. Тестовая, 1", area_m2=50, rooms=2, radius_m=1500)
    assert payload.radius_m == 1500


def test_radius_m_out_of_range_rejected() -> None:
    with pytest.raises(ValidationError):
        TradeInEstimateInput(address="ул. Тестовая, 1", area_m2=50, rooms=2, radius_m=50)
    with pytest.raises(ValidationError):
        TradeInEstimateInput(address="ул. Тестовая, 1", area_m2=50, rooms=2, radius_m=9000)


def test_radius_none_preserves_default_radii() -> None:
    # estimate_quality resolves `payload.radius_m or DEFAULT_RADIUS_M`. None must
    # keep the byte-identical two-tier defaults; an explicit value overrides both.
    assert (None or estimator.DEFAULT_RADIUS_M) == estimator.DEFAULT_RADIUS_M
    assert (None or estimator.FALLBACK_RADIUS_M) == estimator.FALLBACK_RADIUS_M
    assert (500 or estimator.DEFAULT_RADIUS_M) == 500
    assert (500 or estimator.FALLBACK_RADIUS_M) == 500


def test_api_radius_expand_clamp_bounds() -> None:
    # The house-analytics / sell-time endpoints clamp an explicit radius into
    # [100, 5000] before ST_DWithin. Mirror that guard so the contract is locked.
    def _clamp(r: int) -> int:
        return max(100, min(r, 5000))

    assert _clamp(50) == 100
    assert _clamp(300) == 300
    assert _clamp(9000) == 5000


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