package top.yeij.cyrene.data.remote

import com.google.gson.Gson
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.runBlocking
import okhttp3.Authenticator
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Route
import top.yeij.cyrene.data.local.PreferencesDataStore
import top.yeij.cyrene.data.remote.dto.AuthResponse
import top.yeij.cyrene.data.remote.dto.RefreshTokenRequest
import android.util.Log

class TokenAuthenticator(
    private val authInterceptor: AuthInterceptor,
    private val preferencesDataStore: PreferencesDataStore,
    private val dynamicUrlInterceptor: DynamicUrlInterceptor,
) : Authenticator {

    private val gson = Gson()
    private val client = OkHttpClient()

    override fun authenticate(route: Route?, response: okhttp3.Response): Request? {
        if (response.code != 401) return null

        synchronized(this) {
            // Double-check: if token changed while we were waiting for lock, use it
            val currentToken = authInterceptor.token
            val requestAuthHeader = response.request.header("Authorization")
            if (requestAuthHeader != null && currentToken != null &&
                requestAuthHeader != "Bearer $currentToken"
            ) {
                return response.request.newBuilder()
                    .header("Authorization", "Bearer $currentToken")
                    .build()
            }

            // Try refresh
            return try {
                val refreshToken = runBlocking { preferencesDataStore.refreshToken.firstOrNull() }
                    ?: return null

                val baseUrl = dynamicUrlInterceptor.baseUrl.trimEnd('/')
                val refreshUrl = "$baseUrl/api/v1/auth/refresh"

                val jsonBody = gson.toJson(RefreshTokenRequest(refreshToken))
                val body = jsonBody.toRequestBody("application/json".toMediaType())

                val refreshRequest = Request.Builder()
                    .url(refreshUrl)
                    .post(body)
                    .build()

                val refreshResponse = client.newCall(refreshRequest).execute()
                if (refreshResponse.isSuccessful) {
                    val authResponse = gson.fromJson(
                        refreshResponse.body?.string(),
                        AuthResponse::class.java
                    )
                    authInterceptor.token = authResponse.token
                    runBlocking {
                        preferencesDataStore.saveToken(authResponse.token)
                        authResponse.refreshToken?.let {
                            preferencesDataStore.saveRefreshToken(it)
                        }
                    }
                    Log.i("TokenAuthenticator", "Token refreshed successfully")
                    response.request.newBuilder()
                        .header("Authorization", "Bearer ${authResponse.token}")
                        .build()
                } else {
                    Log.w("TokenAuthenticator", "Token refresh failed: ${refreshResponse.code}")
                    null
                }
            } catch (e: Exception) {
                Log.e("TokenAuthenticator", "Token refresh error: ${e.message}", e)
                null
            }
        }
    }
}
