package com.omixlab.lckcontrol.data.remote

import android.util.Base64
import android.util.Log
import com.omixlab.lckcontrol.data.local.TokenStore
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class AuthInterceptor @Inject constructor(
    private val tokenStore: TokenStore,
) : Interceptor {

    companion object {
        private const val TAG = "AuthInterceptor"
    }

    private fun extractSub(jwt: String): String? {
        return try {
            val parts = jwt.split(".")
            if (parts.size < 2) return null
            val payload = String(Base64.decode(parts[1], Base64.URL_SAFE or Base64.NO_WRAP))
            JSONObject(payload).optString("sub", "").ifEmpty { null }
        } catch (_: Exception) { null }
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        val original = chain.request()

        // Don't add auth header to auth endpoints (login, refresh)
        val path = original.url.encodedPath
        if (path.contains("/auth/meta/callback") || path.contains("/auth/refresh") || path.contains("/health")) {
            return chain.proceed(original)
        }

        val jwt = tokenStore.getJwt()
        val sub = jwt?.let { extractSub(it) }
        Log.d(TAG, "${original.method} ${path} userId=${sub ?: "NO_JWT"}")
        val request = if (jwt != null) {
            original.newBuilder()
                .header("Authorization", "Bearer $jwt")
                .build()
        } else {
            original
        }

        val response = chain.proceed(request)

        // If 401 and we have a refresh token, try to refresh
        if (response.code == 401) {
            Log.w(TAG, "401 on ${original.method} ${path} — attempting token refresh")
            val refreshToken = tokenStore.getRefreshToken()
            if (refreshToken != null) {
                response.close()
                val newTokens = refreshTokenSync(chain, refreshToken)
                if (newTokens != null) {
                    val newSub = extractSub(newTokens.accessToken)
                    Log.d(TAG, "Token refresh OK, new userId=$newSub (was $sub)")
                    tokenStore.saveSession(newTokens.accessToken, newTokens.refreshToken)
                    // Retry original request with new token
                    val retryRequest = original.newBuilder()
                        .header("Authorization", "Bearer ${newTokens.accessToken}")
                        .build()
                    return chain.proceed(retryRequest)
                } else {
                    Log.e(TAG, "Token refresh FAILED, clearing session")
                    // Refresh failed, clear session
                    tokenStore.clearSession()
                }
            } else {
                Log.e(TAG, "401 but no refresh token available")
            }
        }

        return response
    }

    private fun refreshTokenSync(chain: Interceptor.Chain, refreshToken: String): AuthTokensResponse? {
        return try {
            val baseUrl = chain.request().url.newBuilder()
                .encodedPath("/auth/refresh")
                .build()

            val json = """{"refreshToken":"$refreshToken"}"""
            val mediaType = "application/json".toMediaTypeOrNull()
            val body = json.toRequestBody(mediaType)

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

            val refreshResponse = chain.proceed(refreshRequest)
            if (refreshResponse.isSuccessful) {
                val responseBody = refreshResponse.body?.string() ?: return null
                val moshi = com.squareup.moshi.Moshi.Builder().build()
                val adapter = moshi.adapter(AuthTokensResponse::class.java)
                adapter.fromJson(responseBody)
            } else {
                refreshResponse.close()
                null
            }
        } catch (e: Exception) {
            null
        }
    }
}
