"""Geocode endpoints — debug + frontend Suggest proxy."""

from __future__ import annotations

import logging
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session

from app.core.db import get_db
from app.services.geocoder import GeocodeResult, geocode, reverse_geocode, suggest

logger = logging.getLogger(__name__)

router = APIRouter()


@router.get("/lookup", response_model=GeocodeResult)
async def lookup(
    address: Annotated[str, Query(min_length=3, max_length=500)],
    db: Annotated[Session, Depends(get_db)],
) -> GeocodeResult:
    """Геокодинг адреса → lat/lon.

    Примеры:
      /api/v1/geocode/lookup?address=ул.+Малышева+30+Екатеринбург
      /api/v1/geocode/lookup?address=Куйбышева+50+Екатеринбург
    """
    result = await geocode(address, db)
    if result is None:
        raise HTTPException(status_code=404, detail=f"Address not found: {address}")
    return result


class SuggestItem(BaseModel):
    label: str
    full_address: str
    lat: float
    lon: float
    kind: str
    # ГАР OBJECTGUID (ФИАС) дома — присутствует ТОЛЬКО у house-level кандидатов
    # DaData-тира. У прочих тиров / street/locality-кандидатов null. Фронт
    # прокидывает его в POST /estimate как target_fias_id (Tier 0.5 fias_exact).
    fias_id: str | None = None


class SuggestResponse(BaseModel):
    items: list[SuggestItem]


@router.get("/suggest", response_model=SuggestResponse)
async def suggest_addresses(
    q: Annotated[str, Query(min_length=2, max_length=200, description="Запрос для автокомплита")],
    limit: Annotated[int, Query(ge=1, le=15)] = 8,
    db: Annotated[Session, Depends(get_db)] = None,  # type: ignore[assignment]
) -> SuggestResponse:
    """Автокомплит адресов в Свердловской области (region 66; ЕКБ — основной трафик,
    остаётся быстрым fast-path).

    Используется в EstimateForm для подсказок пока пользователь печатает.
    Bounded viewbox — генеральный по всей области (см. geocoder.OBLAST66_VIEWBOX),
    ЕКБ (lon 60.40-60.85, lat 56.65-56.95) внутри него остаётся быстрым fast-path.

    Пример:
      /api/v1/geocode/suggest?q=Малышева
      /api/v1/geocode/suggest?q=Цвиллинга  # → пусто, такой улицы в ЕКБ нет
    """
    items = await suggest(q, db=db, limit=limit)
    return SuggestResponse(
        items=[
            SuggestItem(
                label=s.label,
                full_address=s.full_address,
                lat=s.lat,
                lon=s.lon,
                kind=s.kind,
                fias_id=s.fias_id,
            )
            for s in items
        ]
    )


class ReverseResponse(BaseModel):
    """Reverse-geocode response для MapPicker'а.

    `lat`/`lon` — echo входной точки клика (для логики «отодвинули ли далеко»).
    `snapped_lat`/`snapped_lon` — центр matched здания от provider'а. Если
    `precision in ("exact","number")` фронт двигает marker на snapped point —
    пользователь видит «магнит к дому». Для остальных precision snapped == input.
    """

    address: str
    lat: float = Field(..., description="Исходная latitude клика")
    lon: float = Field(..., description="Исходная longitude клика")
    snapped_lat: float = Field(..., description="Latitude центра matched здания")
    snapped_lon: float = Field(..., description="Longitude центра matched здания")
    precision: str = Field(
        ...,
        description=(
            "Yandex-style: exact/number/street/range/near/locality/other/cadastral. "
            "Фронт двигает marker только если exact/number/cadastral."
        ),
    )
    provider: str = Field(..., description="cadastral | yandex | nominatim")


@router.get("/reverse", response_model=ReverseResponse)
async def reverse(
    lat: Annotated[float, Query(ge=-90, le=90)],
    lon: Annotated[float, Query(ge=-180, le=180)],
    db: Annotated[Session, Depends(get_db)] = None,  # type: ignore[assignment]
) -> ReverseResponse:
    """Обратный геокодинг — координаты с карты → адрес + snapped точка здания.

    Пример: /api/v1/geocode/reverse?lat=56.8389&lon=60.6057
    """
    result = await reverse_geocode(lat, lon, db=db)
    if result is None:
        raise HTTPException(status_code=404, detail="address not found for coordinates")
    return ReverseResponse(
        address=result.address,
        lat=lat,
        lon=lon,
        snapped_lat=result.snapped_lat,
        snapped_lon=result.snapped_lon,
        precision=result.precision,
        provider=result.provider,
    )
