package org.torproject.vpn.circumvention

import okhttp3.OkHttpClient
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST

interface CircumventionEndpoints {

    @GET("countries")
    fun getCountries(): Call<List<String>>

    @GET("builtin")
    fun getBuiltinTransports(): Call<BuiltInBridgesResponse>

    @POST("settings")
    fun getSettings(@Body request: SettingsRequest): Call<SettingsResponse>

    @POST("defaults")
    fun getDefaults(@Body request: SettingsRequest): Call<SettingsResponse>

    @GET("map")
    fun getMap(): Call<Map<String, SettingsResponse>>

}

object ServiceBuilder {
    private val client = OkHttpClient.Builder().build()

    private val retrofit = Retrofit.Builder()
        .baseUrl("https://bridges.torproject.org/moat/circumvention/")
        .addConverterFactory(GsonConverterFactory.create())
        .client(client)
        .build()

    fun<T> buildService(service: Class<T>): T = retrofit.create(service)
}

class CircumventionApiManager {
    private val retrofit = ServiceBuilder.buildService(CircumventionEndpoints::class.java)

    fun getCountries(onResult: (List<String>?) -> Unit, onError: ((Throwable) -> Unit)? = null) {
        retrofit.getCountries().enqueue(object: Callback<List<String>> {
            override fun onResponse(call: Call<List<String>>, response: Response<List<String>>) {
                onResult(response.body())
            }

            override fun onFailure(call: Call<List<String>>, t: Throwable) {
                onError?.let { it(t) }
            }
        })
    }

    fun getBuiltInTransports(onResult: (BuiltInBridgesResponse?) -> Unit, onError: ((Throwable) -> Unit)? = null) {
        retrofit.getBuiltinTransports().enqueue(object : Callback<BuiltInBridgesResponse> {
            override fun onResponse(call: Call<BuiltInBridgesResponse>, response: Response<BuiltInBridgesResponse>) {
                onResult(response.body())
            }

            override fun onFailure(call: Call<BuiltInBridgesResponse>, t: Throwable) {
                onError?.let { it(t) }
            }
        })
    }

    fun getSettings(request: SettingsRequest, onResult: (SettingsResponse?) -> Unit, onError: ((Throwable) -> Unit)? = null) {
        retrofit.getSettings(request).enqueue(object: Callback<SettingsResponse> {
            override fun onResponse(call: Call<SettingsResponse>, response: Response<SettingsResponse>) {
                onResult(response.body())
            }

            override fun onFailure(call: Call<SettingsResponse>, t: Throwable) {
                onError?.let { it(t) }
            }

        })
    }

    fun getDefaults(request: SettingsRequest, onResult: (SettingsResponse?) -> Unit, onError: ((Throwable) -> Unit)? = null) {
        retrofit.getDefaults(request).enqueue(object: Callback<SettingsResponse> {
            override fun onResponse(call: Call<SettingsResponse>, response: Response<SettingsResponse>) {
                onResult(response.body())
            }

            override fun onFailure(call: Call<SettingsResponse>, t: Throwable) {
                onError?.let { it(t) }
            }
        })
    }

    fun getMap(onResult: (Map<String, SettingsResponse>?) -> Unit, onError: ((Throwable) -> Unit)? = null) {
        retrofit.getMap().enqueue(object: Callback<Map<String, SettingsResponse>> {
            override fun onResponse(call: Call<Map<String, SettingsResponse>>, response: Response<Map<String, SettingsResponse>>) {
                onResult(response.body())
            }

            override fun onFailure(call: Call<Map<String, SettingsResponse>>, t: Throwable) {
                onError?.let { it(t) }
            }
        })
    }
}