package app.closer.ui.questions

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.core.crash.CrashReporter
import app.closer.data.remote.FirestoreAnswerDataSource
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.repository.QuestionRepository
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 pendingWrittenText: String = "",
    val pendingSelectedOptionIds: List<String> = emptyList(),
    val pendingScaleValue: Int = 3
)

@HiltViewModel
class DailyQuestionViewModel @Inject constructor(
    private val repository: QuestionRepository,
    private val localAnswerRepository: LocalAnswerRepository,
    private val firestoreAnswerDataSource: FirestoreAnswerDataSource,
    private val authRepository: AuthRepository,
    private val coupleRepository: CoupleRepository,
    private val crashReporter: CrashReporter
) : ViewModel() {

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

    init {
        loadDailyQuestion()
    }

    fun loadDailyQuestion() {
        viewModelScope.launch {
            _uiState.value = LocalQuestionUiState(isLoading = true)
            try {
                val today = FirestoreAnswerDataSource.todayLocalDateString()
                val (coupleId, question) = loadCoupleAndQuestion(today)
                val answer = question?.let { localAnswerRepository.getAnswer(it.id) }
                _uiState.value = LocalQuestionUiState(
                    isLoading = false,
                    question = question,
                    coupleId = coupleId,
                    dailyQuestionDate = today,
                    pendingScaleValue = defaultScaleValue(question)
                ).withLocalAnswer(answer)
            } catch (e: Exception) {
                crashReporter.recordException(e)
                _uiState.value = LocalQuestionUiState(
                    isLoading = false,
                    error = e.message ?: "Couldn't open today's question."
                )
            }
        }
    }

    /**
     * Resolves the current couple (if any) and the daily question.
     *
     * For paired users, read the couple's assigned daily question from Firestore
     * so both partners see the same prompt. If no assignment exists yet, request
     * one from the cloud function and fall back to a local random question while
     * waiting.
     *
     * For unpaired users, fall back to the local random question pool.
     */
    private suspend fun loadCoupleAndQuestion(today: String): Pair<String?, Question?> {
        val couple = authRepository.currentUserId?.let { uid ->
            runCatching { coupleRepository.getCoupleForUser(uid) }
                .onFailure { crashReporter.recordException(it) }
                .getOrNull()
        }

        if (couple == null) {
            return null to repository.getDailyQuestion()
        }

        val coupleId = couple.id
        val assignment = runCatching {
            firestoreAnswerDataSource.getDailyQuestionAssignment(coupleId, today)
        }.onFailure { crashReporter.recordException(it) }.getOrNull()

        val question = if (assignment != null) {
            repository.getQuestionById(assignment.questionId) ?: repository.getDailyQuestion()
        } else {
            // No assignment yet. Request immediate assignment, but keep the app
            // usable with a local random question in case the call fails.
            runCatching {
                firestoreAnswerDataSource.requestDailyQuestionAssignment(coupleId, today)
                repository.getQuestionById(
                    firestoreAnswerDataSource.getDailyQuestionAssignment(coupleId, today)?.questionId ?: ""
                )
            }.onFailure { crashReporter.recordException(it) }.getOrNull()
                ?: repository.getDailyQuestion()
        }

        return coupleId to question
    }

    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) }
            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)
        }.onFailure {
            crashReporter.recordException(it)
        }
    }
}

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)
}
