package app.closer.ui.desiresync

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.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
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.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.draw.clip
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Sync
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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import app.closer.ui.components.CelebrationOverlay
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
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.data.remote.FirestoreDesireSyncDataSource
import android.content.Context
import android.provider.Settings
import app.closer.domain.model.ChoiceAnswerConfigImpl
import app.closer.domain.model.Question
import app.closer.domain.model.SessionLength
import app.closer.domain.repository.QuestionRepository
import app.closer.domain.repository.QuestionSessionRepository
import app.closer.domain.usecase.GameSessionManager
import dagger.hilt.android.qualifiers.ApplicationContext
import app.closer.ui.components.BrandMessageRotator
import app.closer.ui.components.StatusGlyph
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

// ── Domain ────────────────────────────────────────────────────────────────────

/** A topic both partners said yes to. Questions are neutral, so one shared set. */
data class DesireMatch(
    val question: Question
)

enum class DesireSyncPhase { LOADING, SETUP, INTRO, ANSWER, WAITING, REVEAL, ERROR }

data class DesireSyncUiState(
    val phase: DesireSyncPhase = DesireSyncPhase.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 matches: List<DesireMatch> = emptyList(),
    val selectedLength: SessionLength = SessionLength.STANDARD,
    val error: String? = null,
    val submitFailed: Boolean = false,
    val navigateTo: String? = null
)

private val POSITIVE_IDS = setOf("yes", "true")

private fun isBinaryQuestion(q: Question): Boolean {
    val config = q.answerConfig as? ChoiceAnswerConfigImpl ?: return false
    val ids = config.config.options.map { it.id.lowercase() }.toSet()
    return ids == setOf("yes", "no") || ids == setOf("true", "false")
}

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

