package app.closer.data.repository

import app.closer.core.crash.CrashReporter
import app.closer.data.remote.FirestoreCollections
import app.closer.domain.model.GameType
import app.closer.domain.model.QuestionSession
import app.closer.domain.repository.QuestionSessionRepository
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 QuestionSessionRepositoryImpl @Inject constructor(
    private val firestore: FirebaseFirestore,
    private val crashReporter: CrashReporter
) : QuestionSessionRepository {

    override suspend fun saveSession(session: QuestionSession): Result<String> = runCatching {
        val doc = if (session.id.isBlank()) {
            firestore.collection(FirestoreCollections.COUPLES)
                .document(session.coupleId)
                .collection(FirestoreCollections.Couples.SESSIONS)
                .document()
        } else {
            firestore.collection(FirestoreCollections.COUPLES)
                .document(session.coupleId)
                .collection(FirestoreCollections.Couples.SESSIONS)
                .document(session.id)
        }

        val data = mapOf(
            "id" to doc.id,
            "coupleId" to session.coupleId,
            "categoryId" to session.categoryId,
            "questionIds" to session.questionIds,
            "startedByUserId" to session.startedByUserId,
            "startedAt" to session.startedAt,
            "completedAt" to session.completedAt,
            "partnerCompletedAt" to session.partnerCompletedAt,
            "isPremium" to session.isPremium,
            "status" to session.status,
            "gameType" to session.gameType,
            "completedByUsers" to session.completedByUsers
        )
        doc.set(data).await()
        // Return the (possibly auto-generated) document id so callers can observe the
        // freshly-created session. Previously this returned Unit, so a started game carried
        // an empty id → observeAnswers built an invalid 3-segment path → crash on game start.
        doc.id
    }

    override suspend fun getSessionsForCouple(coupleId: String): Result<List<QuestionSession>> =
        runCatching {
            firestore.collection(FirestoreCollections.COUPLES)
                .document(coupleId)
                .collection(FirestoreCollections.Couples.SESSIONS)
                .orderBy("completedAt", com.google.firebase.firestore.Query.Direction.DESCENDING)
                .limit(50)
                .get()
                .await()
                .documents
                .mapNotNull { doc ->
                    runCatching {
                        QuestionSession(
                            id = doc.getString("id") ?: doc.id,
                            coupleId = doc.getString("coupleId") ?: coupleId,
                            categoryId = doc.getString("categoryId") ?: "",
                            questionIds = (doc.get("questionIds") as? List<*>)
                                ?.filterIsInstance<String>() ?: emptyList(),
                            startedByUserId = doc.getString("startedByUserId") ?: "",
                            startedAt = doc.getLong("startedAt") ?: 0L,
                            completedAt = doc.getLong("completedAt"),
                            partnerCompletedAt = doc.getLong("partnerCompletedAt"),
                            isPremium = doc.getBoolean("isPremium") ?: false,
                            status = doc.getString("status") ?: "completed",
                            gameType = doc.getString("gameType") ?: GameType.WHEEL
                        )
                    }
                    .onFailure { crashReporter.recordException(it) }
                    .getOrNull()
                }
        }
    
    override suspend fun getActiveSessionForCouple(coupleId: String): QuestionSession? =
        runCatching {
            firestore.collection(FirestoreCollections.COUPLES)
                .document(coupleId)
                .collection(FirestoreCollections.Couples.SESSIONS)
                .whereEqualTo("status", "active")
                .limit(1)
                .get()
                .await()
                .documents
                .firstOrNull()
                ?.let { doc ->
                    runCatching {
                        QuestionSession(
                            id = doc.getString("id") ?: doc.id,
                            coupleId = doc.getString("coupleId") ?: coupleId,
                            categoryId = doc.getString("categoryId") ?: "",
                            questionIds = (doc.get("questionIds") as? List<*>)
                                ?.filterIsInstance<String>() ?: emptyList(),
                            startedByUserId = doc.getString("startedByUserId") ?: "",
                            startedAt = doc.getLong("startedAt") ?: 0L,
                            completedAt = doc.getLong("completedAt"),
                            partnerCompletedAt = doc.getLong("partnerCompletedAt"),
                            isPremium = doc.getBoolean("isPremium") ?: false,
                            status = doc.getString("status") ?: "active",
                            gameType = doc.getString("gameType") ?: GameType.WHEEL
                        )
                    }
                    .onFailure { crashReporter.recordException(it) }
                    .getOrNull()
                }
        }.getOrNull()
    
    override fun observeActiveSessionForCouple(coupleId: String): Flow<QuestionSession?> =
        callbackFlow {
            val registration = firestore.collection(FirestoreCollections.COUPLES)
                .document(coupleId)
                .collection(FirestoreCollections.Couples.SESSIONS)
                .whereEqualTo("status", "active")
                .limit(1)
                .addSnapshotListener { snapshot, error ->
                    if (error != null) {
                        crashReporter.recordException(error)
                        return@addSnapshotListener
                    }
                    val session = snapshot?.documents?.firstOrNull()?.let { doc ->
                        runCatching {
                            QuestionSession(
                                id = doc.getString("id") ?: doc.id,
                                coupleId = doc.getString("coupleId") ?: coupleId,
                                categoryId = doc.getString("categoryId") ?: "",
                                questionIds = (doc.get("questionIds") as? List<*>)
                                    ?.filterIsInstance<String>() ?: emptyList(),
                                startedByUserId = doc.getString("startedByUserId") ?: "",
                                startedAt = doc.getLong("startedAt") ?: 0L,
                                completedAt = doc.getLong("completedAt"),
                                partnerCompletedAt = doc.getLong("partnerCompletedAt"),
                                isPremium = doc.getBoolean("isPremium") ?: false,
                                status = doc.getString("status") ?: "active",
                                gameType = doc.getString("gameType") ?: GameType.WHEEL
                            )
                        }
                            .onFailure { crashReporter.recordException(it) }
                            .getOrNull()
                    }
                    trySend(session)
                }
            awaitClose { registration.remove() }
        }
    
    override suspend fun hasActiveSession(coupleId: String): Boolean =
        getActiveSessionForCouple(coupleId) != null

    override suspend fun markUserComplete(
        sessionId: String,
        coupleId: String,
        userId: String
    ): Result<Unit> = runCatching {
        val docRef = firestore.collection(FirestoreCollections.COUPLES)
            .document(coupleId)
            .collection(FirestoreCollections.Couples.SESSIONS)
            .document(sessionId)

        firestore.runTransaction { tx ->
            val snap = tx.get(docRef)
            @Suppress("UNCHECKED_CAST")
            val completedBy = (snap.get("completedByUsers") as? List<String>)
                ?.toMutableList() ?: mutableListOf()
            if (userId !in completedBy) completedBy.add(userId)

            val updates = mutableMapOf<String, Any>("completedByUsers" to completedBy)
            if (completedBy.size >= 2) {
                updates["status"] = "completed"
                updates["completedAt"] = System.currentTimeMillis()
            }
            tx.update(docRef, updates)
        }.await()
    }

    override suspend fun abandonSession(coupleId: String): Result<Unit> = runCatching {
        val activeSession = getActiveSessionForCouple(coupleId) ?: return@runCatching
        saveSession(
            activeSession.copy(
                completedAt = System.currentTimeMillis(),
                status = "completed"
            )
        ).map { }.getOrThrow()
    }

    override suspend fun getSessionById(coupleId: String, sessionId: String): QuestionSession? =
        runCatching {
            firestore.collection(FirestoreCollections.COUPLES)
                .document(coupleId)
                .collection(FirestoreCollections.Couples.SESSIONS)
                .document(sessionId)
                .get()
                .await()
                .takeIf { it.exists() }
                ?.let { doc ->
                    runCatching {
                        QuestionSession(
                            id = doc.getString("id") ?: doc.id,
                            coupleId = doc.getString("coupleId") ?: coupleId,
                            categoryId = doc.getString("categoryId") ?: "",
                            questionIds = (doc.get("questionIds") as? List<*>)
                                ?.filterIsInstance<String>() ?: emptyList(),
                            startedByUserId = doc.getString("startedByUserId") ?: "",
                            startedAt = doc.getLong("startedAt") ?: 0L,
                            completedAt = doc.getLong("completedAt"),
                            isPremium = doc.getBoolean("isPremium") ?: false,
                            status = doc.getString("status") ?: "completed",
                            gameType = doc.getString("gameType") ?: GameType.WHEEL,
                            completedByUsers = (doc.get("completedByUsers") as? List<*>)
                                ?.filterIsInstance<String>() ?: emptyList()
                        )
                    }.onFailure { crashReporter.recordException(it) }.getOrNull()
                }
        }.getOrNull()
}
