package app.closer.ui.thisorthat

import app.closer.domain.model.GameType
import app.closer.ui.theme.closerCardColor
import android.util.Log
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import app.closer.ui.components.CloserHeartLoader
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import app.closer.ui.components.CelebrationOverlay
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.closer.core.navigation.AppRoute
import app.closer.data.remote.FirestoreThisOrThatDataSource
import app.closer.domain.model.ChoiceOption
import app.closer.domain.model.Question
import app.closer.domain.model.SessionLength
import app.closer.domain.model.ThisOrThatAnswerConfig
import app.closer.domain.model.ThisOrThatAnswerConfigImpl
import app.closer.domain.repository.QuestionRepository
import app.closer.domain.repository.QuestionSessionRepository
import app.closer.domain.usecase.GameSessionManager
import app.closer.ui.components.BrandMessageRotator
import android.content.Context
import android.provider.Settings
import dagger.hilt.android.qualifiers.ApplicationContext
import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

// ── ViewModel ────────────────────────────────────────────────────────────────

enum class TotPhase { LOADING, PICK_MOOD, PLAYING, WAITING, REVEAL, ERROR }

enum class TotMood(
    val title: String,
    val subtitle: String,
    val categoryIds: Set<String>?
) {
    LIGHT(
        title = "Light",
        subtitle = "Playful, warm, easy picks",
        categoryIds = setOf("fun", "date_night", "gratitude", "home_life")
    ),
    EVERYDAY(
        title = "Everyday",
        subtitle = "Home, values, plans, small rhythms",
        categoryIds = setOf("communication", "home_life", "values", "future", "money")
    ),
    DEEP(
        title = "Deep",
        subtitle = "Trust, repair, commitment, closeness",
        categoryIds = setOf("emotional_intimacy", "trust", "conflict_repair", "difficult_conversations", "marriage")
    ),
    ALL(
        title = "All topics",
        subtitle = "Shuffle the whole question bank",
        categoryIds = null
    )
}

/** One prompt's outcome: what each partner picked and whether they matched. */
data class RevealCard(
    val questionText: String,
    val myText: String,
    val partnerText: String,
    val agreed: Boolean
)

data class ThisOrThatUiState(
    val phase: TotPhase = TotPhase.LOADING,
    val questions: List<Question> = emptyList(),
    val currentIndex: Int = 0,
    val pendingSelection: String? = null,
    val myAnswers: List<String> = emptyList(),
    val partnerName: String = "Your partner",
    val matchedCount: Int = 0,
    val revealCards: List<RevealCard> = emptyList(),
    val selectedLength: SessionLength = SessionLength.STANDARD,
    val error: String? = null,
    val submitFailed: Boolean = false,
    val navigateTo: String? = null
)

