package app.closer.data.remote

import com.google.firebase.firestore.FirebaseFirestore
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Reads and writes release-key documents at
 * couples/{coupleId}/daily_question/{date}/answers/{senderUserId}/releaseKeys/{recipientUserId}.
 *
 * A release key is the sender's one-time answer key, ECIES-encrypted to the
 * recipient's public key. Writing is create-only: if a release key already exists
 * for this sender/recipient pair the write is skipped to prevent overwrite attacks.
 *
 * Firestore rules independently enforce immutability server-side.
 */
@Singleton
class FirestoreReleaseKeyDataSource @Inject constructor(
    private val db: FirebaseFirestore
) {

    /**
     * Writes the release key for [recipientUserId] under [senderUserId]'s answer doc.
     * No-ops if a release key already exists (idempotent retry safety).
     */
    suspend fun writeReleaseKey(
        coupleId: String,
        date: String,
        senderUserId: String,
        recipientUserId: String,
        encryptedAnswerKey: String
    ) {
        val ref = releaseKeyRef(coupleId, date, senderUserId, recipientUserId)
        // The sender cannot READ this doc under the (recipient-only) read rule, so the existence
        // check must tolerate a PERMISSION_DENIED — treat "can't read" as "not there yet" and
        // proceed to create. Pre-fix, this get() threw and the whole reveal failed.
        if (runCatching { ref.get().await().exists() }.getOrDefault(false)) return
        ref.set(
            mapOf(
                "recipientUserId" to recipientUserId,
                "encryptedAnswerKey" to encryptedAnswerKey,
                "releasedAt" to System.currentTimeMillis()
            )
        ).await()
    }

    /**
     * Reads the release key that [senderUserId] wrote for [recipientUserId].
     * Returns null if the release key does not exist yet.
     */
    suspend fun readReleaseKey(
        coupleId: String,
        date: String,
        senderUserId: String,
        recipientUserId: String
    ): String? =
        releaseKeyRef(coupleId, date, senderUserId, recipientUserId)
            .get()
            .await()
            .getString("encryptedAnswerKey")

    // ── Thread release keys ───────────────────────────────────────────────────────
    // Path: couples/{coupleId}/question_threads/{threadId}/answers/{senderUserId}/releaseKeys/{recipientUserId}

    suspend fun writeReleaseKeyForThread(
        coupleId: String,
        threadId: String,
        senderUserId: String,
        recipientUserId: String,
        encryptedAnswerKey: String
    ) {
        val ref = threadReleaseKeyRef(coupleId, threadId, senderUserId, recipientUserId)
        // Sender can't read this doc (recipient-only read rule); tolerate the denied get().
        if (runCatching { ref.get().await().exists() }.getOrDefault(false)) return
        ref.set(
            mapOf(
                "recipientUserId" to recipientUserId,
                "encryptedAnswerKey" to encryptedAnswerKey,
                "releasedAt" to System.currentTimeMillis()
            )
        ).await()
    }

    suspend fun readReleaseKeyForThread(
        coupleId: String,
        threadId: String,
        senderUserId: String,
        recipientUserId: String
    ): String? =
        threadReleaseKeyRef(coupleId, threadId, senderUserId, recipientUserId)
            .get()
            .await()
            .getString("encryptedAnswerKey")

    private fun releaseKeyRef(
        coupleId: String,
        date: String,
        senderUserId: String,
        recipientUserId: String
    ) = db.collection(FirestoreCollections.COUPLES)
        .document(coupleId)
        .collection(FirestoreCollections.Couples.DAILY_QUESTION)
        .document(date)
        .collection(FirestoreCollections.DailyQuestion.ANSWERS)
        .document(senderUserId)
        .collection(FirestoreCollections.Answers.RELEASE_KEYS)
        .document(recipientUserId)

    private fun threadReleaseKeyRef(
        coupleId: String,
        threadId: String,
        senderUserId: String,
        recipientUserId: String
    ) = db.collection(FirestoreCollections.COUPLES)
        .document(coupleId)
        .collection(FirestoreCollections.Couples.QUESTION_THREADS)
        .document(threadId)
        .collection(FirestoreCollections.QuestionThreads.ANSWERS)
        .document(senderUserId)
        .collection(FirestoreCollections.QuestionThreads.RELEASE_KEYS)
        .document(recipientUserId)
}
