package app.closer.ui.wheel

import android.util.Log
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.core.navigation.AppRoute
import app.closer.data.remote.FirestoreWheelAnswerDataSource
import app.closer.data.remote.WheelAnswerEntry
import app.closer.data.remote.WheelQuestionRef
import app.closer.domain.model.ChoiceAnswerConfigImpl
import app.closer.domain.model.Question
import app.closer.domain.model.ScaleAnswerConfigImpl
import app.closer.domain.model.ThisOrThatAnswerConfigImpl
import app.closer.domain.repository.QuestionRepository
import app.closer.domain.usecase.GameSessionManager
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 WheelSessionUiState(
    val isLoading: Boolean = true,
    val questions: List<Question> = emptyList(),
    val currentIndex: Int = 0,
    val skippedCount: Int = 0,
    val answeredCount: Int = 0,
    val categoryName: String = "",
    val navigateTo: String? = null,
    val isEmpty: Boolean = false,
    val selectedOptionIds: List<String> = emptyList(),
    val selectedScaleValue: Int = 3,
    val writtenText: String = ""
)

@HiltViewModel
class WheelSessionViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val sessionStore: LocalWheelSessionStore,
    private val repository: QuestionRepository,
    private val gameSessionManager: GameSessionManager,
    private val answerDataSource: FirestoreWheelAnswerDataSource
) : ViewModel() {

    private val sessionId: String = savedStateHandle["sessionId"] ?: ""

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

    private var coupleId: String? = null
    private var userId: String? = null
    private val myAnswers = mutableListOf<WheelAnswerEntry>()
    private var submitting = false

    init {
        load()
    }

    private fun load() {
        viewModelScope.launch {
            val uid = gameSessionManager.currentUserId
            val couple = uid?.let { gameSessionManager.getCoupleForUser(it) }
            if (uid == null || couple == null || sessionId.isBlank()) {
                _uiState.update { it.copy(isLoading = false, isEmpty = true) }
                return@launch
            }
            userId = uid
            coupleId = couple.id

            // If I've already answered this set (e.g. re-opened while waiting), jump
            // straight to the reveal instead of letting me answer a second time.
            val existing = answerDataSource.getDoc(couple.id, sessionId)
            if (existing?.answersByUser?.get(uid)?.isNotEmpty() == true) {
                _uiState.update {
                    it.copy(isLoading = false, navigateTo = AppRoute.wheelComplete(sessionId))
                }
                return@launch
            }

            val session = gameSessionManager.getActiveSession(couple.id)
            // Load the exact spun set from its fixed id list so both partners answer the
            // identical wheel, in the same order, regardless of who spun.
            val questionIds = session?.questionIds
                ?: sessionStore.activeSession?.questions?.map { it.id }.orEmpty()
            val questions = questionIds.mapNotNull { repository.getQuestionById(it) }
            val categoryName = sessionStore.activeSession?.categoryName
                ?: session?.categoryId
                    ?.let { runCatching { repository.getCategoryById(it)?.displayName }.getOrNull() }
                ?: ""

            if (questions.isEmpty()) {
                _uiState.update { it.copy(isLoading = false, isEmpty = true) }
                return@launch
            }

            _uiState.update {
                it.copy(
                    isLoading = false,
                    questions = questions,
                    categoryName = categoryName,
                    selectedScaleValue = defaultScaleValue(questions.firstOrNull())
                )
            }
        }
    }

    private fun defaultScaleValue(question: Question?): Int {
        val config = question?.answerConfig
        if (config is ScaleAnswerConfigImpl) {
            val cfg = config.config
            val midpoint = (cfg.minScale + cfg.maxScale) / 2
            return midpoint.coerceIn(cfg.minScale..cfg.maxScale)
        }
        return 3
    }

    fun selectOption(optionId: String) {
        val question = _uiState.value.questions.getOrNull(_uiState.value.currentIndex) ?: return
        if (question.type == "multi_choice") {
            val maxSel = (question.answerConfig as? ChoiceAnswerConfigImpl)
                ?.config?.maxSelections ?: 0
            _uiState.update {
                val current = it.selectedOptionIds.toMutableList()
                if (optionId in current) {
                    current.remove(optionId)
                } else if (maxSel == 0 || current.size < maxSel) {
                    current.add(optionId)
                }
                it.copy(selectedOptionIds = current)
            }
        } else {
            _uiState.update { it.copy(selectedOptionIds = listOf(optionId)) }
        }
    }

    fun onScaleChanged(value: Int) {
        _uiState.update { it.copy(selectedScaleValue = value) }
    }

    fun onWrittenTextChanged(text: String) {
        _uiState.update { it.copy(writtenText = text) }
    }

    fun next() {
        val state = _uiState.value
        val question = state.questions.getOrNull(state.currentIndex) ?: return
        val answered = hasValidSelection(state)
        recordAnswer(question.id, if (answered) displayFor(question, state) else SKIPPED)
        advance(answered)
    }

    fun skip() {
        val state = _uiState.value
        val question = state.questions.getOrNull(state.currentIndex) ?: return
        recordAnswer(question.id, SKIPPED)
        advance(answered = false)
    }

    fun endEarly() {
        val state = _uiState.value
        val question = state.questions.getOrNull(state.currentIndex)
        if (question != null) {
            val answered = hasValidSelection(state)
            recordAnswer(question.id, if (answered) displayFor(question, state) else SKIPPED)
        }
        // Mark any prompts we never reached as skipped so both partners' answer lists
        // line up with the shared question set.
        for (i in myAnswers.size until state.questions.size) {
            recordAnswer(state.questions[i].id, SKIPPED)
        }
        submitAndFinish()
    }

    private fun advance(answered: Boolean) {
        val state = _uiState.value
        val nextIndex = state.currentIndex + 1
        if (nextIndex >= state.questions.size) {
            _uiState.update {
                it.copy(
                    answeredCount = it.answeredCount + if (answered) 1 else 0,
                    skippedCount = it.skippedCount + if (answered) 0 else 1
                )
            }
            submitAndFinish()
        } else {
            val nextQuestion = state.questions.getOrNull(nextIndex)
            _uiState.update {
                it.copy(
                    currentIndex = nextIndex,
                    answeredCount = it.answeredCount + if (answered) 1 else 0,
                    skippedCount = it.skippedCount + if (answered) 0 else 1,
                    selectedOptionIds = emptyList(),
                    selectedScaleValue = defaultScaleValue(nextQuestion),
                    writtenText = ""
                )
            }
        }
    }

    private fun recordAnswer(questionId: String, display: String) {
        myAnswers.add(WheelAnswerEntry(questionId = questionId, display = display))
    }

    private fun submitAndFinish() {
        if (submitting) return
        submitting = true
        val cId = coupleId
        val uid = userId
        if (cId == null || uid == null || sessionId.isBlank()) {
            _uiState.update { it.copy(navigateTo = AppRoute.PLAY) }
            return
        }
        val state = _uiState.value
        val questionRefs = state.questions.map { WheelQuestionRef(it.id, it.text) }
        viewModelScope.launch {
            runCatching {
                answerDataSource.submitAnswers(
                    coupleId = cId,
                    sessionId = sessionId,
                    userId = uid,
                    categoryName = state.categoryName,
                    questions = questionRefs,
                    answers = myAnswers.toList()
                )
            }.onFailure { Log.w(TAG, "Could not submit wheel answers", it) }
            _uiState.update { it.copy(navigateTo = AppRoute.wheelComplete(sessionId)) }
        }
    }

    private fun hasValidSelection(state: WheelSessionUiState): Boolean {
        val question = state.questions.getOrNull(state.currentIndex) ?: return false
        return when (question.type) {
            "written" -> state.writtenText.isNotBlank()
            "single_choice", "this_or_that", "multi_choice" -> state.selectedOptionIds.isNotEmpty()
            "scale" -> true // always has a default value
            else -> false
        }
    }

    private fun displayFor(question: Question, state: WheelSessionUiState): String =
        when (question.type) {
            "written" -> state.writtenText.trim().ifBlank { SKIPPED }
            "scale" -> state.selectedScaleValue.toString()
            "single_choice", "this_or_that" ->
                optionLabel(question, state.selectedOptionIds.firstOrNull()) ?: SKIPPED
            "multi_choice" -> {
                val labels = state.selectedOptionIds.mapNotNull { optionLabel(question, it) }
                if (labels.isEmpty()) SKIPPED else labels.joinToString(", ")
            }
            else -> SKIPPED
        }

    private fun optionLabel(question: Question, optionId: String?): String? {
        if (optionId == null) return null
        return when (val cfg = question.answerConfig) {
            is ChoiceAnswerConfigImpl -> cfg.config.options.firstOrNull { it.id == optionId }?.text
            is ThisOrThatAnswerConfigImpl ->
                listOf(cfg.config.optionA, cfg.config.optionB).firstOrNull { it.id == optionId }?.text
            else -> null
        } ?: optionId
    }

    fun onNavigated() {
        _uiState.update { it.copy(navigateTo = null) }
    }

    companion object {
        private const val SKIPPED = "Skipped"
        private const val TAG = "WheelSessionViewModel"
    }
}
