package app.closer.data.repository

import app.closer.data.remote.FirestoreDateMatchDataSource
import app.closer.data.remote.FirestoreDateSwipeDataSource
import app.closer.domain.model.DateIdea
import app.closer.domain.model.DateMatch
import app.closer.domain.model.DateSwipe
import app.closer.domain.model.SwipeAction
import app.closer.domain.repository.DateMatchRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Implementation of [DateMatchRepository].
 *
 * Date ideas are currently loaded from an in-memory seed catalog that is also
 * shipped as a Cloud Function seed. Firestore is the source of truth for
 * per-partner swipes and revealed matches.
 *
 * Swipes are E2E-encrypted (the server can't read them), so mutual-match detection
 * is done client-side: after recording a LOVE, [recordSwipe] reads everyone's
 * (decrypted) swipes for that idea and, if both partners loved it, writes the match
 * marker via [FirestoreDateMatchDataSource.createMatchIfAbsent] (idempotent). A
 * Cloud Function then fires the "It's a match!" notification on create. The result
 * still arrives on every device via [observeMatches]. [recordSwipe] resolves with a
 * null match (the UI consumes matches through [observeMatches]).
 */
@Singleton
class DateMatchRepositoryImpl @Inject constructor(
    private val swipeDataSource: FirestoreDateSwipeDataSource,
    private val matchDataSource: FirestoreDateMatchDataSource
) : DateMatchRepository {

    override suspend fun getDateIdeas(): List<DateIdea> = DateIdeaSeed.all

    override suspend fun recordSwipe(
        coupleId: String,
        userId: String,
        dateIdeaId: String,
        action: SwipeAction
    ): Result<DateMatch?> = runCatching {
        val swipe = DateSwipe(
            dateIdeaId = dateIdeaId,
            userId = userId,
            action = action,
            swipedAt = System.currentTimeMillis()
        )
        swipeDataSource.recordSwipe(coupleId, swipe)

        // Server is blind to encrypted swipes, so detect mutual love here: if both
        // partners have now loved this idea, write the (idempotent) match marker.
        // Surfaced to the UI via observeMatches; FCM fired by the notify function.
        if (action == SwipeAction.LOVE) {
            val lovers = swipeDataSource.getAllSwipesForDate(coupleId, dateIdeaId)
                .filter { it.action == SwipeAction.LOVE }
                .map { it.userId }
                .distinct()
            if (lovers.size >= 2) {
                runCatching { matchDataSource.createMatchIfAbsent(coupleId, dateIdeaId, lovers.sorted()) }
            }
        }
        null
    }

    override suspend fun getOwnSwipe(coupleId: String, userId: String, dateIdeaId: String): DateSwipe? {
        return swipeDataSource.getSwipe(coupleId, dateIdeaId, userId)
    }

    override fun observeOwnSwipes(coupleId: String, userId: String): Flow<List<DateSwipe>> {
        return swipeDataSource.observeOwnSwipes(coupleId, userId)
    }

    override fun observeMatches(coupleId: String): Flow<List<DateMatch>> {
        return matchDataSource.observeMatches(coupleId)
    }

    override suspend fun getPartnerSwipes(coupleId: String, partnerId: String): List<DateSwipe> {
        return swipeDataSource.observeOwnSwipes(coupleId, partnerId, once = true).first()
    }
}