package app.closer.data.remote

import app.closer.domain.model.Outcome
import app.closer.domain.model.OutcomeDayKey
import app.closer.domain.model.OutcomeScores
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.functions.FirebaseFunctions
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

@Singleton
class FirestoreOutcomeDataSource @Inject constructor(
    private val db: FirebaseFirestore,
    private val functions: FirebaseFunctions
) {
    suspend fun submitOutcome(coupleId: String, dayKey: OutcomeDayKey, scores: OutcomeScores): Unit =
        suspendCancellableCoroutine { cont ->
            functions.getHttpsCallable("submitOutcomeCallable")
                .call(
                    mapOf(
                        "coupleId" to coupleId,
                        "dayKey" to dayKey,
                        "scores" to scores.toMap()
                    )
                )
                .addOnSuccessListener { cont.resume(Unit) }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun getOutcome(coupleId: String, dayKey: OutcomeDayKey): Outcome? =
        suspendCancellableCoroutine { cont ->
            outcomeRef(coupleId, dayKey)
                .get()
                .addOnSuccessListener { snap ->
                    cont.resume(if (snap.exists()) snap.toOutcome() else null)
                }
                .addOnFailureListener { cont.resumeWithException(it) }
        }

    suspend fun getOutcomes(coupleId: String): List<Outcome> {
        val snapshot = db
            .collection(FirestoreCollections.COUPLES)
            .document(coupleId)
            .collection(FirestoreCollections.OUTCOMES)
            .get()
            .await()
        return snapshot.documents.mapNotNull { it.toOutcome() }
    }

    private fun outcomeRef(coupleId: String, dayKey: OutcomeDayKey) =
        db.collection(FirestoreCollections.COUPLES)
            .document(coupleId)
            .collection(FirestoreCollections.OUTCOMES)
            .document(dayKey)

    @Suppress("UNCHECKED_CAST")
    private fun DocumentSnapshot.toOutcome(): Outcome? {
        val data = this.data ?: return null
        val dayKey = data["dayKey"] as? String ?: return null
        return Outcome(
            dayKey = dayKey,
            baseline = (data["baseline"] as? Map<*, *>)?.toOutcomeScores(),
            scores = (data["scores"] as? Map<*, *>)?.toOutcomeScores(),
            delta = (data["delta"] as? Map<*, *>)?.toOutcomeScores(),
            answeredBy = (data["answeredBy"] as? List<String>) ?: emptyList(),
            createdAt = (data["createdAt"] as? com.google.firebase.Timestamp)?.toDate()?.time,
            updatedAt = (data["updatedAt"] as? com.google.firebase.Timestamp)?.toDate()?.time
        )
    }

    @Suppress("UNCHECKED_CAST")
    private fun Map<*, *>.toOutcomeScores(): OutcomeScores? {
        val map = this as? Map<String, Int> ?: return null
        val connection = map["connection"] ?: return null
        val communication = map["communication"] ?: return null
        val intimacy = map["intimacy"] ?: return null
        val happiness = map["happiness"] ?: return null
        return runCatching { OutcomeScores(connection, communication, intimacy, happiness) }
            .getOrNull()
    }
}
