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.billing.CouplePremiumChecker
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.GameType
import app.closer.domain.model.QuestionCategory
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.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

data class SpinWheelUiState(
    val isLoading: Boolean = false,
    val categoryName: String = "",
    val isSpinning: Boolean = false,
    val spunAndReady: Boolean = false,
    val hasPremium: Boolean = false,
    val isPaired: Boolean = true,
    val error: String? = null,
    val navigateTo: String? = null
)

@HiltViewModel
class SpinWheelViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val repository: QuestionRepository,
    private val sessionStore: LocalWheelSessionStore,
    private val gameSessionManager: GameSessionManager,
    private val premiumChecker: CouplePremiumChecker
) : ViewModel() {

    private val categoryId: String = savedStateHandle["categoryId"] ?: ""
    private val isRandomMode: Boolean get() = categoryId.isEmpty()

    private val _uiState = MutableStateFlow(SpinWheelUiState(isLoading = categoryId.isNotEmpty()))
    val uiState: StateFlow<SpinWheelUiState> = _uiState.asStateFlow()

    init {
        if (!isRandomMode) loadCategory()
        loadPremiumStatus()
        loadPairedStatus()
        checkActiveSession()
    }

    private fun loadPremiumStatus() {
        viewModelScope.launch {
            val hasPremium = runCatching { premiumChecker.isPremium().first() }.getOrDefault(false)
            _uiState.update { it.copy(hasPremium = hasPremium) }
        }
    }

    private fun loadPairedStatus() {
        viewModelScope.launch {
            val uid = gameSessionManager.currentUserId
            val paired = uid != null &&
                runCatching { gameSessionManager.getCoupleForUser(uid) }.getOrNull() != null
            // Games are couple activities — an unpaired user is sent to invite their partner.
            if (!paired) {
                _uiState.update { it.copy(isPaired = false, navigateTo = AppRoute.CREATE_INVITE) }
                return@launch
            }
            _uiState.update { it.copy(isPaired = true) }
        }
    }

    private fun loadCategory() {
        viewModelScope.launch {
            val category = runCatching { repository.getCategoryById(categoryId) }
                .onFailure { Log.w(TAG, "Could not load wheel category", it) }
                .getOrNull()
            _uiState.update {
                it.copy(isLoading = false, categoryName = category?.displayName ?: categoryId)
            }
        }
    }

    private fun checkActiveSession() {
        viewModelScope.launch {
            val uid = gameSessionManager.currentUserId ?: return@launch
            val couple = runCatching { gameSessionManager.getCoupleForUser(uid) }.getOrNull() ?: return@launch
            val active = runCatching { gameSessionManager.getActiveSession(couple.id) }.getOrNull() ?: return@launch
            val target = if (active.gameType == GameType.WHEEL) {
                AppRoute.wheelSession(active.id)
            } else {
                AppRoute.WAITING_FOR_PARTNER
            }
            _uiState.update { it.copy(navigateTo = target) }
        }
    }

    fun spin() {
        if (_uiState.value.isSpinning) return
        viewModelScope.launch {
            _uiState.update { it.copy(isSpinning = true, spunAndReady = false, error = null) }

            val loadDeferred = async {
                runCatching { loadSpinData() }.getOrNull()
            }

            // Hold the spinning state for the full animation duration regardless of load speed
            delay(SPIN_DURATION_MS)

            val result = loadDeferred.await()
            if (result == null || result.second.isEmpty()) {
                _uiState.update {
                    it.copy(isSpinning = false, error = "No questions found. Try another spin.")
                }
                return@launch
            }

            val (category, questions) = result
            sessionStore.activeSession = LocalWheelSession(
                categoryId = category.id,
                categoryName = category.displayName,
                questions = questions
            )
            _uiState.update {
                it.copy(
                    isSpinning = false,
                    spunAndReady = true,
                    categoryName = category.displayName
                )
            }
        }
    }

    private suspend fun loadSpinData(): Pair<QuestionCategory, List<app.closer.domain.model.Question>> {
        val effectiveCategoryId = if (isRandomMode) {
            val categories = repository.getCategories()
            if (categories.isEmpty()) error("No categories available")
            categories.random().id
        } else {
            categoryId
        }
        val category = repository.getCategoryById(effectiveCategoryId)
            ?: QuestionCategory(effectiveCategoryId, effectiveCategoryId, "", "free", "")
        val questions = repository.getQuestionsByCategory(effectiveCategoryId)
            .shuffled()
            .take(QUESTION_COUNT)
        return category to questions
    }

    fun startSession() {
        viewModelScope.launch {
            val userId = gameSessionManager.currentUserId
            if (userId == null) {
                _uiState.update { it.copy(error = "Not logged in") }
                return@launch
            }

            val couple = runCatching {
                gameSessionManager.getCoupleForUser(userId)
            }.getOrNull()

            if (couple == null) {
                _uiState.update { it.copy(navigateTo = AppRoute.CREATE_INVITE) }
                return@launch
            }

            val hasActive = runCatching {
                gameSessionManager.hasActiveSession(couple.id)
            }.getOrNull() ?: false

            if (hasActive) {
                _uiState.update { it.copy(navigateTo = AppRoute.WAITING_FOR_PARTNER) }
                return@launch
            }

            val effectiveCategoryId = sessionStore.activeSession?.categoryId ?: categoryId
            val questionIds = sessionStore.activeSession?.questions?.map { it.id }
            val startResult = runCatching {
                gameSessionManager.startGame(
                    userId = userId,
                    gameType = GameType.WHEEL,
                    categoryId = effectiveCategoryId,
                    questionIds = questionIds
                )
            }.getOrElse { Result.failure(it) }

            when {
                startResult.isSuccess -> {
                    val sessionId = startResult.getOrThrow()
                    _uiState.update { it.copy(navigateTo = AppRoute.wheelSession(sessionId)) }
                }
                startResult.exceptionOrNull()?.message?.startsWith("partner_active_session|") == true -> {
                    _uiState.update { it.copy(navigateTo = AppRoute.WAITING_FOR_PARTNER) }
                }
                else -> {
                    Log.w(TAG, "Could not start wheel session", startResult.exceptionOrNull())
                    _uiState.update { it.copy(error = "Could not start session") }
                }
            }
        }
    }

    fun onChooseCategory() {
        val target = if (_uiState.value.hasPremium) AppRoute.CATEGORY_PICKER else AppRoute.PAYWALL
        _uiState.update { it.copy(navigateTo = target) }
    }

    fun onPaywall() {
        _uiState.update { it.copy(navigateTo = AppRoute.PAYWALL) }
    }

    fun onHistory() {
        _uiState.update { it.copy(navigateTo = AppRoute.WHEEL_HISTORY) }
    }

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

    companion object {
        private const val TAG = "SpinWheelViewModel"
        const val SPIN_DURATION_MS = 3500L
        private const val QUESTION_COUNT = 10
    }
}
