package com.mybus17000.data

import android.content.Context
import android.location.Geocoder
import android.util.Log
import com.google.android.gms.maps.model.LatLng
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
import java.net.URL
import java.net.URLEncoder
import java.net.UnknownHostException
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.util.Locale


suspend fun fetchAddresses(query: String): List<String> = withContext(Dispatchers.IO) {
    try {
        if (query.length < 3) return@withContext emptyList()

        val encoded = URLEncoder.encode(query, "UTF-8")
        val url =
            "https://api-adresse.data.gouv.fr/search/?q=$encoded&limit=10&autocomplete=1&lat=45.75&lon=-1.1"

        val response = URL(url).readText()
        val json = JSONObject(response)
        val features = json.getJSONArray("features")

        val results = mutableListOf<String>()
        for (i in 0 until features.length()) {
            val props = features.getJSONObject(i).getJSONObject("properties")
            val label = props.getString("label")
            val context = props.optString("context", "")

            // 🔹 Filtrer sur la Charente-Maritime (département 17)
            if (context.contains("17") || context.contains("Charente-Maritime", ignoreCase = true)) {
                results.add(label)
            }
        }

        results
    } catch (e: Exception) {
        e.printStackTrace()
        emptyList()
    }
}
suspend fun fetchStops(): List<StopFields> = withContext(Dispatchers.IO) {
    val client = OkHttpClient()
    // IMPORTANT : note le "?" après "search/"
    val url =
        "https://opendata.agglo-larochelle.fr/d4c/api/records/1.0/search/?" +
                "dataset=transport_yelo___gtfs_stop_des_bus" +
                "&resource_id=efc3e9e8-4c12-4aac-ba9d-fa1c93c2445c" +
                "&facet=stop_id" +
                "&rows=1500" // tu peux augmenter/diminuer
    val request = Request.Builder()

        .url(url)
        .get()
        .build()
    client.newCall(request).execute().use { response ->
        val code = response.code
        val contentType = response.header("Content-Type") ?: "unknown"
        val body = response.body?.string() ?: throw Exception("Réponse vide (null body)")

        // Logs utiles pour debugging
        Log.d("STOPS_RAW", "HTTP $code  Content-Type: $contentType")
        Log.d("STOPS_RAW_PREVIEW", body.take(2000)) // n'affiche que les 2000 premiers chars

        // Si le serveur n'a pas renvoyé 200, on loggue et on quitte proprement
        if (!response.isSuccessful) {
            Log.e("STOPS_FETCH", "HTTP error $code - body preview: ${body.take(500)}")
            return@withContext emptyList()
        }

        // Parser JSON de façon tolérante
        val json = Json { ignoreUnknownKeys = true }

        return@withContext try {
            val root = json.decodeFromString<StopResponse>(body)
            Log.d("STOPS_COUNT", "records = ${root.records.size}")
            root.records.map { it.fields }
        } catch (e: Exception) {
            // affiche l'erreur de parsing + début du body pour comprendre
            Log.e("STOPS_PARSE", "Erreur parsing JSON: ${e.message}")
            Log.e("STOPS_PARSE", "Body preview: ${body.take(2000)}")
            // Optionnel : tenter d'analyser structure minimale
            try {
                val el = json.parseToJsonElement(body)
                Log.d("STOPS_PARSE", "Root JSON element type: ${el::class.simpleName}")
            } catch (_: Exception) { /* ignore */ }

            emptyList()
        }
    }
}

suspend fun fetchRoutePolylinesByTrip_id(
    stops: List<StopFields>,
    trip_id: String?
): Map<String, RoutePolylineData> = withContext(Dispatchers.IO) {

    if (trip_id == null) return@withContext emptyMap()

    // Map résultat : trip_id → RoutePolylineData (points, nom, couleur)
    val routePolylines = mutableMapOf<String, RoutePolylineData>()
    try {
        // 1️⃣ Récupère tous les stop_id liés à ce trip
        val tripStopIds = fetchBusStopId(trip_id)

        // 2️⃣ Construit la liste de points (lat/lon)
        val points = tripStopIds.mapNotNull { stopId ->
            stops.find { it.stop_id == stopId }?.let {
                LatLng(it.stop_lat.toDouble(), it.stop_lon.toDouble())
            }
        }

        // 3️⃣ Récupère le nom et la couleur de la ligne à partir du trip_id
        val busName = fetchBusLineName(trip_id)
        val lineInfo = fetchLineInfoFromTripId(busName)
        val (lineName, lineColor) = lineInfo ?: ("Inconnue" to "#1971DE")

        // 4️⃣ Ajoute dans la map si au moins deux points
        if (points.size > 1) {
            routePolylines[trip_id] = RoutePolylineData(
                points = points,
                lineName = lineName,
                color = lineColor
            )
        }

        Log.d("POLYLINE", "Trip $trip_id → ${points.size} points, $lineName ($lineColor)")

    } catch (e: Exception) {
        e.printStackTrace()
        Log.e("POLYLINE", "Erreur lors du chargement de la polyline : ${e.message}")
    }
    return@withContext routePolylines
}
suspend fun loadPolylineDataFromAssets(context: Context): List<RoutePolylineData>  {


    return emptyList()
}