@HiltViewModel
class ThisOrThatViewModel @Inject constructor(
    @ApplicationContext private val context: Context,
    private val repository: QuestionRepository,
    private val gameSessionManager: GameSessionManager,
    private val dataSource: FirestoreThisOrThatDataSource
) : ViewModel() {

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

    private var userId: String? = null
    private var partnerId: String? = null
    private var coupleId: String? = null
    private var sessionId: String? = null
    private var observeJob: Job? = null

    /** True once this user's picks are written, so quitting won't cancel the shared session. */
    private var submitted = false

    init {
        load()
    }

    private fun load() {
        viewModelScope.launch {
            val uid = gameSessionManager.currentUserId
                ?: return@launch fail("You need to be signed in to play.")
            val couple = gameSessionManager.getCoupleForUser(uid) ?: run {
                _uiState.update { it.copy(navigateTo = AppRoute.CREATE_INVITE) }
                return@launch
            }

            userId = uid
            coupleId = couple.id
            partnerId = couple.userIds.firstOrNull { it != uid }
            partnerId?.let { pid ->
                runCatching { gameSessionManager.getUser(pid)?.displayName }
                    .getOrNull()
                    ?.takeIf { it.isNotBlank() }
                    ?.let { name -> _uiState.update { s -> s.copy(partnerName = name) } }
            }

            val active = runCatching { gameSessionManager.getActiveSession(couple.id) }.getOrNull()
            when {
                active != null && active.gameType == GameType.THIS_OR_THAT ->
                    joinSession(active.id, active.questionIds)
                active != null ->
                    // A different game is already in progress — respect the one-game lock.
                    _uiState.update { it.copy(navigateTo = AppRoute.WAITING_FOR_PARTNER) }
                else ->
                    _uiState.update { it.copy(phase = TotPhase.PICK_MOOD) }
            }
        }
    }

    fun setLength(len: SessionLength) {
        _uiState.update { it.copy(selectedLength = len) }
    }

    fun chooseMood(mood: TotMood) {
        val uid = userId ?: return fail("You need to be signed in to play.")
        _uiState.update { it.copy(phase = TotPhase.LOADING, error = null) }
        viewModelScope.launch { createSession(uid, mood) }
    }

    /** First partner: pick a fixed set of prompts and open the shared session. */
    private suspend fun createSession(uid: String, mood: TotMood) {
        val allQuestions = runCatching { repository.getQuestionsByType("this_or_that") }
            .onFailure { Log.w(TAG, "Failed to load this_or_that questions", it) }
            .getOrElse { emptyList() }
        val moodQuestions = mood.categoryIds
            ?.let { categoryIds -> allQuestions.filter { it.category in categoryIds } }
            ?: allQuestions
        val count = _uiState.value.selectedLength.count
        val pool = moodQuestions.takeIf { it.size >= count } ?: allQuestions
        val picked = pool
            .shuffled()
            .take(count)
        if (picked.isEmpty()) return fail("No questions available.")

        val startResult = runCatching {
            gameSessionManager.startGame(
                userId = uid,
                gameType = GameType.THIS_OR_THAT,
                questionIds = picked.map { it.id }
            )
        }.getOrElse { Result.failure(it) }

        when {
            startResult.isSuccess -> {
                sessionId = startResult.getOrThrow()
                _uiState.update { it.copy(phase = TotPhase.PLAYING, questions = picked) }
                observeReveal()
            }
            startResult.exceptionOrNull()?.message?.startsWith("partner_active_session|") == true ->
                _uiState.update { it.copy(navigateTo = AppRoute.WAITING_FOR_PARTNER) }
            else -> {
                Log.w(TAG, "Could not start session", startResult.exceptionOrNull())
                fail("Couldn't start this round. Go back and try again.")
            }
        }
    }

    /** Second partner: join the in-flight session with the exact same prompts, in the same order. */
    private suspend fun joinSession(existingSessionId: String, questionIds: List<String>) {
        sessionId = existingSessionId
        val byId = runCatching { repository.getQuestionsByType("this_or_that") }
            .getOrElse { emptyList() }
            .associateBy { it.id }
        val questions = questionIds.mapNotNull { byId[it] }
        if (questions.isEmpty()) return fail("Couldn't load this round's questions.")
        // Re-opened while waiting? Don't re-ask — if I've already submitted my picks, go
        // straight to WAITING; observeReveal upgrades to the reveal once both are in.
        val uid = userId
        val cId = coupleId
        val alreadyAnswered = uid != null && cId != null &&
            runCatching { dataSource.getAnswers(cId, existingSessionId)?.byUser?.get(uid)?.isNotEmpty() == true }
                .getOrDefault(false)
        if (alreadyAnswered) submitted = true
        _uiState.update {
            it.copy(
                phase = if (alreadyAnswered) TotPhase.WAITING else TotPhase.PLAYING,
                questions = questions
            )
        }
        observeReveal()
    }

    /** Single source of truth for WAITING/REVEAL: driven by what's in Firestore. */
    private fun observeReveal() {
        val cId = coupleId ?: return
        val sId = sessionId?.takeIf { it.isNotBlank() } ?: return
        observeJob?.cancel()
        observeJob = viewModelScope.launch {
            dataSource.observeAnswers(cId, sId).collect { answers ->
                val mine = userId?.let { answers.byUser[it] }
                val theirs = partnerId?.let { answers.byUser[it] }
                when {
                    mine != null && theirs != null -> revealResult(mine, theirs)
                    mine != null -> _uiState.update {
                        if (it.phase == TotPhase.REVEAL) it else it.copy(phase = TotPhase.WAITING)
                    }
                    // else: I still haven't answered — stay on PLAYING.
                }
            }
        }
    }

    fun select(optionId: String) {
        val s = _uiState.value
        if (s.pendingSelection != null || s.phase != TotPhase.PLAYING) return
        _uiState.update { it.copy(pendingSelection = optionId) }
        viewModelScope.launch {
            val reduceMotion = Settings.Global.getFloat(
                context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f
            ) == 0f
            delay(if (reduceMotion) 0L else ADVANCE_DELAY_MS)
            val answers = _uiState.value.myAnswers + optionId
            val next = _uiState.value.currentIndex + 1
            if (next >= _uiState.value.questions.size) {
                _uiState.update {
                    it.copy(pendingSelection = null, myAnswers = answers, phase = TotPhase.WAITING)
                }
                submitAnswers(answers)
            } else {
                _uiState.update {
                    it.copy(pendingSelection = null, currentIndex = next, myAnswers = answers)
                }
            }
        }
    }

    private suspend fun submitAnswers(answers: List<String>) {
        submitted = true
        val cId = coupleId ?: return
        val sId = sessionId?.takeIf { it.isNotBlank() } ?: return
        val uid = userId ?: return
        runCatching { dataSource.submitAnswers(cId, sId, uid, answers) }
            .onFailure { e ->
                submitted = false
                Log.w(TAG, "Could not submit answers", e)
                _uiState.update { it.copy(phase = TotPhase.ERROR, error = "Couldn't save your answers. Check your connection and try again.", submitFailed = true) }
            }
        // The observer flips WAITING → REVEAL once the partner's answers land
        // (or right away, if they finished first).
    }

    private fun revealResult(mine: List<String>, theirs: List<String>) {
        if (_uiState.value.phase == TotPhase.REVEAL) return
        val questions = _uiState.value.questions
        val cards = questions.mapIndexed { i, q ->
            val config = q.answerConfig as? ThisOrThatAnswerConfigImpl
            val myOpt = mine.getOrNull(i)
            val theirOpt = theirs.getOrNull(i)
            RevealCard(
                questionText = q.text,
                myText = optionText(config, myOpt),
                partnerText = optionText(config, theirOpt),
                agreed = myOpt != null && myOpt == theirOpt
            )
        }
        _uiState.update {
            it.copy(
                phase = TotPhase.REVEAL,
                revealCards = cards,
                matchedCount = cards.count { c -> c.agreed }
            )
        }
        // Both have answered — release the one-game lock so a new game can start.
        finishSession()
    }

    private fun optionText(config: ThisOrThatAnswerConfigImpl?, optionId: String?): String =
        when (optionId) {
            null -> "—"
            config?.config?.optionA?.id -> config.config.optionA.text
            config?.config?.optionB?.id -> config.config.optionB.text
            else -> optionId
        }

    /** Both answered — mark this user done; session auto-completes once both sides recorded. */
    private fun finishSession() {
        val cId = coupleId ?: return
        val sId = sessionId?.takeIf { it.isNotBlank() } ?: return
        viewModelScope.launch {
            gameSessionManager.markUserComplete(sId, cId)
                .onFailure { Log.d(TAG, "finishSession no-op: ${it.message}") }
        }
    }

    fun quit() {
        viewModelScope.launch {
            if (!submitted) {
                val cId = coupleId
                if (cId != null) {
                    gameSessionManager.abandonSession(cId)
                        .onFailure { Log.d(TAG, "quit-abandon no-op: ${it.message}") }
                }
            }
            _uiState.update { it.copy(navigateTo = AppRoute.PLAY) }
        }
    }

    /** Force-end the game from the waiting screen (partner never played). */
    fun abandon() {
        val cId = coupleId ?: return
        viewModelScope.launch {
            gameSessionManager.abandonSession(cId)
                .onFailure { Log.d(TAG, "abandon no-op: ${it.message}") }
            _uiState.update { it.copy(navigateTo = AppRoute.PLAY) }
        }
    }

    fun retrySubmit() {
        val answers = _uiState.value.myAnswers
        if (answers.isEmpty()) return
        _uiState.update { it.copy(phase = TotPhase.WAITING, error = null, submitFailed = false) }
        viewModelScope.launch { submitAnswers(answers) }
    }

    fun restart() {
        observeJob?.cancel()
        sessionId = null
        submitted = false
        _uiState.value = ThisOrThatUiState()
        load()
    }

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

    private fun fail(message: String) {
        _uiState.update { it.copy(phase = TotPhase.ERROR, error = message) }
    }

    companion object {
        private const val ADVANCE_DELAY_MS = 420L
        private const val TAG = "ThisOrThatViewModel"
    }
}

