package com.fabledsword.minstrel.connectivity

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Single source of truth for "does the device have a network link at
 * all" — wraps [ConnectivityManager] and exposes a hot cold-startable
 * Flow that emits `false` only when there is no active INTERNET-capable
 * network (airplane mode, no carrier/Wi-Fi) and `true` once any network
 * link appears.
 *
 * Deliberately does NOT require `NET_CAPABILITY_VALIDATED`. VALIDATED
 * tracks whether Android reached its own WAN internet-validation probe
 * (Google's `generate_204`) — which is the wrong question for a
 * self-hosted server that is usually on the LAN. A transient WAN/DNS
 * blip (or Android's periodic re-validation) momentarily drops VALIDATED
 * while the Minstrel box stays perfectly reachable; gating on it flipped
 * the app to Offline with no debounce and fast-failed in-flight playback
 * via [com.fabledsword.minstrel.player.OfflineGatedDataSource]. The
 * authority on whether *Minstrel* is reachable is the `/healthz` poll
 * ([com.fabledsword.minstrel.connectivity.NetworkStatusController], which
 * has its own failure hysteresis), not this coarse device-link signal.
 *
 * Used by the shell-level ConnectionErrorBanner; downstream
 * repositories can also collect this to gate retry loops.
 */
@Singleton
class ConnectivityObserver @Inject constructor(
    @ApplicationContext context: Context,
) {
    private val cm = context.getSystemService(ConnectivityManager::class.java)

    val online: Flow<Boolean> = callbackFlow {
        val request = NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build()
        val callback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                trySend(hasActiveNetwork())
            }
            override fun onLost(network: Network) {
                trySend(hasActiveNetwork())
            }
            override fun onCapabilitiesChanged(
                network: Network,
                capabilities: NetworkCapabilities,
            ) {
                // INTERNET only -- NOT VALIDATED. A WAN/validation flicker
                // must not read as "device offline" when the LAN (and the
                // Minstrel server on it) is still reachable. /healthz is the
                // authority on server reachability.
                trySend(
                    capabilities.hasCapability(
                        NetworkCapabilities.NET_CAPABILITY_INTERNET,
                    ),
                )
            }
        }
        cm.registerNetworkCallback(request, callback)
        // Seed the initial value so the banner doesn't flash before the
        // first capability callback fires.
        trySend(hasActiveNetwork())
        awaitClose { cm.unregisterNetworkCallback(callback) }
    }.distinctUntilChanged()

    private fun hasActiveNetwork(): Boolean {
        val caps = cm.activeNetwork?.let { cm.getNetworkCapabilities(it) }
        return caps != null &&
            caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    }
}
