package com.humand.demo.modules.IncomingCallMessagingService.api

import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.util.Log
import com.humand.demo.BuildConfig
import com.humand.demo.modules.IncomingCallMessagingService.common.Constants
import com.reactnativekeysjsi.KeysModule
import com.tencent.mmkv.MMKV
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.json.JSONObject


data class AuthTokens(
    val accessToken: String?,
    val refreshToken: String?
)

@SuppressLint("StaticFieldLeak")
object ApiService {
    private val TAG = BuildConfig.APPLICATION_ID
    private val baseUrl = BuildConfig.API_URL
    private lateinit var context: Context
    private val client: OkHttpClient by lazy {
        OkHttpClient()
    }

    private val mmkv by lazy {
        val encryptionKey = getKeychainKey()
        MMKV.mmkvWithID("user-storage", MMKV.MULTI_PROCESS_MODE, encryptionKey)
    }

    fun init(appContext: Context) {
        context = appContext.applicationContext
        MMKV.initialize(context)
    }

    private fun getKeychainKey():String{
        return KeysModule.getSecureFor(Constants.API.KEYCHAIN_KEY)
    }

    private fun getAuthTokens(): AuthTokens? {
        val value =  mmkv.decodeString(Constants.API.TOKENS) ?: return null
        return try {
            val json = JSONObject(value)
            val accessToken = json.optString(Constants.API.ACCESS_TOKEN, "")
            val refreshToken = json.optString(Constants.API.REFRESH_TOKEN, "")

            AuthTokens(accessToken, refreshToken)
        } catch (e: Exception) {
            Log.e(TAG, "Failed to parse TOKENS: ${e.message}")
            null
        }
    }

    private fun getHeaders(authToken:String): Headers {
        val osVersion = Build.VERSION.RELEASE
        val versionName = try {
            context.packageManager.getPackageInfo(context.packageName, 0).versionName
        } catch (e: Exception) {
            "unknown"
        }

        return Headers.Builder()
            .add("Authorization", "Bearer $authToken")
            .add("Content-Type", "application/json")
            .add("Accept", "*/*")
            .add("x-device-type", "android")
            .add(Constants.API.Headers.ORIGIN, Constants.API.Headers.ORIGIN_ANDROID)
            .add("x-humand-device-version", versionName.toString())
            .add("x-device-version", osVersion)
            .build()
    }

    suspend fun rejectCallSuspend(id: String) {
        try {
            val tokens = getAuthTokens()
            if (tokens == null || tokens.accessToken.isNullOrBlank() || tokens.refreshToken.isNullOrBlank()) return

            fun makeRequest(accessToken: String): Response {
                val headers = getHeaders(accessToken)
                val request = Request.Builder()
                    .url("$baseUrl/calls/$id/reject")
                    .post("".toRequestBody(null))
                    .headers(headers)
                    .build()

                return client.newCall(request).execute()
            }

            val response = makeRequest(tokens.accessToken)

            response.use {
                if (it.isSuccessful) {
                    Log.d(TAG, "Call rejected successfully: ${it.body?.string()}")
                    return
                }

                val errorBody = it.body?.string()
                Log.e(TAG, "Reject call failed: HTTP ${it.code}, body: $errorBody")

                val shouldRefreshToken = it.code == 401 && try {
                    val json = JSONObject(errorBody ?: "")
                    json.optString("code") == Constants.API.TOKEN_EXPIRED
                } catch (e: Exception) {
                    false
                }

                // Refresh the access token and call this method again after saving the new tokens to the store
                if (shouldRefreshToken) {
                    val newTokens = refreshToken(tokens.refreshToken)
                    if (newTokens?.accessToken != null) {
                        val retryResponse = makeRequest(newTokens.accessToken)
                        retryResponse.use { retry ->
                            if (retry.isSuccessful) {
                                Log.d(TAG, "Retry reject successful: ${retry.body?.string()}")
                            } else {
                                Log.e(TAG, "Retry reject failed: HTTP ${retry.code}, body: ${retry.body?.string()}")
                            }
                        }
                    } else {
                        Log.e(TAG, "Token refresh failed.")
                    }
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "Reject call exception: ${e.message}")
        }
    }

    fun refreshToken(token: String): AuthTokens? {
        val headers = getHeaders(token)
        val request = Request.Builder()
            .url("$baseUrl/auth/refresh")
            .headers(headers)
            .build()

        val response = client.newCall(request).execute()
        response.use {
            val bodyString = it.body?.string()

            if (!it.isSuccessful) {
                Log.e(TAG, "Refresh token failed: HTTP ${it.code}, body: $bodyString")
                return null
            }

            return try {
                val json = JSONObject(bodyString ?: "")
                val accessToken = json.optString("accessToken", "")
                val refreshToken = json.optString("refreshToken", "")

                if (accessToken.isNotEmpty() && refreshToken.isNotEmpty()) {
                    val tokens = AuthTokens(accessToken, refreshToken)
                    // Save new tokens to the store
                    updateTokens(tokens)
                    tokens
                } else {
                    Log.e(TAG, "Missing tokens in response: $bodyString")
                    null
                }
            } catch (e: Exception) {
                Log.e(TAG, "Failed to parse JSON: ${e.message}")
                null
            }
        }
    }

    private fun updateTokens(tokens: AuthTokens) {
        try {
            val json = JSONObject().apply {
                put(Constants.API.ACCESS_TOKEN, tokens.accessToken)
                put(Constants.API.REFRESH_TOKEN, tokens.refreshToken)
            }
            mmkv.encode(Constants.API.TOKENS, json.toString())
            Log.d(TAG, "Tokens updated successfully")
        } catch (e: Exception) {
            Log.e(TAG, "Failed to save tokens: ${e.message}")
        }
    }
}