// ── Screen ────────────────────────────────────────────────────────────────────

@Composable
fun ThisOrThatScreen(
    onNavigate: (String) -> Unit = {},
    viewModel: ThisOrThatViewModel = hiltViewModel()
) {
    val state by viewModel.uiState.collectAsState()

    LaunchedEffect(state.navigateTo) {
        state.navigateTo?.let {
            onNavigate(it)
            viewModel.onNavigated()
        }
    }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(closerBackgroundBrush())
    ) {
        when (state.phase) {
            TotPhase.LOADING -> CloserHeartLoader(
                modifier = Modifier.align(Alignment.Center)
            )
            TotPhase.ERROR -> ErrorState(
                message = state.error ?: "Something didn't load. Go back and try again.",
                onBack = viewModel::quit,
                onRetry = if (state.submitFailed) viewModel::retrySubmit else null
            )
            TotPhase.WAITING -> WaitingForRevealScreen(
                partnerName = state.partnerName,
                onBack = viewModel::quit,
                onAbandon = viewModel::abandon
            )
            TotPhase.PICK_MOOD -> ThisOrThatMoodPicker(
                selectedLength = state.selectedLength,
                onLengthSelected = viewModel::setLength,
                onMoodSelected = viewModel::chooseMood,
                onBack = viewModel::quit
            )
            TotPhase.REVEAL -> ThisOrThatReveal(
                matched = state.matchedCount,
                total = state.revealCards.size,
                partnerName = state.partnerName,
                cards = state.revealCards,
                onPlayAgain = viewModel::restart,
                onHome = viewModel::quit
            )
            TotPhase.PLAYING -> {
                val question = state.questions.getOrNull(state.currentIndex)
                if (question == null) {
                    CloserHeartLoader(
                        modifier = Modifier.align(Alignment.Center)
                    )
                } else {
                    val config = question.answerConfig as? ThisOrThatAnswerConfigImpl
                    ThisOrThatContent(
                        question = question,
                        config = config,
                        currentIndex = state.currentIndex,
                        total = state.questions.size,
                        pendingSelection = state.pendingSelection,
                        onSelect = viewModel::select,
                        onBack = viewModel::quit
                    )
                }
            }
        }

        var totCelebrate by remember { mutableStateOf(false) }
        var totCelebrated by remember { mutableStateOf(false) }
        LaunchedEffect(state.phase) {
            if (state.phase == TotPhase.REVEAL && !totCelebrated) {
                totCelebrated = true
                totCelebrate = true
            }
        }
        val totRatio = state.revealCards.size.takeIf { it > 0 }
            ?.let { state.matchedCount.toFloat() / it } ?: 0.5f
        CelebrationOverlay(
            visible = totCelebrate,
            intensity = 0.5f + totRatio,
            onFinished = { totCelebrate = false }
        )
    }
}

