"""Lightweight reverse geocoding helpers (Nominatim)."""

from __future__ import annotations

import json
import logging
from urllib.parse import urlencode
from urllib.request import Request, urlopen

from core.countries import normalize_country_code

logger = logging.getLogger(__name__)

NOMINATIM_REVERSE_URL = 'https://nominatim.openstreetmap.org/reverse'
DEFAULT_USER_AGENT = 'TruckMe/1.0 (logistics platform)'


def _nominatim_reverse(lat, lon, *, zoom: int = 18, timeout: int = 5) -> dict | None:
    """Call Nominatim reverse API. Returns parsed JSON or None."""
    if lat is None or lon is None:
        return None
    try:
        lat_f = float(lat)
        lon_f = float(lon)
    except (TypeError, ValueError):
        return None

    params = urlencode(
        {
            'lat': f'{lat_f:.7f}',
            'lon': f'{lon_f:.7f}',
            'format': 'json',
            'zoom': zoom,
            'addressdetails': 1,
        }
    )
    request = Request(
        f'{NOMINATIM_REVERSE_URL}?{params}',
        headers={'User-Agent': DEFAULT_USER_AGENT, 'Accept': 'application/json'},
    )
    try:
        with urlopen(request, timeout=timeout) as response:
            return json.loads(response.read().decode('utf-8'))
    except Exception:
        logger.exception('Reverse geocode failed for lat=%s lon=%s', lat, lon)
        return None


def reverse_geocode_country_code(lat, lon, *, timeout: int = 5) -> str | None:
    """Resolve ISO alpha-2 country code from coordinates via OpenStreetMap Nominatim."""
    payload = _nominatim_reverse(lat, lon, zoom=3, timeout=timeout)
    if not payload:
        return None
    address = payload.get('address') or {}
    code = address.get('country_code')
    return normalize_country_code(code)


def reverse_geocode_address(lat, lon, *, timeout: int = 5) -> str | None:
    """Resolve a human-readable address from coordinates via Nominatim."""
    payload = _nominatim_reverse(lat, lon, zoom=18, timeout=timeout)
    if not payload:
        return None
    display = (payload.get('display_name') or '').strip()
    return display or None


def maybe_fill_country_code_from_coords(shipment, *, field: str, lat_field: str, lon_field: str) -> bool:
    """Fill a shipment country code from coordinates when missing. Returns True if updated."""
    if normalize_country_code(getattr(shipment, field)):
        return False
    lat = getattr(shipment, lat_field)
    lon = getattr(shipment, lon_field)
    code = reverse_geocode_country_code(lat, lon)
    if not code:
        return False
    setattr(shipment, field, code)
    return True
