package com.fabledsword.minstrel.connectivity

import com.fabledsword.minstrel.api.BaseUrlInterceptor.Companion.PLACEHOLDER_HOST
import dagger.Lazy
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton

private const val HEALTHZ_PATH = "/healthz"

/**
 * Feeds real Minstrel API outcomes into [NetworkStatusController]. A 2xx is
 * self-proving proof the server is reachable → reportSuccess(); a transport
 * [IOException] (no response at all) → reportFailure(), which triggers /healthz
 * arbitration.
 *
 * MUST run first in the OkHttp chain (before [BaseUrlInterceptor]) so the host
 * is still the [PLACEHOLDER_HOST] sentinel: this shared client also fetches
 * EXTERNAL artwork (musicbrainz / coverartarchive), and an external image
 * loading must NOT be read as "our server is reachable" — only sentinel-host
 * requests are Minstrel-bound. 5xx is deliberately NOT a failure (the server
 * answered), and /healthz is skipped to avoid a feedback loop with the poll.
 *
 * [NetworkStatusController] is injected as a [Lazy] to break the Hilt cycle:
 * the controller needs `Retrofit`, which needs `OkHttpClient`, which needs this
 * interceptor. By the time a request flows through, the controller singleton is
 * already constructed (construct-the-singleton trick in MinstrelApplication).
 */
@Singleton
class ReachabilityReportingInterceptor @Inject constructor(
    private val networkStatus: Lazy<NetworkStatusController>,
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val isMinstrel = request.url.host == PLACEHOLDER_HOST
        val isHealthz = request.url.encodedPath.endsWith(HEALTHZ_PATH)
        if (!isMinstrel || isHealthz) return chain.proceed(request)
        return try {
            val response = chain.proceed(request)
            if (response.isSuccessful) networkStatus.get().reportSuccess()
            response
        } catch (e: IOException) {
            networkStatus.get().reportFailure()
            throw e
        }
    }
}
