"""Utilitarios de geocodificacao e rotas usando o Mapbox API."""

from __future__ import annotations

from typing import Tuple
from urllib.parse import quote

import httpx
from fastapi import HTTPException, status


async def geocode_with_mapbox(address: str, access_token: str, *, country: str = "br") -> Tuple[float, float]:
    """Geocodifica um endereco e retorna latitude/longitude."""

    if not address or not address.strip():
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Endereco nao pode ser vazio para geocodificacao.",
        )

    if not access_token:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="MAPBOX_ACCESS_TOKEN nao configurado.",
        )

    encoded_address = quote(address.strip())
    url = f"https://api.mapbox.com/geocoding/v5/mapbox.places/{encoded_address}.json"
    params = {
        "access_token": access_token,
        "limit": 1,
        "language": "pt",
        "country": country,
    }

    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.get(url, params=params)
            response.raise_for_status()
    except httpx.HTTPStatusError as exc:  # pragma: no cover - integracao externa
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Erro ao consultar servico de geocodificacao.",
        ) from exc
    except httpx.RequestError as exc:  # pragma: no cover - integracao externa
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Falha de comunicacao com servico de geocodificacao.",
        ) from exc

    data = response.json()
    features = data.get("features") or []
    if not features:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Endereco nao encontrado para geocodificacao.",
        )

    center = features[0].get("center") or []
    if len(center) < 2:
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Resposta invalida do servico de geocodificacao.",
        )

    longitude, latitude = center[0], center[1]
    return float(latitude), float(longitude)


async def driving_distance_km_mapbox(
    origin_lat: float,
    origin_lng: float,
    dest_lat: float,
    dest_lng: float,
    access_token: str,
) -> float:
    """Calcula distancia (km) via Mapbox Directions Matrix."""

    if not access_token:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="MAPBOX_ACCESS_TOKEN nao configurado.",
        )

    url = (
        "https://api.mapbox.com/directions-matrix/v1/mapbox/driving/"
        f"{origin_lng},{origin_lat};{dest_lng},{dest_lat}"
    )
    params = {"access_token": access_token, "annotations": "distance"}

    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.get(url, params=params)
            response.raise_for_status()
    except httpx.HTTPStatusError as exc:  # pragma: no cover - integracao externa
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Erro ao consultar servico de rotas.",
        ) from exc
    except httpx.RequestError as exc:  # pragma: no cover - integracao externa
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Falha de comunicacao com servico de rotas.",
        ) from exc

    data = response.json()
    distances = data.get("distances")
    if not distances or not isinstance(distances, list) or not distances[0]:
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Resposta invalida do servico de rotas.",
        )

    distance_meters = distances[0][1] if len(distances[0]) > 1 else None
    if distance_meters is None:
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Distancia nao retornada pelo servico de rotas.",
        )

    return float(distance_meters) / 1000.0