suspend fun fetchStopTimes(stopId: String?): List<StopTimeFields> {
    val client = OkHttpClient()
    val url =
        "https://opendata.agglo-larochelle.fr/d4c/api/records/1.0/search/" +
                "?dataset=transport_yelo___gtfs_stop_times_des_bus" +
                "&resource_id=e31c8338-26f7-4076-af86-93714833841f" +
                "&facet=trip_id" +
                "&refine.stop_id=$stopId"

    val request = Request.Builder().url(url).get().build()
    client.newCall(request).execute().use { response ->
        val code = response.code
        val body = response.body?.string() ?: throw Exception("Réponse vide")

        Log.d("STOP_TIMES_RAW", "HTTP $code")
        Log.d("STOP_TIMES_BODY", body.take(1000))

        if (!response.isSuccessful) {
            Log.e("STOP_TIMES", "Erreur HTTP $code")
            return emptyList()
        }

        val json = Json { ignoreUnknownKeys = true }
        return try {
            val root = json.decodeFromString<StopTimeResponse>(body)
            Log.d("STOP_TIMES", "Records = ${root.records.size}")
            root.records.map { it.fields }
        } catch (e: Exception) {
            Log.e("STOP_TIMES_PARSE", "Erreur parsing: ${e.message}")
            emptyList()
        }
    }
}


suspend fun fetchBusLineName(tripId: String): String? {
    val client = OkHttpClient()
    val url = "https://opendata.agglo-larochelle.fr/d4c/api/records/1.0/search/dataset=transport_yelo___gtfs_trips_des_bus&resource_id=1ecbd570-9e39-47f6-ba87-bea64c69e727&facet=route_id&refine.trip_id=$tripId"

    val request = Request.Builder().url(url).get().build()
    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) {
            println("Erreur HTTP: ${response.code}")
            return null
        }

        val body = response.body?.string() ?: return null
        val json = Json { ignoreUnknownKeys = true }
        val root = json.decodeFromString<JsonObject>(body)

        val records = root["records"]?.jsonArray ?: return null
        if (records.isEmpty()) return null

        val fields = records[0].jsonObject["fields"]?.jsonObject ?: return null
        return fields["route_id"]?.jsonPrimitive?.content
    }
}

suspend fun fetchBusStopId(trip_id: String?) : List<String> = withContext(Dispatchers.IO){
    val client = OkHttpClient()
    val url ="https://opendata.agglo-larochelle.fr/d4c/api/records/1.0/search/dataset=transport_yelo___gtfs_stop_times_des_bus&resource_id=e31c8338-26f7-4076-af86-93714833841f&facet=trip_id&refine.trip_id=$trip_id"

    val request = Request.Builder().url(url).get().build()
    client.newCall(request).execute().use { response ->
        val body = response.body?.string() ?: throw Exception("Réponse vide")
        if (!response.isSuccessful) {
            println("Erreur HTTP: ${response.code}")
            return@use emptyList<String>()
        }

        try {
            // 🔹 On parse le JSON manuellement avec kotlinx.serialization.json
            val json = Json.parseToJsonElement(body).jsonObject
            val records = json["records"]?.jsonArray ?: return@use emptyList()

            // 🔹 On extrait tous les stop_id présents
            val stopIds = records.mapNotNull { record ->
                val fields = record.jsonObject["fields"]?.jsonObject
                fields?.get("stop_id")?.jsonPrimitive?.content
            }

            Log.d("STOPS", "Trouvé ${stopIds.size} stops pour $trip_id")
            stopIds

        } catch (e: Exception) {
            Log.e("STOPS_PARSE", "Erreur parsing: ${e.message}")
            emptyList()
        }
    }
}

suspend fun fetchBusLineRouteId(routeId: String): String? {
    val client = OkHttpClient()
    val url ="https://opendata.agglo-larochelle.fr/d4c/api/records/1.0/search/dataset=transport_yelo___gtfs_trips_des_bus&resource_id=1ecbd570-9e39-47f6-ba87-bea64c69e727&facet=route_id&rows=1&refine.route_id=$routeId"

    val request = Request.Builder().url(url).get().build()
    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) {
            println("Erreur HTTP: ${response.code}")
            return null
        }

        val body = response.body?.string() ?: return null
        val json = Json { ignoreUnknownKeys = true }
        val root = json.decodeFromString<JsonObject>(body)

        val records = root["records"]?.jsonArray ?: return null
        if (records.isEmpty()) return null

        val fields = records[0].jsonObject["fields"]?.jsonObject ?: return null
        return fields["trip_id"]?.jsonPrimitive?.content
    }
}

