package app.closer.data.remote

import app.closer.domain.model.ChallengeProgressState
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.FirebaseFirestore
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class FirestoreChallengeDataSource @Inject constructor(private val db: FirebaseFirestore) {

    private fun challengeRef(coupleId: String, challengeId: String) =
        db.collection(FirestoreCollections.COUPLES)
            .document(coupleId)
            .collection(FirestoreCollections.Couples.CHALLENGES)
            .document(challengeId)

    private fun challengesCol(coupleId: String) =
        db.collection(FirestoreCollections.COUPLES)
            .document(coupleId)
            .collection(FirestoreCollections.Couples.CHALLENGES)

    suspend fun getCompletedChallengeIds(coupleId: String): List<Pair<String, Long>> =
        challengesCol(coupleId)
            .whereEqualTo("status", "completed")
            .get()
            .await()
            .documents
            .mapNotNull { doc ->
                val id = doc.getString("challengeId") ?: return@mapNotNull null
                val startedAt = doc.getLong("startedAt") ?: 0L
                Pair(id, startedAt)
            }

    suspend fun getActiveChallengeId(coupleId: String): String? =
        challengesCol(coupleId)
            .whereEqualTo("status", "active")
            .limit(1)
            .get()
            .await()
            .documents
            .firstOrNull()
            ?.getString("challengeId")

    suspend fun startChallenge(coupleId: String, challengeId: String) {
        challengeRef(coupleId, challengeId).set(
            mapOf(
                "challengeId" to challengeId,
                "startedAt" to System.currentTimeMillis(),
                "status" to "active",
                "completions" to emptyMap<String, List<Int>>()
            )
        ).await()
    }

    suspend fun markDayComplete(coupleId: String, challengeId: String, userId: String, day: Int) {
        challengeRef(coupleId, challengeId)
            .update("completions.$userId", FieldValue.arrayUnion(day))
            .await()
    }

    suspend fun finishChallenge(coupleId: String, challengeId: String) {
        challengeRef(coupleId, challengeId).update("status", "completed").await()
    }

    suspend fun abandonChallenge(coupleId: String, challengeId: String) {
        challengeRef(coupleId, challengeId).update("status", "abandoned").await()
    }

    fun observeProgress(
        coupleId: String,
        challengeId: String,
        myUserId: String,
        partnerUserId: String
    ): Flow<ChallengeProgressState> = callbackFlow {
        val reg = challengeRef(coupleId, challengeId)
            .addSnapshotListener { snap, err ->
                if (err != null || snap == null) return@addSnapshotListener
                @Suppress("UNCHECKED_CAST")
                val completions = snap.get("completions") as? Map<String, List<Long>> ?: emptyMap()
                val mine = completions[myUserId]?.map { it.toInt() } ?: emptyList()
                val partner = completions[partnerUserId]?.map { it.toInt() } ?: emptyList()
                trySend(
                    ChallengeProgressState(
                        challengeId = challengeId,
                        startedAt = snap.getLong("startedAt") ?: 0L,
                        status = snap.getString("status") ?: "active",
                        myCompletedDays = mine,
                        partnerCompletedDays = partner
                    )
                )
            }
        awaitClose { reg.remove() }
    }
}