@HiltViewModel
class DesireSyncViewModel @Inject constructor(
    @ApplicationContext private val context: Context,
    private val repository: QuestionRepository,
    private val gameSessionManager: GameSessionManager,
    private val dataSource: FirestoreDesireSyncDataSource,
    private val premiumChecker: CouplePremiumChecker
) : ViewModel() {

    private val _uiState = MutableStateFlow(DesireSyncUiState())
    val uiState: StateFlow<DesireSyncUiState> = _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 {
            if (!premiumChecker.hasPremium()) {
                _uiState.update { it.copy(navigateTo = AppRoute.PAYWALL) }
                return@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.DESIRE_SYNC ->
                    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 = DesireSyncPhase.SETUP) }
            }
        }
    }

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

    fun startGame() {
        val uid = userId ?: return
        _uiState.update { it.copy(phase = DesireSyncPhase.LOADING) }
        viewModelScope.launch { createSession(uid) }
    }

    /** First partner: pick the neutral question set and open the shared session. */
    private suspend fun createSession(uid: String) {
        val questions = loadNeutralQuestions().shuffled().take(_uiState.value.selectedLength.count)
        if (questions.isEmpty()) return fail("No questions available.")

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

        when {
            startResult.isSuccess -> {
                sessionId = startResult.getOrThrow()
                _uiState.update { it.copy(phase = DesireSyncPhase.INTRO, questions = questions) }
                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("Could not start the game.")
            }
        }
    }

    /** Second partner: join the in-flight session with the identical question set, same order. */
    private suspend fun joinSession(existingSessionId: String, questionIds: List<String>) {
        sessionId = existingSessionId
        val byId = loadNeutralQuestions().associateBy { it.id }
        val questions = questionIds.mapNotNull { byId[it] }
        if (questions.isEmpty()) return fail("Couldn't load this round's questions.")
        // Re-opened after answering? Skip straight to WAITING instead of re-prompting.
        val uid = userId
        val cId = coupleId
        val alreadyAnswered = uid != null && cId != null &&
            runCatching { dataSource.getAnswers(cId, existingSessionId)?.byUser?.get(uid)?.isNotEmpty() == true }
                .getOrDefault(false)
        _uiState.update {
            it.copy(
                phase = if (alreadyAnswered) DesireSyncPhase.WAITING else DesireSyncPhase.INTRO,
                questions = questions
            )
        }
        observeReveal()
    }

    /**
     * The shared, gender-neutral binary desire pool — both partners answer the same
     * questions, so any couple can play. (See [isBinaryQuestion] for the yes/no filter.)
     */
    private suspend fun loadNeutralQuestions(): List<Question> =
        runCatching { repository.getDesireSyncQuestions("neutral") }
            .onFailure { Log.w(TAG, "load desire questions failed", it) }
            .getOrElse { emptyList() }
            .filter { isBinaryQuestion(it) }

    fun startAnswering() {
        _uiState.update { it.copy(phase = DesireSyncPhase.ANSWER, currentIndex = 0) }
    }

    fun select(optionId: String) {
        val s = _uiState.value
        if (s.pendingSelection != null || s.phase != DesireSyncPhase.ANSWER) 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 = DesireSyncPhase.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 = DesireSyncPhase.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.
    }

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

    /** 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 == DesireSyncPhase.REVEAL) it
                        else it.copy(phase = DesireSyncPhase.WAITING)
                    }
                    // else: I haven't answered yet — stay on INTRO/ANSWER.
                }
            }
        }
    }

    private fun revealResult(mine: List<String>, theirs: List<String>) {
        if (_uiState.value.phase == DesireSyncPhase.REVEAL) return
        val questions = _uiState.value.questions
        val matches = questions.indices.mapNotNull { i ->
            val a = mine.getOrNull(i)?.lowercase()
            val b = theirs.getOrNull(i)?.lowercase()
            if (a != null && b != null && a in POSITIVE_IDS && b in POSITIVE_IDS) {
                DesireMatch(questions[i])
            } else null
        }
        _uiState.update { it.copy(phase = DesireSyncPhase.REVEAL, matches = matches) }
        // Both have answered — release the one-game lock so a new game can start.
        finishSession()
    }

    /** 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 restart() {
        observeJob?.cancel()
        sessionId = null
        submitted = false
        _uiState.value = DesireSyncUiState()
        load()
    }

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

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

    companion object {
        private const val ADVANCE_DELAY_MS = 380L
        private const val TAG = "DesireSyncViewModel"
    }
}

// ── Root screen ───────────────────────────────────────────────────────────────

@Composable
fun DesireSyncScreen(
    onNavigate: (String) -> Unit = {},
    viewModel: DesireSyncViewModel = 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) {
            DesireSyncPhase.LOADING -> CloserHeartLoader(
                modifier = Modifier.align(Alignment.Center)
            )
            DesireSyncPhase.ERROR -> DSErrorScreen(
                message = state.error ?: "Something didn't load. Go back and try again.",
                onBack = viewModel::quit,
                onRetry = if (state.submitFailed) viewModel::retrySubmit else null
            )
            DesireSyncPhase.SETUP -> DSSetupScreen(
                selectedLength = state.selectedLength,
                onLengthSelected = viewModel::setLength,
                onStart = viewModel::startGame,
                onBack = viewModel::quit
            )
            DesireSyncPhase.INTRO -> DSIntroScreen(
                total = state.questions.size,
                onReady = viewModel::startAnswering
            )
            DesireSyncPhase.ANSWER -> {
                val question = state.questions.getOrNull(state.currentIndex) ?: return@Box
                DSAnswerScreen(
                    question = question,
                    index = state.currentIndex,
                    total = state.questions.size,
                    pendingSelection = state.pendingSelection,
                    onSelect = viewModel::select,
                    onQuit = viewModel::quit
                )
            }
            DesireSyncPhase.WAITING -> DSWaitingScreen(
                partnerName = state.partnerName,
                onBack = viewModel::quit,
                onAbandon = viewModel::abandon
            )
            DesireSyncPhase.REVEAL -> DSRevealScreen(
                matches = state.matches,
                total = state.questions.size,
                partnerName = state.partnerName,
                onPlayAgain = viewModel::restart,
                onHome = viewModel::quit
            )
        }

        var dsCelebrate by remember { mutableStateOf(false) }
        var dsCelebrated by remember { mutableStateOf(false) }
        LaunchedEffect(state.phase) {
            if (state.phase == DesireSyncPhase.REVEAL && !dsCelebrated) {
                dsCelebrated = true
                dsCelebrate = true
            }
        }
        val dsRatio = state.questions.size.takeIf { it > 0 }
            ?.let { state.matches.size.toFloat() / it } ?: 0.5f
        CelebrationOverlay(
            visible = dsCelebrate,
            intensity = 0.5f + dsRatio,
            onFinished = { dsCelebrate = false }
        )
    }
}

// ── Phase screens ─────────────────────────────────────────────────────────────

@Composable
private fun DSSetupScreen(
    selectedLength: SessionLength,
    onLengthSelected: (SessionLength) -> Unit,
    onStart: () -> Unit,
    onBack: () -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding()
            .padding(horizontal = 24.dp, vertical = 24.dp),
        verticalArrangement = Arrangement.spacedBy(24.dp)
    ) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "How long?",
                style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold)
            )
            TextButton(onClick = onBack) {
                Text("Back", color = MaterialTheme.colorScheme.onSurfaceVariant)
            }
        }
        Text(
            text = "Choose a round length. Your partner will answer the same questions.",
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant
        )
        Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
            SessionLength.values().forEach { len ->
                Surface(
                    onClick = { onLengthSelected(len) },
                    modifier = Modifier.weight(1f),
                    shape = RoundedCornerShape(12.dp),
                    color = if (len == selectedLength) CloserPalette.Romantic else Color.Transparent,
                    border = if (len != selectedLength)
                        BorderStroke(1.dp, CloserPalette.Romantic.copy(alpha = 0.4f))
                    else null
                ) {
                    Text(
                        text = len.label,
                        modifier = Modifier.padding(vertical = 10.dp),
                        style = MaterialTheme.typography.labelMedium,
                        color = if (len == selectedLength) Color.White else CloserPalette.Romantic,
                        textAlign = TextAlign.Center
                    )
                }
            }
        }
        Spacer(Modifier.weight(1f))
        Button(
            onClick = onStart,
            modifier = Modifier.fillMaxWidth().heightIn(min = 56.dp),
            shape = RoundedCornerShape(18.dp),
            colors = ButtonDefaults.buttonColors(containerColor = CloserPalette.Romantic)
        ) {
            Text("Create session")
        }
    }
}

@Composable
private fun DSIntroScreen(total: Int, onReady: () -> Unit) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding()
            .padding(horizontal = 28.dp, vertical = 40.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        StatusGlyph(
            icon = Icons.Filled.FavoriteBorder,
            tint = CloserPalette.Romantic,
            container = CloserPalette.Romantic.copy(alpha = 0.12f)
        )
        Spacer(Modifier.height(20.dp))
        Text(
            text = "Answer $total questions privately — just tap Yes or No.",
            style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center
        )
        Spacer(Modifier.height(8.dp))
        Text(
            text = "Your partner answers on their own device. Only the things you both want will ever be shown — everything else stays private.",
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            textAlign = TextAlign.Center
        )
        Spacer(Modifier.height(36.dp))
        Button(
            onClick = onReady,
            modifier = Modifier.fillMaxWidth().heightIn(min = 56.dp),
            shape = RoundedCornerShape(18.dp),
            colors = ButtonDefaults.buttonColors(containerColor = CloserPalette.Romantic)
        ) { Text("I'm ready", color = Color.White) }
    }
}

@Composable
private fun DSWaitingScreen(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))
        StatusGlyph(
            icon = Icons.Filled.Favorite,
            tint = CloserPalette.Romantic,
            container = CloserPalette.Romantic.copy(alpha = 0.12f)
        )
        Text(
            text = "Your answers are in!",
            style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center
        )
        Text(
            text = "Nothing is shared yet. The moment $partnerName finishes, we'll reveal only what you both said yes to.",
            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 DSErrorScreen(message: String, onBack: () -> Unit, onRetry: (() -> Unit)? = null) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding()
            .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, shape = RoundedCornerShape(18.dp)) { Text("Try again") }
            Spacer(Modifier.height(8.dp))
        }
        OutlinedButton(onClick = onBack, shape = RoundedCornerShape(18.dp)) {
            Text("Back to Play")
        }
    }
}

@Composable
private fun DSAnswerScreen(
    question: Question,
    index: Int,
    total: Int,
    pendingSelection: String?,
    onSelect: (String) -> Unit,
    onQuit: () -> Unit
) {
    val config = question.answerConfig as? ChoiceAnswerConfigImpl
    val options = config?.config?.options?.take(2) ?: return

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

        DesireProgressPill(
            progress = index.toFloat() / total,
            modifier = Modifier.fillMaxWidth()
        )

        Card(
            modifier = Modifier.fillMaxWidth().weight(1f),
            shape = RoundedCornerShape(28.dp),
            colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.92f)),
            elevation = CardDefaults.cardElevation(8.dp)
        ) {
            Box(modifier = Modifier.fillMaxSize().padding(28.dp), contentAlignment = Alignment.Center) {
                Text(
                    text = question.text,
                    style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold),
                    color = MaterialTheme.colorScheme.onSurface,
                    textAlign = TextAlign.Center,
                    maxLines = 5,
                    overflow = TextOverflow.Ellipsis
                )
            }
        }

        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            options.forEach { option ->
                val isSelected = pendingSelection == option.id
                val isPositive = option.id.lowercase() in POSITIVE_IDS
                val selectedColor = if (isPositive) CloserPalette.Romantic else Color(0xFF6B6B8A)
                val targetColor = when {
                    isSelected -> selectedColor
                    pendingSelection != null -> MaterialTheme.colorScheme.surface.copy(alpha = 0.5f)
                    else -> MaterialTheme.colorScheme.surface
                }
                val animatedColor by animateColorAsState(targetColor, animationSpec = tween(160), label = "option")

                Card(
                    onClick = { if (pendingSelection == null) onSelect(option.id) },
                    modifier = Modifier.weight(1f).height(88.dp),
                    shape = RoundedCornerShape(18.dp),
                    colors = CardDefaults.cardColors(containerColor = animatedColor),
                    elevation = CardDefaults.cardElevation(if (isSelected) 8.dp else 3.dp)
                ) {
                    Box(
                        modifier = Modifier.fillMaxWidth().padding(16.dp),
                        contentAlignment = Alignment.Center
                    ) {
                        Text(
                            text = option.text,
                            style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
                            color = if (isSelected) Color.White else MaterialTheme.colorScheme.onSurface,
                            textAlign = TextAlign.Center
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun DSRevealScreen(
    matches: List<DesireMatch>,
    total: Int,
    partnerName: String,
    onPlayAgain: () -> Unit,
    onHome: () -> Unit
) {
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding()
            .padding(horizontal = 20.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        item {
            Column(
                modifier = Modifier.fillMaxWidth().padding(top = 32.dp, bottom = 4.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(10.dp)
            ) {
                StatusGlyph(
                    icon = if (matches.isEmpty()) Icons.Filled.FavoriteBorder else Icons.Filled.Favorite,
                    tint = CloserPalette.Romantic,
                    container = CloserPalette.Romantic.copy(alpha = 0.12f),
                    size = 82.dp,
                    iconSize = 40.dp
                )
                Text(
                    text = if (matches.isEmpty()) "Nothing in common this round" else "${matches.size} shared desire${if (matches.size != 1) "s" else ""}",
                    style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold),
                    color = MaterialTheme.colorScheme.onSurface,
                    textAlign = TextAlign.Center
                )
                if (total - matches.size > 0) {
                    Text(
                        text = "${total - matches.size} answer${if (total - matches.size != 1) "s" else ""} stayed private",
                        style = MaterialTheme.typography.bodyMedium,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                        textAlign = TextAlign.Center
                    )
                }
                DesireRevealMeter(matches = matches.size, total = total, partnerName = partnerName)
            }
        }

        if (matches.isNotEmpty()) {
            item {
                Text(
                    text = "You both said yes to",
                    style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
                    color = CloserPalette.Romantic
                )
            }

            items(matches) { match ->
                DesireMatchCard(match)
            }
        } else {
            item {
                Card(
                    modifier = Modifier.fillMaxWidth(),
                    shape = RoundedCornerShape(20.dp),
                    colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
                    elevation = CardDefaults.cardElevation(4.dp)
                ) {
                    Text(
                        text = "Your desires are very different this round — or maybe you're just not in the same headspace. Play again to explore more.",
                        modifier = Modifier.padding(20.dp),
                        style = MaterialTheme.typography.bodyLarge,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                        textAlign = TextAlign.Center
                    )
                }
            }
        }

        item {
            Column(
                modifier = Modifier.padding(top = 8.dp, bottom = 20.dp),
                verticalArrangement = Arrangement.spacedBy(10.dp)
            ) {
                Button(
                    onClick = onPlayAgain,
                    modifier = Modifier.fillMaxWidth().heightIn(min = 56.dp),
                    shape = RoundedCornerShape(18.dp),
                    colors = ButtonDefaults.buttonColors(containerColor = CloserPalette.Romantic)
                ) { Text("Play again", color = Color.White) }
                OutlinedButton(
                    onClick = onHome,
                    modifier = Modifier.fillMaxWidth().heightIn(min = 56.dp),
                    shape = RoundedCornerShape(18.dp)
                ) { Text("Back to Play") }
            }
        }
    }
}

@Composable
private fun DesireProgressPill(
    progress: Float,
    modifier: Modifier = Modifier
) {
    Box(
        modifier = modifier
            .height(8.dp)
            .clip(RoundedCornerShape(999.dp))
            .background(CloserPalette.Romantic.copy(alpha = 0.15f))
    ) {
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .fillMaxWidth(progress.coerceIn(0f, 1f))
                .clip(RoundedCornerShape(999.dp))
                .background(CloserPalette.Romantic)
        )
    }
}

@Composable
private fun DesireRevealMeter(
    matches: Int,
    total: Int,
    partnerName: String
) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(22.dp),
        colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.86f)),
        elevation = CardDefaults.cardElevation(0.dp)
    ) {
        Column(
            modifier = Modifier.padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.spacedBy(10.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                // Each person's individual answers always stay private — only mutual
                // "yes" answers surface as shared desires. Previously these tiles showed
                // "$total private" (e.g. "5 private"), which contradicted the caption's
                // "${total - matches} kept private" (e.g. "2 kept private") and confused
                // whether 3 were shared or all 5 stayed private (B-003). Show just the
                // privacy guarantee; the shared/private breakdown lives in the caption.
                DesirePrivacyTile(
                    label = "You",
                    value = "Private",
                    modifier = Modifier.weight(1f)
                )
                StatusGlyph(
                    icon = Icons.Filled.Sync,
                    tint = CloserPalette.Romantic,
                    container = CloserPalette.Romantic.copy(alpha = 0.12f),
                    size = 38.dp,
                    iconSize = 20.dp
                )
                DesirePrivacyTile(
                    label = partnerName,
                    value = "Private",
                    modifier = Modifier.weight(1f)
                )
            }
            DesireProgressPill(
                progress = matches.toFloat() / total.coerceAtLeast(1),
                modifier = Modifier.fillMaxWidth()
            )
            Text(
                text = "$matches shared, ${total - matches} kept private",
                style = MaterialTheme.typography.labelMedium,
                color = CloserPalette.Romantic,
                fontWeight = FontWeight.SemiBold,
                textAlign = TextAlign.Center,
                modifier = Modifier.fillMaxWidth()
            )
        }
    }
}

@Composable
private fun DesirePrivacyTile(
    label: String,
    value: String,
    modifier: Modifier = Modifier
) {
    Surface(
        modifier = modifier,
        shape = RoundedCornerShape(16.dp),
        color = CloserPalette.Romantic.copy(alpha = 0.08f)
    ) {
        Column(
            modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.spacedBy(2.dp)
        ) {
            Text(
                text = label,
                style = MaterialTheme.typography.labelSmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
            Text(
                text = value,
                style = MaterialTheme.typography.labelMedium,
                color = CloserPalette.Romantic,
                fontWeight = FontWeight.SemiBold,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}

@Composable
private fun DesireMatchCard(match: DesireMatch) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(18.dp),
        colors = CardDefaults.cardColors(
            containerColor = CloserPalette.Romantic.copy(alpha = 0.10f)
        ),
        elevation = CardDefaults.cardElevation(0.dp)
    ) {
        Row(
            modifier = Modifier.padding(horizontal = 18.dp, vertical = 14.dp),
            horizontalArrangement = Arrangement.spacedBy(12.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            StatusGlyph(
                icon = Icons.Filled.Favorite,
                tint = CloserPalette.Romantic,
                container = CloserPalette.Romantic.copy(alpha = 0.12f),
                size = 34.dp,
                iconSize = 18.dp
            )
            Text(
                text = match.question.text,
                style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Medium),
                // Was a hardcoded dark plum (Color(0xFF3D1F2E)) — fine on the light card in
                // light mode, but dim/low-contrast on the dark-tinted card in dark mode
                // (C-DS-001). onSurface adapts: dark text on light, light text on dark.
                color = MaterialTheme.colorScheme.onSurface,
                modifier = Modifier.weight(1f),
                maxLines = 3,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}

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

sealed interface DSReplayPhase {
    object Loading : DSReplayPhase
    data class Ready(val matches: List<DesireMatch>, val total: Int) : DSReplayPhase
    data class Error(val message: String) : DSReplayPhase
}

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

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

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

    init {
        viewModelScope.launch {
            val uid = gameSessionManager.currentUserId ?: run {
                _uiState.update { it.copy(phase = DSReplayPhase.Error("Not signed in")) }
                return@launch
            }
            val couple = gameSessionManager.getCoupleForUser(uid) ?: run {
                _uiState.update { it.copy(phase = DSReplayPhase.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 = DSReplayPhase.Error("Session not found")) }
                return@launch
            }
            val answers = answerDataSource.getAnswers(couple.id, sessionId) ?: run {
                _uiState.update { it.copy(phase = DSReplayPhase.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 matches = questions.indices.mapNotNull { i ->
                val a = mine.getOrNull(i)?.lowercase()
                val b = theirs.getOrNull(i)?.lowercase()
                if (a != null && b != null && a in POSITIVE_IDS && b in POSITIVE_IDS) {
                    DesireMatch(questions[i])
                } else null
            }
            _uiState.update { it.copy(phase = DSReplayPhase.Ready(matches, questions.size)) }
        }
    }
}

@Composable
fun DSReplayScreen(
    onNavigate: (String) -> Unit = {},
    viewModel: DSReplayViewModel = hiltViewModel()
) {
    val state by viewModel.uiState.collectAsState()
    val phase = state.phase
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(closerBackgroundBrush())
    ) {
        when (phase) {
            is DSReplayPhase.Loading -> CloserHeartLoader(
                modifier = Modifier.align(Alignment.Center)
            )
            is DSReplayPhase.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 DSReplayPhase.Ready -> DSRevealScreen(
                matches = phase.matches,
                total = phase.total,
                partnerName = state.partnerName,
                onPlayAgain = { onNavigate(AppRoute.DESIRE_SYNC) },
                onHome = { onNavigate(AppRoute.PLAY) }
            )
        }
    }
}