suspend fun fetchLineInfoFromTripId(routeId: String?): Pair<String, String>? = withContext(Dispatchers.IO) {
    try {
        val apiUrl =
            "https://opendata.agglo-larochelle.fr/d4c/api/records/1.0/search/dataset=transport_yelo___gtfs_routes_des_bus&resource_id=f1447749-73fa-433a-96ec-1bfeed083d0c&facet=route_id&facet=agency_id&facet=route_short_name&facet=route_long_name&facet=route_type&facet=route_color&refine.route_id=$routeId"

        val response = URL(apiUrl).readText()
        val json = Json.parseToJsonElement(response).jsonObject
        val records = json["records"]?.jsonArray ?: return@withContext null
        if (records.isEmpty()) return@withContext null

        val fields = records[0].jsonObject["fields"]?.jsonObject ?: return@withContext null

        val routeLongName = fields["route_long_name"]?.jsonPrimitive?.content ?: "Ligne inconnue"
        val routeColor = fields["route_color"]?.jsonPrimitive?.content ?: "1971DE" // couleur par défaut bleu Yélo

        Pair(routeLongName, "#$routeColor")
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}


suspend fun fetchStopPredictions(input: String): List<String> =
    withContext(Dispatchers.IO) {
        if (input.isBlank()) return@withContext emptyList()
        try {
        val url = "https://opendata.agglo-larochelle.fr/d4c/api/records/1.0/search/"+
                "?dataset=transport_yelo___gtfs_stop_des_bus"+
                "&q=$input" +
                "&rows=10"

        val client = OkHttpClient()
        val request = Request.Builder().url(url).build()

        client.newCall(request).execute().use { response ->
            val body = response.body?.string() ?: return@withContext emptyList()
            val json = JSONObject(body)
            val records = json.optJSONArray("records") ?: return@withContext emptyList()

            val results = mutableListOf<String>()
            for (i in 0 until records.length()) {
                val fields = records.optJSONObject(i)?.optJSONObject("fields")
                val stopName = fields?.optString("stop_name")
                if (!stopName.isNullOrEmpty()) {
                    results.add("Arrêt : $stopName")
                }
            }
            results
        }
        } catch (e: UnknownHostException) {
            Log.e("NETWORK", "Impossible de joindre le serveur: ${e.message}")
            emptyList() // renvoyer vide si pas de réseau
        } catch (e: Exception) {
            Log.e("NETWORK", "Erreur fetchStopPredictions: ${e.message}")
            emptyList()
        }
    }
suspend fun fetchBusLineDirection(tripId: String): String? {
    val client = OkHttpClient()
    val url = "https://opendata.agglo-larochelle.fr/d4c/api/records/1.0/search/dataset=transport_yelo___gtfs_trips_des_bus&resource_id=1ecbd570-9e39-47f6-ba87-bea64c69e727&facet=route_id&refine.trip_id=$tripId"

    val request = Request.Builder().url(url).get().build()
    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) {
            println("Erreur HTTP: ${response.code}")
            return null
        }

        val body = response.body?.string() ?: return null
        val json = Json { ignoreUnknownKeys = true }
        val root = json.decodeFromString<JsonObject>(body)

        val records = root["records"]?.jsonArray ?: return null
        if (records.isEmpty()) return null

        val fields = records[0].jsonObject["fields"]?.jsonObject ?: return null
        return fields["trip_headsign"]?.jsonPrimitive?.content
    }
}


suspend fun getNextTwoTimesWithLineName(stopTimes: List<StopTimeFields>): List<String> {
    val now = LocalTime.now()
    val formatter = DateTimeFormatter.ofPattern("HH:mm")

    // Associe tripId et horaires
    val timesWithTrip = stopTimes.mapNotNull { stopTime ->
        stopTime.departure_time?.let { timeStr ->
            try {
                val parsed = LocalTime.parse(timeStr, DateTimeFormatter.ofPattern("HH:mm:ss"))
                stopTime.trip_id?.let { tripId -> tripId to parsed }
            } catch (e: Exception) {
                null
            }
        }
    }

    // Filtrer + trier + garder 2 prochains
    val nextTwo = timesWithTrip
        .filter { (_, time) -> time.isAfter(now) }
        .sortedBy { (_, time) -> time }
        .take(2)

    // ⚡ Ici on boucle manuellement pour pouvoir appeler suspend
    val results = mutableListOf<String>()
    var result = ""

    for ((tripId, time) in nextTwo) {
        val busLineDirection = tripId?.let {
            withContext(Dispatchers.IO) { fetchBusLineDirection(it) }
        }

        val busLineName = tripId?.let {
            withContext(Dispatchers.IO) { fetchBusLineName(it) }
        }
        val resultNew = "Direction $busLineDirection \n Ligne $busLineName → ${time.format(formatter)}"
        if (result == "") {
            result = resultNew
            results.add(result)
        }
        else if (result != resultNew) {
            results.add(resultNew)
        }
    }
    return results
}

/**
 * Géocode une adresse en coordonnées GPS (LatLng).
 * Retourne null si l'adresse n'a pas pu être trouvée.
 */
suspend fun geocodeAddressSuspend(context: Context, address: String): LatLng? {
    return withContext(Dispatchers.IO) {
        try {
            val geocoder = Geocoder(context, Locale.getDefault())
            val results = geocoder.getFromLocationName(address, 1)
            if (!results.isNullOrEmpty()) {
                val location = results[0]
                LatLng(location.latitude, location.longitude)
            } else {
                null
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
}
