// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package xyz.apiote.bimba.czwek.api

import android.content.Context
import android.util.JsonReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.api.responses.ErrorResponse
import xyz.apiote.bimba.czwek.repo.Position
import xyz.apiote.bimba.czwek.repo.Queryable
import xyz.apiote.bimba.czwek.repo.Stop
import xyz.apiote.bimba.czwek.repo.TrafficResponseException
import xyz.apiote.bimba.czwek.units.DistanceUnit
import xyz.apiote.bimba.czwek.units.Metre

suspend fun getTransitousQueryables(query: String, context: Context): List<Queryable> {
	if (!isNetworkAvailable(context)) {
		throw TrafficResponseException(0, "", Error(0, R.string.error_offline, R.drawable.error_net))
	}

	val json = """
		{
			"destination": {
				"type": "Module",
				"target": "/guesser"
			},
			"content_type": "StationGuesserRequest",
			"content": {
				"input": "$query",
				"guess_count": 24
			}
		}
	"""
	val result = askTransitous(context, json)

	if (result.error != null) {
		if (result.stream != null) {
			val response = withContext(Dispatchers.IO) { ErrorResponse.unmarshal(result.stream) }
			throw TrafficResponseException(result.error.statusCode, response.message, result.error)
		} else {
			throw TrafficResponseException(result.error.statusCode, "", result.error)
		}
	} else {
		return withContext(Dispatchers.IO) {
			val queryables = mutableListOf<Queryable>()
			val r = JsonReader(result.stream!!.bufferedReader())
			r.beginObject()
			while (r.hasNext()) {
				val rootFieldName = r.nextName() // content
				if (rootFieldName == "content") {
					r.beginObject()
					r.nextName() // guesses
					r.beginArray()
					while (r.hasNext()) {
						var id = ""
						var name = ""
						var latitude = 0.0
						var longitude = 0.0
						r.beginObject()
						while (r.hasNext()) {
							val fieldName = r.nextName()
							when (fieldName) {
								"id" -> id = r.nextString()
								"name" -> name = r.nextString()
								"pos" -> {
									r.beginObject()
									while (r.hasNext()) {
										val positionFieldName = r.nextName()
										when (positionFieldName) {
											"lat" -> latitude = r.nextDouble()
											"lng" -> longitude = r.nextDouble()
										}
									}
									r.endObject()
								}
							}
						}
						r.endObject()
						queryables.add(
							Stop(
								code = id,
								name = name,
								nodeName = name,
								zone = "",
								feedID = "transitous",
								position = Position(latitude = latitude, longitude = longitude),
								changeOptions = emptyList()
							)
						)
					}
					r.endArray()
					r.endObject()
				} else {
					r.skipValue()
				}
			}
			r.endObject()

			return@withContext queryables
		}
	}
}

suspend fun locateTransitousQueryables(position: Position, context: Context, radius: DistanceUnit = Metre(500.0)): List<Queryable> {
	if (!isNetworkAvailable(context)) {
		throw TrafficResponseException(0, "", Error(0, R.string.error_offline, R.drawable.error_net))
	}

	val json = """
		{
			"destination": {
				"type": "Module",
				"target": "/lookup/geo_station"
			},
			"content_type": "LookupGeoStationRequest",
			"content": {
				"max_radius": ${radius.meters().toInt()},
				"pos": {
					"lat": ${position.latitude},
					"lng": ${position.longitude}
				}
			}
		}
	"""
	val result = askTransitous(context, json)

	if (result.error != null) {
		if (result.stream != null) {
			val response = withContext(Dispatchers.IO) { ErrorResponse.unmarshal(result.stream) }
			throw TrafficResponseException(result.error.statusCode, response.message, result.error)
		} else {
			throw TrafficResponseException(result.error.statusCode, "", result.error)
		}
	} else {
		return withContext(Dispatchers.IO) {
			val queryables = mutableListOf<Queryable>()
			val r = JsonReader(result.stream!!.bufferedReader())
			r.beginObject()
			while (r.hasNext()) {
				val rootFieldName = r.nextName() // content
				if (rootFieldName == "content") {
					r.beginObject()
					r.nextName() // guesses
					r.beginArray()
					while (r.hasNext()) {
						var id = ""
						var name = ""
						var latitude = 0.0
						var longitude = 0.0
						r.beginObject()
						while (r.hasNext()) {
							val fieldName = r.nextName()
							when (fieldName) {
								"id" -> id = r.nextString()
								"name" -> name = r.nextString()
								"pos" -> {
									r.beginObject()
									while (r.hasNext()) {
										val positionFieldName = r.nextName()
										when (positionFieldName) {
											"lat" -> latitude = r.nextDouble()
											"lng" -> longitude = r.nextDouble()
										}
									}
									r.endObject()
								}
							}
						}
						r.endObject()
						queryables.add(
							Stop(
								code = id,
								name = name,
								nodeName = name,
								zone = "",
								feedID = "transitous",
								position = Position(latitude = latitude, longitude = longitude),
								changeOptions = emptyList()
							)
						)
					}
					r.endArray()
					r.endObject()
				} else {
					r.skipValue()
				}
			}
			r.endObject()
			return@withContext queryables
		}
	}
}