@Composable
private fun ThisOrThatMoodPicker(
    selectedLength: SessionLength,
    onLengthSelected: (SessionLength) -> Unit,
    onMoodSelected: (TotMood) -> Unit,
    onBack: () -> Unit
) {
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding()
            .padding(horizontal = 20.dp, vertical = 16.dp),
        verticalArrangement = Arrangement.spacedBy(14.dp),
        contentPadding = PaddingValues(bottom = 20.dp)
    ) {
        item {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
                    Text(
                        text = "Choose a mood",
                        style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold),
                        color = MaterialTheme.colorScheme.onBackground
                    )
                    Text(
                        text = "Your partner will join the same set.",
                        style = MaterialTheme.typography.bodyMedium,
                        color = MaterialTheme.colorScheme.onSurfaceVariant
                    )
                }
                TextButton(onClick = onBack) {
                    Text("Back", color = MaterialTheme.colorScheme.onSurfaceVariant)
                }
            }
        }

        item {
            TotLengthChips(selected = selectedLength, onSelect = onLengthSelected)
        }

        items(TotMood.values().toList()) { mood ->
            Card(
                onClick = { onMoodSelected(mood) },
                modifier = Modifier.fillMaxWidth(),
                shape = RoundedCornerShape(18.dp),
                colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.9f)),
                elevation = CardDefaults.cardElevation(defaultElevation = 5.dp)
            ) {
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(18.dp),
                    horizontalArrangement = Arrangement.spacedBy(14.dp),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Surface(
                        modifier = Modifier.size(44.dp),
                        shape = CircleShape,
                        color = CloserPalette.PurpleMist
                    ) {
                        Box(contentAlignment = Alignment.Center) {
                            Text(
                                text = when (mood) {
                                    TotMood.LIGHT -> "1"
                                    TotMood.EVERYDAY -> "2"
                                    TotMood.DEEP -> "3"
                                    TotMood.ALL -> "All"
                                },
                                style = MaterialTheme.typography.titleMedium,
                                color = CloserPalette.PurpleDeep,
                                fontWeight = FontWeight.Bold
                            )
                        }
                    }
                    Column(
                        modifier = Modifier.weight(1f),
                        verticalArrangement = Arrangement.spacedBy(4.dp)
                    ) {
                        Text(
                            text = mood.title,
                            style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
                            color = MaterialTheme.colorScheme.onSurface
                        )
                        Text(
                            text = mood.subtitle,
                            style = MaterialTheme.typography.bodyMedium,
                            color = MaterialTheme.colorScheme.onSurfaceVariant,
                            maxLines = 2,
                            overflow = TextOverflow.Ellipsis
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun ThisOrThatContent(
    question: Question,
    config: ThisOrThatAnswerConfigImpl?,
    currentIndex: Int,
    total: Int,
    pendingSelection: String?,
    onSelect: (String) -> Unit,
    onBack: () -> Unit
) {
    val animatedProgress by animateFloatAsState(
        targetValue = ((currentIndex + 1).toFloat() / total.coerceAtLeast(1)).coerceIn(0f, 1f),
        animationSpec = tween(durationMillis = 260),
        label = "this_or_that_progress"
    )

    Column(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding()
            .padding(horizontal = 20.dp, vertical = 16.dp),
        verticalArrangement = Arrangement.spacedBy(14.dp)
    ) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Surface(
                shape = RoundedCornerShape(999.dp),
                color = CloserPalette.PurpleMist
            ) {
                Text(
                    text = "${currentIndex + 1} / $total",
                    modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
                    style = MaterialTheme.typography.labelMedium,
                    color = CloserPalette.PurpleDeep,
                    fontWeight = FontWeight.SemiBold
                )
            }
            TextButton(onClick = onBack) {
                Text("Quit", color = MaterialTheme.colorScheme.onSurfaceVariant)
            }
        }

        ThisOrThatProgress(progress = animatedProgress)

        Card(
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f),
            shape = RoundedCornerShape(28.dp),
            colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.9f)),
            elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
        ) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(24.dp),
                contentAlignment = Alignment.Center
            ) {
                ChoicePromptBackdrop(
                    modifier = Modifier.matchParentSize()
                )
                Column(
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.spacedBy(10.dp)
                ) {
                    Surface(
                        shape = RoundedCornerShape(999.dp),
                        color = CloserPalette.PurpleMist
                    ) {
                        Text(
                            text = "This or That",
                            modifier = Modifier.padding(horizontal = 12.dp, vertical = 5.dp),
                            style = MaterialTheme.typography.labelSmall,
                            color = CloserPalette.PurpleDeep,
                            fontWeight = FontWeight.SemiBold
                        )
                    }
                    Text(
                        text = question.text,
                        style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold),
                        color = MaterialTheme.colorScheme.onSurface,
                        textAlign = TextAlign.Center,
                        maxLines = 5,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }
        }

        if (config != null) {
            OptionCard(
                text = config.config.optionA.text,
                label = "A",
                optionId = config.config.optionA.id,
                pendingSelection = pendingSelection,
                accentColor = CloserPalette.PurpleDeep,
                onSelect = onSelect
            )

            VersusBadge(
                modifier = Modifier.align(Alignment.CenterHorizontally)
            )

            OptionCard(
                text = config.config.optionB.text,
                label = "B",
                optionId = config.config.optionB.id,
                pendingSelection = pendingSelection,
                accentColor = CloserPalette.PinkAccentDeep,
                onSelect = onSelect
            )
        } else {
            Box(
                modifier = Modifier.fillMaxWidth(),
                contentAlignment = Alignment.Center
            ) {
                TextButton(onClick = { onSelect("") }) {
                    Text("Skip question", color = MaterialTheme.colorScheme.onSurfaceVariant)
                }
            }
        }
    }
}

