package com.ponzischeme89.memby.data.remote

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import com.ponzischeme89.memby.BuildConfig
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit

/** Builds a [EmbyApi] bound to a specific server base URL. */
object EmbyServiceFactory {

    private val json = Json {
        ignoreUnknownKeys = true
        coerceInputValues = true
        isLenient = true
    }

    fun create(
        baseUrl: String,
        deviceIdProvider: () -> String,
        tokenProvider: () -> String?,
    ): EmbyApi {
        val contentType = "application/json".toMediaType()

        // Kept at NONE so access tokens (carried in the api_key query param and
        // X-Emby-Token header) are never written to logs. Raise deliberately for
        // local debugging only.
        val logging = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.NONE
            redactHeader("X-Emby-Token")
            redactHeader("X-Emby-Authorization")
        }

        // Derived from the shared stack, so this keeps the one connection pool and
        // dispatcher the artwork loader also uses. See HttpStack.
        val client = HttpStack.base.newBuilder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .addInterceptor(EmbyAuthInterceptor(deviceIdProvider, tokenProvider))
            .addInterceptor(logging)
            .build()

        return Retrofit.Builder()
            .baseUrl(baseUrl.ensureTrailingSlash())
            .client(client)
            .addConverterFactory(json.asConverterFactory(contentType))
            .build()
            .create(EmbyApi::class.java)
    }
}

/** Adds the Emby auth headers to every request. */
private class EmbyAuthInterceptor(
    private val deviceIdProvider: () -> String,
    private val tokenProvider: () -> String?,
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val deviceId = deviceIdProvider()
        // Version comes from the build, so Emby's device list shows which release a TV
        // is actually running. The client name is deliberately not the app's own — this
        // header leaves the house with whatever Emby does with its logs — and must match
        // what the gateway sends (MEMBY_CLIENT_NAME), or one television signing in both
        // ways would appear as two clients.
        val authHeader = "MediaBrowser Client=\"MbyATV\", " +
            "Device=\"Android TV\", DeviceId=\"$deviceId\", Version=\"${BuildConfig.VERSION_NAME}\""

        val builder = chain.request().newBuilder()
            .header("X-Emby-Authorization", authHeader)
            .header("Accept", "application/json")

        tokenProvider()?.takeIf { it.isNotBlank() }?.let {
            builder.header("X-Emby-Token", it)
        }
        return chain.proceed(builder.build())
    }
}

fun String.ensureTrailingSlash(): String = if (endsWith("/")) this else "$this/"
