"""
services/isochrone_service.py
==============================
Calcul d'isochrones de temps de trajet :
  - Voiture  : OpenRouteService (ORS)
  - Transports en commun : OpenTripPlanner (OTP local)
"""

from __future__ import annotations

import logging
import os
from datetime import datetime

import httpx
from fastapi import HTTPException

logger = logging.getLogger(__name__)

_ORS_URL         = "https://api.openrouteservice.org/v2/isochrones/driving-car"
_ORS_API_KEY     = os.getenv("ORS_API_KEY", "")
_OTP_URL         = os.getenv("OTP_URL", "http://localhost:8080")
_CONTOUR_MINUTES = [5, 10, 15, 20, 25]


async def get_isochrone(lat: float, lng: float, mode: str = "car") -> dict:
    """
    Calcule des isochrones de temps de trajet pour un point.
    mode="transit" → OpenTripPlanner (OTP local)
    mode="car"     → OpenRouteService (ORS, clé API requise)

    Raises:
        HTTPException 502/503/504: échec de l'API externe.
    """
    logger.info("Isochrone demandée | lat=%s lng=%s mode=%s", lat, lng, mode)
    if mode == "transit":
        return await _isochrone_transit(lat, lng)
    return await _isochrone_voiture(lat, lng)


async def _isochrone_transit(lat: float, lng: float) -> dict:
    """Isochrones transports communs via OpenTripPlanner."""
    url = f"{_OTP_URL}/otp/traveltime/isochrone"
    params = {
        "location":      f"{lat},{lng}",
        "time":          datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),
        "cutoffMinutes": ",".join(str(m) for m in _CONTOUR_MINUTES),
    }
    logger.debug("OTP request | url=%s | params=%s", url, params)

    try:
        async with httpx.AsyncClient(timeout=60.0) as client:
            resp = await client.get(url, params=params)
        if not resp.is_success:
            body_preview = resp.text[:500]
            logger.error(
                "OTP erreur HTTP | status=%d | lat=%s lng=%s | body=%r",
                resp.status_code, lat, lng, body_preview,
            )
            raise HTTPException(
                status_code=502,
                detail=f"OTP HTTP {resp.status_code}: {body_preview}",
            )
        data = resp.json()
        for feature in data.get("features", []):
            feature["properties"]["contour"] = feature["properties"].pop("time", 0)

        nb_features = len(data.get("features", []))
        if not nb_features:
            logger.warning("OTP n'a retourné aucune isochrone | lat=%s lng=%s", lat, lng)
            raise HTTPException(status_code=502, detail="OTP n'a retourné aucune isochrone")

        logger.info("Isochrone transit OK | features=%d", nb_features)
        return data

    except HTTPException:
        raise
    except httpx.ConnectError as exc:
        logger.error("OTP inaccessible | url=%s | erreur=%s", url, exc)
        raise HTTPException(
            status_code=503,
            detail="Serveur OTP inaccessible — est-il démarré ?",
        )
    except Exception as exc:
        logger.error(
            "Erreur inattendue OTP | type=%s | lat=%s lng=%s | erreur=%s",
            type(exc).__name__, lat, lng, exc,
            exc_info=True,
        )
        raise HTTPException(status_code=502, detail=str(exc))


async def _isochrone_voiture(lat: float, lng: float) -> dict:
    """Isochrones voiture via OpenRouteService."""
    if not _ORS_API_KEY:
        logger.error("ORS_API_KEY non configurée — isochrone voiture impossible")
        raise HTTPException(status_code=503, detail="ORS_API_KEY non configurée")

    body = {
        "locations":  [[lng, lat]],
        "range":      [m * 60 for m in _CONTOUR_MINUTES],
        "range_type": "time",
        "smoothing":  0.5,
    }
    logger.debug("ORS request | lat=%s lng=%s | contours=%s min", lat, lng, _CONTOUR_MINUTES)

    try:
        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.post(
                _ORS_URL,
                json=body,
                headers={"Authorization": _ORS_API_KEY},
            )
        if not resp.is_success:
            body_preview = resp.text[:500]
            logger.error(
                "ORS erreur HTTP | status=%d | lat=%s lng=%s | body=%r",
                resp.status_code, lat, lng, body_preview,
            )
            raise HTTPException(
                status_code=502,
                detail=f"ORS HTTP {resp.status_code}: {body_preview}",
            )
        data = resp.json()
        for feature in data.get("features", []):
            feature["properties"]["contour"] = int(
                feature["properties"].get("value", 0) // 60
            )
        nb_features = len(data.get("features", []))
        logger.info("Isochrone voiture OK | features=%d", nb_features)
        return data

    except HTTPException:
        raise
    except httpx.TimeoutException:
        logger.error("Timeout ORS | lat=%s lng=%s (>30 s)", lat, lng)
        raise HTTPException(status_code=504, detail="ORS timeout (>30 s)")
    except httpx.ConnectError as exc:
        logger.error("ORS inaccessible | url=%s | erreur=%s", _ORS_URL, exc)
        raise HTTPException(status_code=502, detail=f"Impossible de joindre ORS : {exc}")
    except Exception as exc:
        logger.error(
            "Erreur inattendue ORS | type=%s | lat=%s lng=%s | erreur=%s",
            type(exc).__name__, lat, lng, exc,
            exc_info=True,
        )
        raise HTTPException(status_code=502, detail=str(exc))
