package app.closer.core.billing

import app.closer.data.remote.FirestoreCollections
import app.closer.domain.model.AuthState
import app.closer.domain.repository.AuthRepository
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import com.revenuecat.purchases.CustomerInfo
import com.revenuecat.purchases.EntitlementInfo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Server-side entitlement checker backed by Firestore.
 *
 * - Observes `users/{userId}/entitlements/premium` for real-time changes.
 * - If the server document does not exist, falls back to RevenueCat
 *   client-side [CustomerInfo] state.
 * - Treats an expiration timestamp in the past as `premium = false`.
 *
 * All writes to the entitlement document come from Cloud Functions
 * (`revenueCatWebhook` and `syncEntitlement`). The app never writes
 * premium status client-side.
 */
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
@Singleton
class FirestoreEntitlementChecker @Inject constructor(
    private val firestore: FirebaseFirestore,
    private val authRepository: AuthRepository
) : EntitlementChecker {

    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())

    @Volatile
    private var revenueCatFallback: Map<String, EntitlementInfo> = emptyMap()

    init {
        scope.launch {
            // Keep fallback state warm by listening for auth changes, but actual
            // RevenueCat updates are pushed in via onCustomerInfoUpdated.
            authRepository.authState.collect { /* listener recomposed by flow below */ }
        }
    }

    override fun isPremium(): Flow<Boolean> = authRepository.authState
        .flatMapLatest { state ->
            when (state) {
                is AuthState.Authenticated -> observePremium(state.userId)
                else -> flowOf(false)
            }
        }
        .distinctUntilChanged()

    override suspend fun hasPremium(): Boolean = isPremium().first()

    override fun onCustomerInfoUpdated(customerInfo: CustomerInfo) {
        revenueCatFallback = customerInfo.entitlements.active
    }

    /**
     * Emits `true` when:
     * - the Firestore entitlement doc says premium and (if expiresAt exists) it
     *   has not yet expired, OR
     * - the Firestore doc does not exist AND RevenueCat's client-side SDK reports
     *   an active premium entitlement.
     */
    private fun observePremium(userId: String): Flow<Boolean> = callbackFlow {
        val listener = entitlementsRef(userId)
            .addSnapshotListener { snapshot, error ->
                if (error != null) {
                    // Fail closed: on Firestore error assume not premium.
                    trySend(false)
                    return@addSnapshotListener
                }

                val premium = when {
                    snapshot == null || !snapshot.exists() -> revenueCatPremium()
                    else -> readPremiumFromSnapshot(snapshot)
                }
                trySend(premium)
            }

        awaitClose { listener.remove() }
    }

    private fun readPremiumFromSnapshot(snapshot: DocumentSnapshot): Boolean {
        val premium = snapshot.getBoolean("premium") ?: false
        if (!premium) return false

        val expiresAt = snapshot.getTimestamp("expiresAt")
        if (expiresAt != null) {
            val nowSeconds = System.currentTimeMillis() / 1000
            if (expiresAt.seconds <= nowSeconds) {
                return false
            }
        }
        return true
    }

    private fun revenueCatPremium(): Boolean {
        val info = revenueCatFallback[PREMIUM_ENTITLEMENT_ID] ?: return false
        return info.isActive
    }

    private fun entitlementsRef(userId: String) =
        firestore.collection(FirestoreCollections.USERS).document(userId)
            .collection(FirestoreCollections.Users.ENTITLEMENTS)
            .document(FirestoreCollections.Users.ENTITLEMENT_PREMIUM_DOC)

    companion object {
        private const val PREMIUM_ENTITLEMENT_ID = "closer_premium"
    }
}
