package infra

import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URL

data class GeocodedLocation(
    val displayName: String,
    val street: String,
    val houseNumber: String,
    val neighbourhood: String,
    val city: String,
    val state: String,
    val country: String,
    val postcode: String,
    val lat: Double,
    val lon: Double,
    val osmType: String,
    val osmId: Long
)

/**
 * nominatimReverseGeocode — Convierte coordenadas lat/lon a una dirección estructurada
 * usando la API pública de Nominatim (OpenStreetMap).
 *
 * @param lat  Latitud en grados decimales.
 * @param lon  Longitud en grados decimales.
 * @param lang Código de idioma ISO 639-1 para la respuesta. Por defecto "es".
 * @return     GeocodedLocation con los campos normalizados de la dirección.
 * @throws     RuntimeException si la petición HTTP falla o el servidor retorna error.
 */
fun nominatimReverseGeocode(lat: Double, lon: Double, lang: String = "es"): GeocodedLocation {
    val url = URL(
        "https://nominatim.openstreetmap.org/reverse" +
            "?format=jsonv2" +
            "&lat=$lat" +
            "&lon=$lon" +
            "&accept-language=$lang" +
            "&zoom=18"
    )

    val conn = url.openConnection() as HttpURLConnection
    conn.setRequestProperty("User-Agent", "fn_registry/1.0")
    conn.connectTimeout = 5_000
    conn.readTimeout = 5_000
    conn.requestMethod = "GET"

    val responseCode = conn.responseCode
    if (responseCode != HttpURLConnection.HTTP_OK) {
        conn.disconnect()
        throw RuntimeException(
            "nominatimReverseGeocode: HTTP $responseCode para lat=$lat, lon=$lon"
        )
    }

    val body = try {
        conn.inputStream.bufferedReader(Charsets.UTF_8).use { it.readText() }
    } catch (e: Exception) {
        conn.disconnect()
        throw RuntimeException(
            "nominatimReverseGeocode: error leyendo respuesta — ${e.message}", e
        )
    } finally {
        conn.disconnect()
    }

    val data = JSONObject(body)
    val address = data.optJSONObject("address") ?: JSONObject()

    val city = when {
        address.has("city") && address.getString("city").isNotEmpty() ->
            address.getString("city")
        address.has("town") && address.getString("town").isNotEmpty() ->
            address.getString("town")
        address.has("village") && address.getString("village").isNotEmpty() ->
            address.getString("village")
        else -> ""
    }

    val neighbourhood = when {
        address.has("neighbourhood") && address.getString("neighbourhood").isNotEmpty() ->
            address.getString("neighbourhood")
        address.has("suburb") && address.getString("suburb").isNotEmpty() ->
            address.getString("suburb")
        else -> ""
    }

    return GeocodedLocation(
        displayName = data.optString("display_name", ""),
        street      = address.optString("road", ""),
        houseNumber = address.optString("house_number", ""),
        neighbourhood = neighbourhood,
        city        = city,
        state       = address.optString("state", ""),
        country     = address.optString("country", ""),
        postcode    = address.optString("postcode", ""),
        lat         = data.optString("lat", lat.toString()).toDoubleOrNull() ?: lat,
        lon         = data.optString("lon", lon.toString()).toDoubleOrNull() ?: lon,
        osmType     = data.optString("osm_type", ""),
        osmId       = data.optLong("osm_id", 0L)
    )
}