@Composable
private fun ThisOrThatProgress(
    progress: Float
) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(8.dp)
            .clip(RoundedCornerShape(999.dp))
            .background(CloserPalette.PinkMist)
    ) {
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .fillMaxWidth(progress.coerceIn(0f, 1f))
                .clip(RoundedCornerShape(999.dp))
                .background(CloserPalette.PurpleDeep)
        )
    }
}

@Composable
private fun ChoicePromptBackdrop(
    modifier: Modifier = Modifier
) {
    Canvas(modifier = modifier) {
        val minDimension = size.minDimension
        val radius = minDimension * 0.16f
        val left = Offset(size.width * 0.18f, size.height * 0.18f)
        val right = Offset(size.width * 0.82f, size.height * 0.82f)
        val strokeWidth = 3.dp.toPx()

        drawLine(
            color = CloserPalette.PurpleMist.copy(alpha = 0.9f),
            start = left,
            end = right,
            strokeWidth = strokeWidth
        )
        drawCircle(
            color = CloserPalette.PurpleGlow.copy(alpha = 0.58f),
            radius = radius,
            center = left
        )
        drawCircle(
            color = CloserPalette.PinkSoft.copy(alpha = 0.76f),
            radius = radius * 0.94f,
            center = right
        )
        drawCircle(
            color = CloserPalette.PurpleDeep.copy(alpha = 0.12f),
            radius = radius * 0.56f,
            center = left,
            style = Stroke(width = strokeWidth)
        )
        drawCircle(
            color = CloserPalette.PinkAccentDeep.copy(alpha = 0.1f),
            radius = radius * 0.54f,
            center = right,
            style = Stroke(width = strokeWidth)
        )
    }
}

