package app.closer.data.remote

import app.closer.domain.model.DateSwipe
import app.closer.domain.model.SwipeAction
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FieldValue
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

/**
 * Firestore data source for per-couple, per-date swipe state.
 *
 * Path layout:
 * couples/{coupleId}/date_swipes/{dateId}
 *
 * Each document stores a map keyed by userId:
 * {
 *   "actions": {
 *     "userA": { "action": "love", "swipedAt": 12345 },
 *     "userB": { "action": "skip", "swipedAt": 12346 }
 *   }
 * }
 *
 * Security rules ensure only couple members can read/write, and each user can
 * only write their own action entry.
 */
@Singleton
class FirestoreDateSwipeDataSource @Inject constructor(private val db: FirebaseFirestore) {
    private fun swipesRef(coupleId: String) =
        db.collection(FirestoreCollections.COUPLES).document(coupleId)
            .collection(FirestoreCollections.Couples.DATE_SWIPES)

    suspend fun recordSwipe(coupleId: String, swipe: DateSwipe) {
        val path = swipesRef(coupleId).document(swipe.dateIdeaId)
        val entry = mapOf(
            "action" to swipe.action.toFirestoreValue(),
            "swipedAt" to swipe.swipedAt
        )
        path.set(
            mapOf("actions" to mapOf(swipe.userId to entry)),
            SetOptions.merge()
        ).voidAwait()
    }

    suspend fun getSwipe(coupleId: String, dateIdeaId: String, userId: String): DateSwipe? {
        val snap = swipesRef(coupleId).document(dateIdeaId).getDoc()
        return snap.toDateSwipe(dateIdeaId, userId)
    }

    fun observeOwnSwipes(
        coupleId: String,
        userId: String,
        once: Boolean = false
    ): Flow<List<DateSwipe>> {
        return if (once) {
            kotlinx.coroutines.flow.flow {
                val swipes = getAllSwipesForUser(coupleId, userId)
                emit(swipes)
            }
        } else {
            callbackFlow {
                val listener = swipesRef(coupleId)
                    .addSnapshotListener { snap, err ->
                        if (err != null || snap == null) return@addSnapshotListener
                        val swipes = snap.documents.mapNotNull { it.toDateSwipe(it.id, userId) }
                        trySend(swipes)
                    }
                awaitClose { listener.remove() }
            }
        }
    }

    @Suppress("UNCHECKED_CAST")
    private suspend fun getAllSwipesForUser(coupleId: String, userId: String): List<DateSwipe> {
        val snap = swipesRef(coupleId).getQuery()
        return snap.documents.mapNotNull { it.toDateSwipe(it.id, userId) }
    }

    private suspend fun com.google.firebase.firestore.CollectionReference.getQuery() =
        suspendCancellableCoroutine { cont ->
            get()
                .addOnSuccessListener { cont.resume(it) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun getAllSwipesForDate(coupleId: String, dateIdeaId: String): List<DateSwipe> {
        val snap = swipesRef(coupleId).document(dateIdeaId).getDoc()
        @Suppress("UNCHECKED_CAST")
        val actions = snap.get("actions") as? Map<String, Map<String, Any>> ?: emptyMap()
        return actions.map { (uid, data) ->
            DateSwipe(
                dateIdeaId = dateIdeaId,
                userId = uid,
                action = SwipeAction.fromFirestoreValue(data["action"] as? String ?: ""),
                swipedAt = (data["swipedAt"] as? Number)?.toLong() ?: 0L
            )
        }
    }

    // ─── Coroutine helpers ───────────────────────────────────────────────────

    private suspend fun com.google.firebase.firestore.DocumentReference.getDoc(): DocumentSnapshot =
        suspendCancellableCoroutine { cont ->
            get()
                .addOnSuccessListener { cont.resume(it) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    private suspend fun com.google.android.gms.tasks.Task<Void>.voidAwait() =
        suspendCancellableCoroutine<Unit> { cont ->
            addOnSuccessListener { cont.resume(Unit) }
            addOnFailureListener { cont.resumeWithException(it) }
        }

    // ─── Mappers ─────────────────────────────────────────────────────────────

    @Suppress("UNCHECKED_CAST")
    private fun DocumentSnapshot.toDateSwipe(dateIdeaId: String, userId: String): DateSwipe? {
        val actions = get("actions") as? Map<String, Map<String, Any>> ?: return null
        val data = actions[userId] ?: return null
        return DateSwipe(
            dateIdeaId = dateIdeaId,
            userId = userId,
            action = SwipeAction.fromFirestoreValue(data["action"] as? String ?: ""),
            swipedAt = (data["swipedAt"] as? Number)?.toLong() ?: 0L
        )
    }
}
