"""Тесты для GET /{cad_num}/utility-infrastructure (#1746).

FastAPI TestClient + mock сервиса get_nearby_utility_infrastructure — без Postgres.
"""

from __future__ import annotations

from typing import Any
from unittest.mock import patch

import pytest
from fastapi.testclient import TestClient

from app.main import app

_VALID_CAD = "66:41:0204016:10"


def _make_feature(
    osm_id: int = 1,
    kind: str = "power",
    distance_m: float = 120.5,
    osm_type: str = "way",
) -> dict[str, Any]:
    return {
        "osm_id": osm_id,
        "osm_type": osm_type,
        "infrastructure_kind": kind,
        "name": "ЛЭП-110",
        "source_tag": "power=line",
        "distance_m": distance_m,
        "geometry_geojson": {"type": "LineString", "coordinates": [[60.6, 56.8], [60.61, 56.81]]},
    }


def _make_response(
    features: list[dict[str, Any]] | None = None,
    nearest: float | None = 120.5,
    nearest_by_kind: dict[str, float | None] | None = None,
) -> dict[str, Any]:
    if features is None:
        features = [_make_feature()]
    if nearest_by_kind is None:
        nearest_by_kind = {"power": 120.5}
    return {
        "features": features,
        "summary": {
            "total_features": len(features),
            "nearest_distance_m": nearest,
            "nearest_by_kind": nearest_by_kind,
        },
    }


def test_utility_infra_returns_features() -> None:
    """Нормальный ответ: фичи + summary с ближайшими по видам сети."""
    full = _make_response(
        features=[
            _make_feature(1, "power", 80.0, "way"),
            _make_feature(2, "water", 150.0, "node"),
        ],
        nearest=80.0,
        nearest_by_kind={"power": 80.0, "water": 150.0},
    )
    with patch(
        "app.api.v1.parcels.get_nearby_utility_infrastructure",
        return_value=full,
    ):
        client = TestClient(app)
        r = client.get(f"/api/v1/parcels/{_VALID_CAD}/utility-infrastructure")

    assert r.status_code == 200, r.text
    body = r.json()
    assert body["summary"]["total_features"] == 2
    assert body["summary"]["nearest_distance_m"] == pytest.approx(80.0)
    assert body["summary"]["nearest_by_kind"]["power"] == pytest.approx(80.0)
    assert body["summary"]["nearest_by_kind"]["water"] == pytest.approx(150.0)
    assert len(body["features"]) == 2
    assert body["features"][0]["infrastructure_kind"] == "power"
    assert body["features"][0]["geometry_geojson"]["type"] == "LineString"


def test_utility_infra_empty() -> None:
    """Нет сетей в радиусе → пустой список, total=0, nearest=null."""
    empty = _make_response(features=[], nearest=None, nearest_by_kind={})
    with patch(
        "app.api.v1.parcels.get_nearby_utility_infrastructure",
        return_value=empty,
    ):
        client = TestClient(app)
        r = client.get(f"/api/v1/parcels/{_VALID_CAD}/utility-infrastructure")

    assert r.status_code == 200, r.text
    body = r.json()
    assert body["features"] == []
    assert body["summary"]["total_features"] == 0
    assert body["summary"]["nearest_distance_m"] is None
    assert body["summary"]["nearest_by_kind"] == {}


def test_utility_infra_parcel_not_found_404() -> None:
    """Участок не найден (ValueError из сервиса) → 404."""
    with patch(
        "app.api.v1.parcels.get_nearby_utility_infrastructure",
        side_effect=ValueError("Участок '66:41:9999999:1' не найден в БД"),
    ):
        client = TestClient(app)
        r = client.get("/api/v1/parcels/66:41:9999999:1/utility-infrastructure")

    assert r.status_code == 404
    assert "не найден" in r.json()["detail"]


def test_utility_infra_passes_radius() -> None:
    """radius_m из query передаётся в сервис."""
    with patch("app.api.v1.parcels.get_nearby_utility_infrastructure") as mock_svc:
        mock_svc.return_value = _make_response()
        client = TestClient(app)
        r = client.get(f"/api/v1/parcels/{_VALID_CAD}/utility-infrastructure?radius_m=1000")
        assert r.status_code == 200
        call = mock_svc.call_args
        assert call[0][2] == 1000 or call[1].get("radius_m") == 1000


def test_utility_infra_radius_out_of_range_422() -> None:
    """radius_m=10 (< min 50) → 422."""
    client = TestClient(app)
    r = client.get(f"/api/v1/parcels/{_VALID_CAD}/utility-infrastructure?radius_m=10")
    assert r.status_code == 422


def test_utility_infra_radius_too_large_422() -> None:
    """radius_m=5000 (> max 2000) → 422."""
    client = TestClient(app)
    r = client.get(f"/api/v1/parcels/{_VALID_CAD}/utility-infrastructure?radius_m=5000")
    assert r.status_code == 422