@Composable
private fun OptionCard(
    text: String,
    label: String,
    optionId: String,
    pendingSelection: String?,
    accentColor: Color,
    onSelect: (String) -> Unit
) {
    val isSelected = pendingSelection == optionId
    val isOtherSelected = pendingSelection != null && !isSelected

    val cardScale by animateFloatAsState(
        targetValue = when {
            isSelected -> 1.02f
            isOtherSelected -> 0.98f
            else -> 1f
        },
        animationSpec = tween(durationMillis = 180),
        label = "scale_$optionId"
    )
    val background by animateColorAsState(
        targetValue = when {
            isSelected -> accentColor
            isOtherSelected -> MaterialTheme.colorScheme.surface.copy(alpha = 0.45f)
            else -> MaterialTheme.colorScheme.surface
        },
        animationSpec = tween(180),
        label = "bg_$optionId"
    )
    val contentColor by animateColorAsState(
        targetValue = when {
            isSelected -> Color.White
            isOtherSelected -> Color(0xFFCCC0D5)
            else -> accentColor
        },
        animationSpec = tween(180),
        label = "fg_$optionId"
    )

    Card(
        onClick = { if (pendingSelection == null) onSelect(optionId) },
        modifier = Modifier
            .fillMaxWidth()
            .heightIn(min = 72.dp)
            .scale(cardScale),
        shape = RoundedCornerShape(18.dp),
        colors = CardDefaults.cardColors(containerColor = background),
        elevation = CardDefaults.cardElevation(defaultElevation = if (isSelected) 10.dp else 4.dp)
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 16.dp, vertical = 18.dp),
            horizontalArrangement = Arrangement.spacedBy(12.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Surface(
                shape = RoundedCornerShape(8.dp),
                color = if (isSelected) Color.White.copy(alpha = 0.22f) else accentColor.copy(alpha = 0.12f),
                modifier = Modifier.size(32.dp)
            ) {
                Box(contentAlignment = Alignment.Center) {
                    Text(
                        text = label,
                        style = MaterialTheme.typography.labelLarge,
                        fontWeight = FontWeight.Bold,
                        color = contentColor
                    )
                }
            }
            Text(
                text = text,
                style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
                color = contentColor,
                maxLines = 2,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}

@Composable
private fun VersusBadge(
    modifier: Modifier = Modifier
) {
    val infiniteTransition = rememberInfiniteTransition(label = "versus_badge")
    val pulse by infiniteTransition.animateFloat(
        initialValue = 0.96f,
        targetValue = 1.04f,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 1100),
            repeatMode = RepeatMode.Reverse
        ),
        label = "versus_badge_pulse"
    )

    Surface(
        modifier = modifier.scale(pulse),
        shape = RoundedCornerShape(999.dp),
        color = CloserPalette.PurpleMist,
        shadowElevation = 4.dp
    ) {
        Text(
            text = "OR",
            modifier = Modifier.padding(horizontal = 18.dp, vertical = 8.dp),
            style = MaterialTheme.typography.labelLarge,
            color = CloserPalette.PurpleDeep,
            fontWeight = FontWeight.Bold
        )
    }
}

@Composable
private fun WaitingForRevealScreen(
    partnerName: String,
    onBack: () -> Unit,
    onAbandon: () -> Unit = {}
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding()
            .padding(horizontal = 28.dp, vertical = 40.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Spacer(Modifier.weight(1f))

        Surface(
            modifier = Modifier.size(104.dp),
            shape = CircleShape,
            color = CloserPalette.PinkMist,
            shadowElevation = 8.dp
        ) {
            Box(contentAlignment = Alignment.Center) {
                Text(
                    text = "✓",
                    style = MaterialTheme.typography.displaySmall,
                    color = CloserPalette.PurpleDeep
                )
            }
        }

        Text(
            text = "Your picks are in!",
            style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center
        )
        Text(
            text = "We'll reveal how you two compare as soon as $partnerName finishes.",
            style = MaterialTheme.typography.bodyLarge,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            textAlign = TextAlign.Center
        )
        Spacer(Modifier.height(4.dp))
        CloserHeartLoader(size = 48.dp)
        BrandMessageRotator(style = MaterialTheme.typography.bodySmall)

        Spacer(Modifier.weight(1f))

        OutlinedButton(
            onClick = onBack,
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = 56.dp),
            shape = RoundedCornerShape(18.dp)
        ) {
            Text("Back to Play")
        }
        TextButton(onClick = onAbandon) {
            Text(
                "End this game",
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        }
    }
}

@Composable
private fun ThisOrThatReveal(
    matched: Int,
    total: Int,
    partnerName: String,
    cards: List<RevealCard>,
    onPlayAgain: () -> Unit,
    onHome: () -> Unit
) {
    val ratio = if (total > 0) matched.toFloat() / total else 0f
    val headline = when {
        ratio >= 0.8f -> "Two peas in a pod 🫛"
        ratio >= 0.5f -> "Lots in common 💛"
        ratio > 0f -> "Opposites attract ✨"
        else -> "Total opposites 😄"
    }

    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding(),
        contentPadding = PaddingValues(horizontal = 20.dp, vertical = 24.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        item {
            Column(
                modifier = Modifier.fillMaxWidth(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(10.dp)
            ) {
                MatchScoreBadge(matched = matched, total = total)
                Text(
                    text = headline,
                    style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold),
                    color = MaterialTheme.colorScheme.onSurface,
                    textAlign = TextAlign.Center
                )
                Text(
                    text = "You and $partnerName matched on $matched of $total.",
                    style = MaterialTheme.typography.bodyMedium,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    textAlign = TextAlign.Center
                )
                Spacer(Modifier.height(4.dp))
            }
        }

        items(cards) { card ->
            RevealRow(card = card, partnerName = partnerName)
        }

        item {
            Spacer(Modifier.height(8.dp))
            Button(
                onClick = onPlayAgain,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = 56.dp),
                shape = RoundedCornerShape(18.dp),
                colors = ButtonDefaults.buttonColors(containerColor = CloserPalette.PurpleDeep)
            ) {
                Text("Play again", color = Color.White)
            }
            Spacer(Modifier.height(10.dp))
            OutlinedButton(
                onClick = onHome,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = 56.dp),
                shape = RoundedCornerShape(18.dp)
            ) {
                Text("Back to Play")
            }
        }
    }
}

