package app.closer.ui.questions

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.analytics.RetentionAnalytics
import app.closer.analytics.RetentionEvent
import app.closer.core.crash.CrashReporter
import app.closer.data.remote.FirestoreAnswerDataSource
import app.closer.domain.DailyModeResolver
import app.closer.domain.model.ChoiceAnswerConfigImpl
import app.closer.domain.model.LocalAnswer
import app.closer.domain.model.Question
import app.closer.domain.repository.AuthRepository
import app.closer.domain.repository.CoupleRepository
import app.closer.domain.repository.LocalAnswerRepository
import app.closer.domain.usecase.DailyQuestionResolver
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ListenerRegistration
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

data class LocalQuestionUiState(
    val isLoading: Boolean = true,
    val error: String? = null,
    val question: Question? = null,
    val coupleId: String? = null,
    val dailyQuestionDate: String? = null,
    val submitted: Boolean = false,
    val isRevealed: Boolean = false,
    val pendingWrittenText: String = "",
    val pendingSelectedOptionIds: List<String> = emptyList(),
    val pendingScaleValue: Int = 3,
    val partnerHasAnswered: Boolean = false,
    val dailyMode: DailyModeResolver.DailyMode? = null
)

@HiltViewModel
class DailyQuestionViewModel @Inject constructor(
    private val localAnswerRepository: LocalAnswerRepository,
    private val firestoreAnswerDataSource: FirestoreAnswerDataSource,
    private val authRepository: AuthRepository,
    private val coupleRepository: CoupleRepository,
    private val crashReporter: CrashReporter,
    private val dailyQuestionResolver: DailyQuestionResolver,
    private val retentionAnalytics: RetentionAnalytics,
    private val db: FirebaseFirestore
) : ViewModel() {

    private val _uiState = MutableStateFlow(LocalQuestionUiState())
    val uiState: StateFlow<LocalQuestionUiState> = _uiState.asStateFlow()

    private var partnerAnswerListener: ListenerRegistration? = null

    override fun onCleared() {
        super.onCleared()
        partnerAnswerListener?.remove()
        partnerAnswerListener = null
    }

    init {
        loadDailyQuestion()
    }

    fun loadDailyQuestion() {
        viewModelScope.launch {
            _uiState.value = LocalQuestionUiState(isLoading = true)
            try {
                val resolved = dailyQuestionResolver.resolve()
                val resolvedMode = resolved.mode
                val today = resolved.date
                val coupleId = resolved.coupleId
                val question = resolved.question
                val answer = question?.let { localAnswerRepository.getAnswer(it.id) }
                val partnerHasAnswered = coupleId?.let {
                    runCatching { checkPartnerAnswered(it, today) }.getOrDefault(false)
                } ?: false
                _uiState.value = LocalQuestionUiState(
                    isLoading = false,
                    question = question,
                    coupleId = coupleId,
                    dailyQuestionDate = today,
                    pendingScaleValue = defaultScaleValue(question),
                    partnerHasAnswered = partnerHasAnswered,
                    dailyMode = resolvedMode
                ).withLocalAnswer(answer)

                if (coupleId != null) startPartnerAnswerObserver(coupleId, today)
                question?.let { observeLocalAnswerRevealed(it.id) }

                retentionAnalytics.track(RetentionEvent.DailyQuestionViewed(categoryId = question?.category))
                retentionAnalytics.track(RetentionEvent.DailyModeResolved(categoryId = resolvedMode.id))
            } catch (e: Exception) {
                crashReporter.recordException(e)
                _uiState.value = LocalQuestionUiState(
                    isLoading = false,
                    error = e.message ?: "Couldn't open today's question."
                )
            }
        }
    }

    private fun startPartnerAnswerObserver(coupleId: String, date: String) {
        viewModelScope.launch {
            val userId = authRepository.currentUserId ?: return@launch
            val couple = runCatching { coupleRepository.getCoupleForUser(userId) }.getOrNull() ?: return@launch
            val partnerId = couple.userIds.firstOrNull { it != userId } ?: return@launch
            partnerAnswerListener?.remove()
            partnerAnswerListener = db.collection("couples")
                .document(coupleId)
                .collection("daily_question")
                .document(date)
                .collection("answers")
                .document(partnerId)
                .addSnapshotListener { snapshot, error ->
                    if (error != null) return@addSnapshotListener
                    _uiState.update { it.copy(partnerHasAnswered = snapshot?.exists() == true) }
                }
        }
    }

    private fun observeLocalAnswerRevealed(questionId: String) {
        viewModelScope.launch {
            localAnswerRepository.observeAnswer(questionId).collect { answer ->
                _uiState.update { it.copy(isRevealed = answer?.isRevealed == true) }
            }
        }
    }

    fun updateWrittenText(text: String) {
        _uiState.update { it.copy(pendingWrittenText = text, submitted = false) }
    }

    fun toggleOption(optionId: String) {
        _uiState.update { state ->
            val question = state.question ?: return@update state
            val updated = if (question.type == "multi_choice") {
                val maxSel = (question.answerConfig as? ChoiceAnswerConfigImpl)?.config?.maxSelections ?: 0
                when {
                    optionId in state.pendingSelectedOptionIds -> state.pendingSelectedOptionIds - optionId
                    maxSel == 0 || state.pendingSelectedOptionIds.size < maxSel -> state.pendingSelectedOptionIds + optionId
                    else -> state.pendingSelectedOptionIds
                }
            } else {
                listOf(optionId)
            }
            state.copy(pendingSelectedOptionIds = updated, submitted = false)
        }
    }

    fun updateScale(value: Int) {
        _uiState.update { it.copy(pendingScaleValue = value, submitted = false) }
    }

    fun submitAnswer() {
        val state = _uiState.value
        val question = state.question ?: return
        if (!canSubmit(state)) return
        viewModelScope.launch {
            val localAnswer = state.toLocalAnswer(question).copy(updatedAt = System.currentTimeMillis())
            localAnswerRepository.saveAnswer(localAnswer)
            _uiState.update { it.copy(submitted = true) }
            retentionAnalytics.track(RetentionEvent.DailyQuestionAnswered(categoryId = question.category))
            syncAnswerToFirestore(state.coupleId, state.dailyQuestionDate, question.id, localAnswer)
        }
    }

    fun clearSubmittedState() {
        _uiState.update { it.copy(submitted = false) }
    }

    fun canSubmit(): Boolean = canSubmit(_uiState.value)

    private fun canSubmit(state: LocalQuestionUiState): Boolean {
        val question = state.question ?: return false
        return when (question.type) {
            "written" -> state.pendingWrittenText.isNotBlank()
            "single_choice", "multi_choice", "this_or_that" -> state.pendingSelectedOptionIds.isNotEmpty()
            "scale" -> true
            else -> false
        }
    }

    /**
     * Syncs the local answer to Firestore so the partner can see it. Failures are
     * non-blocking: the user's answer is already persisted locally.
     */
    private suspend fun syncAnswerToFirestore(
        coupleId: String?,
        dailyQuestionDate: String?,
        questionId: String,
        answer: LocalAnswer
    ) {
        val userId = authRepository.currentUserId ?: return
        if (coupleId.isNullOrBlank()) return
        if (dailyQuestionDate.isNullOrBlank()) return
        runCatching {
            firestoreAnswerDataSource.saveAnswer(coupleId, questionId, userId, answer, dailyQuestionDate)
            // Mirror the sealed schema fields into local storage so the reveal screen can
            // enter the sealed reveal state machine on subsequent launches.
            localAnswerRepository.saveAnswer(answer.copy(schemaVersion = 3, isSealed = true, answerDate = dailyQuestionDate))
        }.onFailure {
            crashReporter.recordException(it)
        }
        // After submitting, refresh partner-answered status so the reveal button appears
        // immediately if the partner answered while the user was composing.
        val partnerHasAnswered = runCatching { checkPartnerAnswered(coupleId, dailyQuestionDate) }.getOrDefault(false)
        _uiState.update { it.copy(partnerHasAnswered = partnerHasAnswered) }
    }

    private suspend fun checkPartnerAnswered(coupleId: String, date: String): Boolean {
        val userId = authRepository.currentUserId ?: return false
        val couple = runCatching { coupleRepository.getCoupleForUser(userId) }.getOrNull() ?: return false
        val partnerId = couple.userIds.firstOrNull { it != userId } ?: return false
        return firestoreAnswerDataSource.getAnswerForUser(coupleId, partnerId, date) != null
    }
}

fun defaultScaleValue(question: Question?): Int {
    val cfg = question?.answerConfig as? app.closer.domain.model.ScaleAnswerConfigImpl
    val min = cfg?.config?.minScale ?: 1
    val max = cfg?.config?.maxScale ?: 5
    return ((min + max) / 2).coerceIn(min, max)
}
