package app.closer.data.remote

import app.closer.crypto.CoupleEncryptionManager
import app.closer.crypto.FieldEncryptor
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.tasks.await
import org.json.JSONArray
import javax.inject.Inject
import javax.inject.Singleton

/** Both partners' yes/no picks for one Desire Sync session, keyed by userId. */
data class DesireSyncAnswers(
    val byUser: Map<String, List<String>> = emptyMap()
)

/**
 * Stores each partner's private yes/no answers for the async Desire Sync reveal at
 * `couples/{coupleId}/desire_sync/{sessionId}`. The session (the shared topic set +
 * the one-active-game lock) is owned by [app.closer.domain.usecase.GameSessionManager];
 * this only carries the answers so each partner plays privately on their own device
 * and only mutually-wanted topics surface once both have submitted.
 */
@Singleton
class FirestoreDesireSyncDataSource @Inject constructor(
    private val db: FirebaseFirestore,
    private val encryptionManager: CoupleEncryptionManager,
    private val fieldEncryptor: FieldEncryptor
) {
    private fun doc(coupleId: String, sessionId: String) =
        db.collection(FirestoreCollections.COUPLES)
            .document(coupleId)
            .collection(FirestoreCollections.Couples.DESIRE_SYNC)
            .document(sessionId)

    /** Persist this user's picks (optionIds aligned to the session's topic order). */
    suspend fun submitAnswers(
        coupleId: String,
        sessionId: String,
        userId: String,
        optionIds: List<String>
    ) {
        val aead = encryptionManager.requireAead(coupleId)
        val value = fieldEncryptor.encrypt(JSONArray(optionIds).toString(), aead, coupleId)
        doc(coupleId, sessionId)
            .set(mapOf("answers" to mapOf(userId to value)), SetOptions.merge())
            .await()
    }

    /** One-shot read — used to detect whether this user has already answered. */
    suspend fun getAnswers(coupleId: String, sessionId: String): DesireSyncAnswers? =
        runCatching {
            val snap = doc(coupleId, sessionId).get().await()
            val aead = encryptionManager.aeadFor(coupleId)
            DesireSyncAnswers(parseAnswers(snap.get("answers"), aead, coupleId))
        }.getOrNull()

    /** Live view of both partners' picks; emits whenever either side submits. */
    fun observeAnswers(coupleId: String, sessionId: String): Flow<DesireSyncAnswers> =
        callbackFlow {
            val reg = doc(coupleId, sessionId).addSnapshotListener { snap, err ->
                if (err != null || snap == null) return@addSnapshotListener
                val aead = encryptionManager.aeadFor(coupleId)
                trySend(DesireSyncAnswers(parseAnswers(snap.get("answers"), aead, coupleId)))
            }
            awaitClose { reg.remove() }
        }

    private fun parseAnswers(
        raw: Any?,
        aead: com.google.crypto.tink.Aead?,
        coupleId: String
    ): Map<String, List<String>> {
        @Suppress("UNCHECKED_CAST")
        val map = raw as? Map<String, *> ?: return emptyMap()
        return map.mapNotNull { (uid, value) ->
            val list = when (value) {
                is String -> listOf(value)
                is List<*> -> value.filterIsInstance<String>()
                else -> return@mapNotNull null
            }
            // Current format is an encrypted string; the single-item list supports v1 data.
            val decrypted = if (list.size == 1 && fieldEncryptor.isEncrypted(list[0])) {
                val json = fieldEncryptor.decrypt(list[0], aead, coupleId) ?: return@mapNotNull null
                runCatching {
                    val arr = JSONArray(json)
                    (0 until arr.length()).map { arr.getString(it) }
                }.getOrNull() ?: return@mapNotNull null
            } else list
            uid to decrypted
        }.toMap()
    }
}