@Composable
private fun MatchScoreBadge(matched: Int, total: Int) {
    Surface(
        modifier = Modifier.size(116.dp),
        shape = CircleShape,
        color = CloserPalette.PurpleMist,
        shadowElevation = 8.dp
    ) {
        Box(contentAlignment = Alignment.Center) {
            Column(horizontalAlignment = Alignment.CenterHorizontally) {
                Text(
                    text = "$matched/$total",
                    style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.Bold),
                    color = CloserPalette.PurpleDeep
                )
                Text(
                    text = "in sync",
                    style = MaterialTheme.typography.labelMedium,
                    color = CloserPalette.PurpleDeep.copy(alpha = 0.8f)
                )
            }
        }
    }
}

@Composable
private fun RevealRow(card: RevealCard, partnerName: String) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(18.dp),
        colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.9f)),
        elevation = CardDefaults.cardElevation(3.dp)
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(
                    text = card.questionText,
                    style = MaterialTheme.typography.titleSmall.copy(fontWeight = FontWeight.SemiBold),
                    color = MaterialTheme.colorScheme.onSurface,
                    modifier = Modifier.weight(1f)
                )
                Spacer(Modifier.width(8.dp))
                Surface(
                    shape = RoundedCornerShape(999.dp),
                    color = if (card.agreed) CloserPalette.Evergreen.copy(alpha = 0.15f)
                    else CloserPalette.PinkMist
                ) {
                    Text(
                        text = if (card.agreed) "Match" else "Differ",
                        modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp),
                        style = MaterialTheme.typography.labelSmall,
                        color = if (card.agreed) CloserPalette.Evergreen else CloserPalette.PinkAccentDeep,
                        fontWeight = FontWeight.SemiBold
                    )
                }
            }
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.spacedBy(10.dp)
            ) {
                PickChip(
                    label = "You",
                    text = card.myText,
                    accent = CloserPalette.PurpleDeep,
                    modifier = Modifier.weight(1f)
                )
                PickChip(
                    label = partnerName,
                    text = card.partnerText,
                    accent = CloserPalette.PinkAccentDeep,
                    modifier = Modifier.weight(1f)
                )
            }
        }
    }
}

@Composable
private fun PickChip(
    label: String,
    text: String,
    accent: Color,
    modifier: Modifier = Modifier
) {
    Surface(
        modifier = modifier,
        shape = RoundedCornerShape(12.dp),
        color = accent.copy(alpha = 0.08f)
    ) {
        Column(
            modifier = Modifier.padding(10.dp),
            verticalArrangement = Arrangement.spacedBy(2.dp)
        ) {
            Text(
                text = label,
                style = MaterialTheme.typography.labelSmall,
                color = accent,
                fontWeight = FontWeight.SemiBold,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
            Text(
                text = text,
                style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Medium),
                color = MaterialTheme.colorScheme.onSurface,
                maxLines = 2,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}

@Composable
private fun ErrorState(message: String, onBack: () -> Unit, onRetry: (() -> Unit)? = null) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .padding(28.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = message,
            style = MaterialTheme.typography.bodyLarge,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            textAlign = TextAlign.Center
        )
        Spacer(Modifier.height(20.dp))
        if (onRetry != null) {
            Button(onClick = onRetry) { Text("Try again") }
            Spacer(Modifier.height(8.dp))
        }
        OutlinedButton(onClick = onBack) {
            Text("Back")
        }
    }
}

// ── Length picker ─────────────────────────────────────────────────────────────

@Composable
private fun TotLengthChips(selected: SessionLength, onSelect: (SessionLength) -> Unit) {
    Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
        SessionLength.values().forEach { len ->
            Surface(
                onClick = { onSelect(len) },
                modifier = Modifier.weight(1f),
                shape = RoundedCornerShape(12.dp),
                color = if (len == selected) CloserPalette.PurpleDeep else Color.Transparent,
                border = if (len != selected)
                    BorderStroke(1.dp, CloserPalette.PurpleDeep.copy(alpha = 0.4f))
                else null
            ) {
                Text(
                    text = len.label,
                    modifier = Modifier.padding(vertical = 10.dp),
                    style = MaterialTheme.typography.labelMedium,
                    color = if (len == selected) Color.White else CloserPalette.PurpleDeep,
                    textAlign = TextAlign.Center
                )
            }
        }
    }
}

// ── History Replay ────────────────────────────────────────────────────────────

sealed interface TotReplayPhase {
    object Loading : TotReplayPhase
    data class Ready(val matched: Int, val total: Int, val cards: List<RevealCard>) : TotReplayPhase
    data class Error(val message: String) : TotReplayPhase
}

