package app.closer.data.remote

import app.closer.crypto.CoupleEncryptionManager
import app.closer.crypto.FieldEncryptor
import app.closer.domain.model.BucketListItem
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 couple bucket lists.
 *
 * Path layout:
 * couples/{coupleId}/bucket_list/{itemId}
 *
 * Shared list where both partners can add, complete, and delete items.
 * Categories: Adventure, Travel, Food, Learning, Romance, Intimacy, Seasonal.
 */
@Singleton
class FirestoreBucketListDataSource @Inject constructor(
    private val db: FirebaseFirestore,
    private val encryptionManager: CoupleEncryptionManager,
    private val fieldEncryptor: FieldEncryptor
) {
    private fun itemsRef(coupleId: String) =
        db.collection(FirestoreCollections.COUPLES).document(coupleId)
            .collection(FirestoreCollections.Couples.BUCKET_LIST)

    // ─── CRUD methods ────────────────────────────────────────────────────────

    suspend fun addItem(coupleId: String, item: BucketListItem): String {
        // Strict E2EE: title/description are user content and must never be stored in plaintext.
        // requireAead throws if the couple key is missing rather than falling back to plaintext.
        val aead = encryptionManager.requireAead(coupleId)
        val doc = itemsRef(coupleId).document()
        val data = mapOf(
            "title" to fieldEncryptor.encrypt(item.title, aead, coupleId),
            "description" to fieldEncryptor.encrypt(item.description, aead, coupleId),
            "category" to item.category,
            "addedBy" to item.addedBy,
            "addedAt" to item.addedAt,
            "completedBy" to item.completedBy,
            "completedAt" to item.completedAt,
            "isCompleted" to item.isCompleted
        )
        doc.set(data, SetOptions.merge()).voidAwait()
        return doc.id
    }

    suspend fun updateItem(coupleId: String, item: BucketListItem) {
        val aead = encryptionManager.requireAead(coupleId)
        val path = itemsRef(coupleId).document(item.id)
        val data = mapOf(
            "title" to fieldEncryptor.encrypt(item.title, aead, coupleId),
            "description" to fieldEncryptor.encrypt(item.description, aead, coupleId),
            "category" to item.category,
            "completedBy" to item.completedBy,
            "completedAt" to item.completedAt,
            "isCompleted" to item.isCompleted
        )
        path.set(data, SetOptions.merge()).voidAwait()
    }

    suspend fun getItem(coupleId: String, itemId: String): BucketListItem? {
        val snap = itemsRef(coupleId).document(itemId).getDoc()
        return snap.toBucketListItem(coupleId)
    }

    fun observeItems(coupleId: String): Flow<List<BucketListItem>> {
        return callbackFlow {
            val listener = itemsRef(coupleId)
                .orderBy("addedAt", com.google.firebase.firestore.Query.Direction.DESCENDING)
                .addSnapshotListener { snap, err ->
                    if (err != null || snap == null) return@addSnapshotListener
                    trySend(snap.documents.mapNotNull { it.toBucketListItem(coupleId) })
                }
            awaitClose { listener.remove() }
        }
    }

    suspend fun getItems(coupleId: String): List<BucketListItem> {
        val snap = itemsRef(coupleId).queryAwait()
        return snap.documents.mapNotNull { it.toBucketListItem(coupleId) }
    }

    suspend fun deleteItem(coupleId: String, itemId: String) {
        itemsRef(coupleId).document(itemId).delete().voidAwait()
    }

    suspend fun completeItem(coupleId: String, itemId: String, completedBy: String) {
        val path = itemsRef(coupleId).document(itemId)
        val data = mapOf(
            "completedBy" to completedBy,
            "completedAt" to System.currentTimeMillis(),
            "isCompleted" to true
        )
        path.set(data, SetOptions.merge()).voidAwait()
    }

    // ─── Category queries ────────────────────────────────────────────────────

    fun observeItemsByCategory(coupleId: String, category: String): Flow<List<BucketListItem>> {
        return callbackFlow {
            val listener = itemsRef(coupleId)
                .whereEqualTo("category", category)
                .orderBy("addedAt", com.google.firebase.firestore.Query.Direction.DESCENDING)
                .addSnapshotListener { snap, err ->
                    if (err != null || snap == null) return@addSnapshotListener
                    trySend(snap.documents.mapNotNull { it.toBucketListItem(coupleId) })
                }
            awaitClose { listener.remove() }
        }
    }

    suspend fun getItemsByCategory(coupleId: String, category: String): List<BucketListItem> {
        val snap = itemsRef(coupleId)
            .whereEqualTo("category", category)
            .orderBy("addedAt", com.google.firebase.firestore.Query.Direction.DESCENDING)
            .queryAwait()
        return snap.documents.mapNotNull { it.toBucketListItem(coupleId) }
    }

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

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

    private suspend fun com.google.firebase.firestore.Query.queryAwait() =
        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) }
        }

    // ─── Mapper ──────────────────────────────────────────────────────────────

    @Suppress("UNCHECKED_CAST")
    private fun com.google.firebase.firestore.DocumentSnapshot.toBucketListItem(coupleId: String): BucketListItem? {
        val rawTitle = getString("title") ?: return null
        val addedBy = getString("addedBy") ?: return null
        val addedAt = (get("addedAt") as? Number)?.toLong() ?: 0L
        val aead = encryptionManager.aeadFor(coupleId)

        return BucketListItem(
            id = id,
            coupleId = coupleId,
            // decryptForDisplay shows a locked placeholder if the key is missing, never ciphertext.
            title = fieldEncryptor.decryptForDisplay(rawTitle, aead, coupleId) ?: FieldEncryptor.LOCKED_PLACEHOLDER,
            description = fieldEncryptor.decryptForDisplay(getString("description"), aead, coupleId) ?: "",
            category = getString("category") ?: "",
            addedBy = addedBy,
            addedAt = addedAt,
            completedBy = getString("completedBy"),
            completedAt = (get("completedAt") as? Number)?.toLong(),
            isCompleted = getBoolean("isCompleted") ?: false
        )
    }
}
