package app.closer.data.remote

import app.closer.core.notifications.TokenRegistrar.DeviceMetadata
import app.closer.domain.model.User
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.SetOptions
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.suspendCancellableCoroutine
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

@Singleton
class FirestoreUserDataSource @Inject constructor(private val db: FirebaseFirestore) {
    private fun userRef(uid: String) = db.collection(FirestoreCollections.USERS).document(uid)

    private fun snapshotToUser(id: String, data: com.google.firebase.firestore.DocumentSnapshot): User =
        User(
            id = id,
            email = data.getString("email") ?: "",
            displayName = data.getString("displayName") ?: "",
            photoUrl = data.getString("photoUrl") ?: "",
            sex = data.getString("sex") ?: "",
            partnerId = data.getString("partnerId"),
            coupleId = data.getString("coupleId"),
            plan = data.getString("plan") ?: "free",
            createdAt = data.getLong("createdAt") ?: 0L,
            lastActiveAt = data.getLong("lastActiveAt") ?: 0L
        )

    suspend fun getUser(uid: String): User? =
        suspendCancellableCoroutine { cont ->
            userRef(uid).get()
                .addOnSuccessListener { snap ->
                    if (!snap.exists()) { cont.resume(null); return@addOnSuccessListener }
                    cont.resume(snapshotToUser(snap.id, snap))
                }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    fun observeUser(uid: String): Flow<User?> = callbackFlow {
        val listener = userRef(uid).addSnapshotListener { snap, error ->
            if (error != null) {
                close(error)
                return@addSnapshotListener
            }
            trySend(snap?.takeIf { it.exists() }?.let { snapshotToUser(it.id, it) })
        }
        awaitClose { listener.remove() }
    }

    suspend fun createUser(user: User): Unit =
        suspendCancellableCoroutine { cont ->
            userRef(user.id).set(
                mapOf(
                    "email" to user.email,
                    "displayName" to user.displayName,
                    "photoUrl" to user.photoUrl,
                    "sex" to user.sex,
                    "partnerId" to user.partnerId,
                    "coupleId" to user.coupleId,
                    "plan" to user.plan,
                    "createdAt" to user.createdAt,
                    "lastActiveAt" to user.lastActiveAt
                )
            )
                .addOnSuccessListener { cont.resume(Unit) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun updateDisplayName(uid: String, displayName: String): Unit =
        suspendCancellableCoroutine { cont ->
            userRef(uid).set(
                mapOf("displayName" to displayName, "lastActiveAt" to System.currentTimeMillis()),
                SetOptions.merge()
            )
                .addOnSuccessListener { cont.resume(Unit) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun updatePhotoUrl(uid: String, photoUrl: String): Unit =
        suspendCancellableCoroutine { cont ->
            userRef(uid).set(
                mapOf("photoUrl" to photoUrl, "lastActiveAt" to System.currentTimeMillis()),
                SetOptions.merge()
            )
                .addOnSuccessListener { cont.resume(Unit) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun updateSex(uid: String, sex: String): Unit =
        suspendCancellableCoroutine { cont ->
            userRef(uid).set(
                mapOf("sex" to sex, "lastActiveAt" to System.currentTimeMillis()),
                SetOptions.merge()
            )
                .addOnSuccessListener { cont.resume(Unit) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun hasProfile(uid: String): Boolean =
        suspendCancellableCoroutine { cont ->
            userRef(uid).get()
                .addOnSuccessListener { snap ->
                    val name = snap.getString("displayName") ?: ""
                    cont.resume(snap.exists() && name.isNotBlank())
                }
                .addOnFailureListener { cont.resume(false) }
        }

    suspend fun storeFcmToken(uid: String, token: String): Unit =
        suspendCancellableCoroutine { cont ->
            userRef(uid).set(
                mapOf("fcmToken" to token, "lastActiveAt" to System.currentTimeMillis()),
                SetOptions.merge()
            )
                .addOnSuccessListener { cont.resume(Unit) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun storeTokenMetadata(
        uid: String,
        token: String,
        metadata: DeviceMetadata
    ): Unit = suspendCancellableCoroutine { cont ->
        userRef(uid).collection(FirestoreCollections.Users.FCM_TOKENS).document(token)
            .set(
                mapOf(
                    "token" to token,
                    "platform" to metadata.platform,
                    "osVersion" to metadata.osVersion,
                    "appVersion" to metadata.appVersion,
                    "sdkVersion" to metadata.sdkVersion,
                    "updatedAt" to metadata.timestamp
                )
            )
            .addOnSuccessListener { cont.resume(Unit) }
            .addOnFailureListener { cont.resumeWithException(it) }
    }

    suspend fun clearCoupleId(uid: String): Unit =
        suspendCancellableCoroutine { cont ->
            userRef(uid).update(mapOf("coupleId" to null))
                .addOnSuccessListener { cont.resume(Unit) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun updateNotificationPrefs(
        uid: String,
        partnerAnswered: Boolean,
        chatMessage: Boolean
    ): Unit = suspendCancellableCoroutine { cont ->
        userRef(uid).set(
            mapOf(
                "notifPartnerAnswered" to partnerAnswered,
                "notifChatMessage" to chatMessage
            ),
            SetOptions.merge()
        )
            .addOnSuccessListener { cont.resume(Unit) }
            .addOnFailureListener { cont.resumeWithException(it) }
    }

    suspend fun deleteUserData(uid: String): Unit =
        suspendCancellableCoroutine { cont ->
            userRef(uid).delete()
                .addOnSuccessListener { cont.resume(Unit) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }
}