data class TotReplayUiState(
    val phase: TotReplayPhase = TotReplayPhase.Loading,
    val partnerName: String = "Your partner"
)

@HiltViewModel
class ThisOrThatReplayViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val gameSessionManager: GameSessionManager,
    private val answerDataSource: FirestoreThisOrThatDataSource,
    private val questionRepository: QuestionRepository,
    private val sessionRepository: QuestionSessionRepository
) : ViewModel() {

    private val sessionId: String = savedStateHandle["sessionId"] ?: ""
    private val _uiState = MutableStateFlow(TotReplayUiState())
    val uiState: StateFlow<TotReplayUiState> = _uiState.asStateFlow()

    init {
        viewModelScope.launch {
            val uid = gameSessionManager.currentUserId ?: run {
                _uiState.update { it.copy(phase = TotReplayPhase.Error("Not signed in")) }
                return@launch
            }
            val couple = gameSessionManager.getCoupleForUser(uid) ?: run {
                _uiState.update { it.copy(phase = TotReplayPhase.Error("No couple found")) }
                return@launch
            }
            val partnerId = couple.userIds.firstOrNull { it != uid }
            val partnerName = partnerId
                ?.let { runCatching { gameSessionManager.getUser(it) }.getOrNull() }
                ?.displayName ?: "Your partner"
            _uiState.update { it.copy(partnerName = partnerName) }

            val session = sessionRepository.getSessionById(couple.id, sessionId) ?: run {
                _uiState.update { it.copy(phase = TotReplayPhase.Error("Session not found")) }
                return@launch
            }
            val answers = answerDataSource.getAnswers(couple.id, sessionId) ?: run {
                _uiState.update { it.copy(phase = TotReplayPhase.Error("Answers not found")) }
                return@launch
            }
            val questions = session.questionIds.mapNotNull { questionRepository.getQuestionById(it) }
            val mine = answers.byUser[uid].orEmpty()
            val theirs = partnerId?.let { answers.byUser[it] }.orEmpty()
            val cards = questions.mapIndexed { i, q ->
                val config = q.answerConfig as? ThisOrThatAnswerConfigImpl
                val myOpt = mine.getOrNull(i)
                val theirOpt = theirs.getOrNull(i)
                RevealCard(
                    questionText = q.text,
                    myText = replayOptionText(config, myOpt),
                    partnerText = replayOptionText(config, theirOpt),
                    agreed = myOpt != null && myOpt == theirOpt
                )
            }
            val matched = cards.count { it.agreed }
            _uiState.update { it.copy(phase = TotReplayPhase.Ready(matched, cards.size, cards)) }
        }
    }

    private fun replayOptionText(config: ThisOrThatAnswerConfigImpl?, optionId: String?): String =
        when (optionId) {
            null -> "—"
            config?.config?.optionA?.id -> config.config.optionA.text
            config?.config?.optionB?.id -> config.config.optionB.text
            else -> optionId
        }
}

@Composable
fun ThisOrThatReplayScreen(
    onNavigate: (String) -> Unit = {},
    viewModel: ThisOrThatReplayViewModel = hiltViewModel()
) {
    val state by viewModel.uiState.collectAsState()
    val phase = state.phase
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(closerBackgroundBrush())
    ) {
        when (phase) {
            is TotReplayPhase.Loading -> CloserHeartLoader(
                modifier = Modifier.align(Alignment.Center)
            )
            is TotReplayPhase.Error -> Column(
                modifier = Modifier
                    .align(Alignment.Center)
                    .padding(24.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                Text(phase.message, textAlign = TextAlign.Center)
                OutlinedButton(onClick = { onNavigate(AppRoute.GAME_HISTORY) }) { Text("Back") }
            }
            is TotReplayPhase.Ready -> ThisOrThatReveal(
                matched = phase.matched,
                total = phase.total,
                partnerName = state.partnerName,
                cards = phase.cards,
                onPlayAgain = { onNavigate(AppRoute.THIS_OR_THAT) },
                onHome = { onNavigate(AppRoute.PLAY) }
            )
        }
    }
}

// ── Preview ───────────────────────────────────────────────────────────────────

@Preview
@Composable
private fun ThisOrThatContentPreview() {
    val config = ThisOrThatAnswerConfigImpl(
        config = ThisOrThatAnswerConfig(
            optionA = ChoiceOption(id = "game_night", text = "Game night"),
            optionB = ChoiceOption(id = "movie_night", text = "Movie night")
        )
    )
    val question = Question(
        id = "1",
        text = "Game night or movie night?",
        category = "fun",
        answerConfig = config
    )
    Box(Modifier.background(Color(0xFFFFFBFE))) {
        ThisOrThatContent(
            question = question,
            config = config,
            currentIndex = 2,
            total = 10,
            pendingSelection = null,
            onSelect = {},
            onBack = {}
        )
    }
}
