package com.jihe.punchnet.api

import android.util.Log
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit

class ApiClient {
    private val client = OkHttpClient.Builder()
        .connectTimeout(15, TimeUnit.SECONDS)
        .readTimeout(15, TimeUnit.SECONDS)
        .writeTimeout(15, TimeUnit.SECONDS)
        .addInterceptor(SignInterceptor())
        .build()

    private val gson = Gson()
    private val JSON = "application/json; charset=utf-8".toMediaType()

    private val BASE_URL = "https://root.punchsky.com/api"

    suspend fun loginWithToken(request: AuthTokenRequest): BaseResponse<AuthResponseData>? {
        return post("/auth/token", request)
    }

    suspend fun connect(request: ConnectRequest): BaseResponse<ConnectResponseData>? {
        return post("/connect", request)
    }

    suspend fun disconnect(request: DisconnectRequest): BaseResponse<Any>? {
        return post("/disconnect", request)
    }

    suspend fun loginWithAccount(request: AuthLoginRequest): BaseResponse<AuthResponseData>? {
        return post("/auth/login", request)
    }

    suspend fun getNodeResources(request: GetNodeResourcesRequest): BaseResponse<GetNodeResourcesResponseData>? {
        return post("/get_node_resources", request)
    }

    suspend fun getAcl(request: AclRequest): BaseResponse<AclResponseData>? {
        return post("/acl", request)
    }

    private suspend inline fun <reified T, reified R> post(path: String, requestBody: T): BaseResponse<R>? {
        return withContext(Dispatchers.IO) {
            try {
                val jsonBody = gson.toJson(requestBody)
                Log.i("apiClient", "post [${BASE_URL + path}]")
                val body = jsonBody.toRequestBody(JSON)
                val request = Request.Builder()
                    .url(BASE_URL + path)
                    .post(body)
                    .build()

                client.newCall(request).execute().use { response ->
                    if (!response.isSuccessful) {
                        response.body?.close()
                        Log.e("apiClient", "response error [${BASE_URL + path}]: HTTP ${response.code}")
                        return@withContext BaseResponse<R>(code = -1, message = "HTTP ${response.code}: ${response.message}", data = null)
                    }
                    val responseStr = response.body?.string() ?: return@withContext BaseResponse<R>(code = -1, message = "Empty response body", data = null)
                    Log.i("apiClient", "response [${BASE_URL + path}]: HTTP ${response.code}")
                    
                    val type = object : TypeToken<BaseResponse<R>>() {}.type
                    gson.fromJson<BaseResponse<R>>(responseStr, type)
                }
            } catch (e: Exception) {
                e.printStackTrace()
                Log.e("apiClient", "response exception [${BASE_URL + path}]: ${e.message}")
                BaseResponse<R>(code = -1, message = e.message ?: "Unknown error", data = null)
            }
        }
    }
}
