package app.closer.ui.howwell

import app.closer.domain.model.GameType
import app.closer.ui.theme.closerCardColor
import android.util.Log
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Canvas
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.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.draw.clip
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Psychology
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.icons.filled.Timeline
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.graphics.StrokeCap
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.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 android.content.Context
import app.closer.domain.model.AnswerConfig
import app.closer.domain.model.ChoiceAnswerConfigImpl
import app.closer.domain.model.ChoiceOption
import app.closer.domain.model.Question
import app.closer.domain.model.ScaleAnswerConfigImpl
import app.closer.domain.model.SessionLength
import app.closer.domain.model.ThisOrThatAnswerConfigImpl
import dagger.hilt.android.qualifiers.ApplicationContext
import app.closer.domain.repository.QuestionRepository
import app.closer.domain.repository.QuestionSessionRepository
import app.closer.data.remote.FirestoreHowWellDataSource
import app.closer.data.remote.HowWellAnswers
import app.closer.data.remote.HowWellRawAnswer
import app.closer.domain.usecase.GameSessionManager
import app.closer.ui.components.BrandMessageRotator
import app.closer.ui.components.ResultGlyph
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 kotlin.math.abs
import kotlinx.coroutines.Job
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 ────────────────────────────────────────────────────────────────────

data class HowWellAnswer(
    val selectedOptionId: String? = null,
    val scaleValue: Int? = null
)

fun HowWellAnswer.isMatch(other: HowWellAnswer): Boolean = when {
    selectedOptionId != null -> selectedOptionId == other.selectedOptionId
    scaleValue != null && other.scaleValue != null -> scaleValue == other.scaleValue
    else -> false
}

fun HowWellAnswer.isClose(other: HowWellAnswer): Boolean =
    scaleValue != null && other.scaleValue != null &&
    !isMatch(other) && abs(scaleValue - other.scaleValue) == 1

fun HowWellAnswer.displayText(config: AnswerConfig?): String = when {
    selectedOptionId != null -> when (config) {
        is ChoiceAnswerConfigImpl -> config.config.options.find { it.id == selectedOptionId }?.text ?: selectedOptionId
        is ThisOrThatAnswerConfigImpl -> when (selectedOptionId) {
            config.config.optionA.id -> config.config.optionA.text
            config.config.optionB.id -> config.config.optionB.text
            else -> selectedOptionId
        }
        else -> selectedOptionId
    }
    scaleValue != null -> "$scaleValue"
    else -> "—"
}

data class HowWellResult(
    val question: Question,
    val playerAAnswer: HowWellAnswer,
    val playerBAnswer: HowWellAnswer,
    val isMatch: Boolean,
    val isClose: Boolean
)

enum class HowWellPhase { LOADING, SETUP, INTRO, ANSWER, WAITING, COMPLETE, ERROR }

data class HowWellUiState(
    val phase: HowWellPhase = HowWellPhase.LOADING,
    val questions: List<Question> = emptyList(),
    val currentIndex: Int = 0,
    val selectedOptionId: String? = null,
    val selectedScale: Int? = null,
    /** True for the session starter (answers honestly); false for the guesser (predicts). */
    val amSubject: Boolean = true,
    val partnerName: String = "Your partner",
    val results: List<HowWellResult> = emptyList(),
    val score: Int = 0,
    val selectedLength: SessionLength = SessionLength.STANDARD,
    val error: String? = null,
    val submitFailed: Boolean = false,
    val navigateTo: String? = null
)

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

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

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

    private var userId: String? = null
    private var partnerId: String? = null
    private var coupleId: String? = null
    private var sessionId: String? = null
    private var startedByUserId: String? = null
    private val myAnswers = mutableListOf<HowWellRawAnswer>()
    private var observeJob: Job? = null
    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 { it.copy(partnerName = name) } }
            }

            val active = runCatching { gameSessionManager.getActiveSession(couple.id) }.getOrNull()
            when {
                active != null && active.gameType == GameType.HOW_WELL ->
                    joinSession(uid, active.id, active.startedByUserId, 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 = HowWellPhase.SETUP) }
            }
        }
    }

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

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

    /** First partner becomes the subject: they answer about themselves. */
    private suspend fun createSession(uid: String) {
        val questions = runCatching { repository.getQuestionsForPrediction() }
            .onFailure { Log.w(TAG, "Failed to load prediction questions", it) }
            .getOrElse { emptyList() }
            .shuffled()
            .take(_uiState.value.selectedLength.count)
        if (questions.isEmpty()) return fail("No questions available.")

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

        when {
            startResult.isSuccess -> {
                sessionId = startResult.getOrThrow()
                startedByUserId = uid
                _uiState.update {
                    it.copy(phase = HowWellPhase.INTRO, questions = questions, amSubject = true)
                }
                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 becomes the guesser: same questions, predicting the subject. */
    private suspend fun joinSession(
        uid: String,
        existingSessionId: String,
        startedBy: String,
        questionIds: List<String>
    ) {
        sessionId = existingSessionId
        startedByUserId = startedBy
        val questions = questionIds.mapNotNull { repository.getQuestionById(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 cId = coupleId
        val alreadyAnswered = cId != null &&
            runCatching { dataSource.getAnswers(cId, existingSessionId)?.byUser?.get(uid)?.isNotEmpty() == true }
                .getOrDefault(false)
        _uiState.update {
            it.copy(
                phase = if (alreadyAnswered) HowWellPhase.WAITING else HowWellPhase.INTRO,
                questions = questions,
                amSubject = startedBy == uid
            )
        }
        observeReveal()
    }

    fun selectOption(id: String) = _uiState.update { it.copy(selectedOptionId = id, selectedScale = null) }
    fun selectScale(v: Int) = _uiState.update { it.copy(selectedScale = v, selectedOptionId = null) }

    fun startAnswering() {
        _uiState.update {
            it.copy(phase = HowWellPhase.ANSWER, currentIndex = 0, selectedOptionId = null, selectedScale = null)
        }
    }

    fun confirm() {
        val s = _uiState.value
        if (s.phase != HowWellPhase.ANSWER) return
        if (s.selectedOptionId == null && s.selectedScale == null) return
        myAnswers.add(HowWellRawAnswer(s.selectedOptionId, s.selectedScale))
        val next = s.currentIndex + 1
        if (next >= s.questions.size) {
            _uiState.update {
                it.copy(selectedOptionId = null, selectedScale = null, phase = HowWellPhase.WAITING)
            }
            viewModelScope.launch { submitAnswers() }
        } else {
            _uiState.update { it.copy(currentIndex = next, selectedOptionId = null, selectedScale = null) }
        }
    }

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

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

    /** Single source of truth for WAITING/COMPLETE: 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.isNullOrEmpty() && !theirs.isNullOrEmpty() -> revealResult(answers)
                    !mine.isNullOrEmpty() -> _uiState.update {
                        if (it.phase == HowWellPhase.COMPLETE) it else it.copy(phase = HowWellPhase.WAITING)
                    }
                    // else: I haven't answered yet — stay on INTRO/ANSWER.
                }
            }
        }
    }

    private fun revealResult(answers: HowWellAnswers) {
        if (_uiState.value.phase == HowWellPhase.COMPLETE) return
        val subjectId = startedByUserId ?: return
        val guesserId = if (subjectId == userId) partnerId else userId
        val subjectAnswers = answers.byUser[subjectId].orEmpty()
        val guesserAnswers = guesserId?.let { answers.byUser[it] }.orEmpty()
        val results = _uiState.value.questions.mapIndexed { i, q ->
            val actual = subjectAnswers.getOrNull(i).toAnswer()
            val prediction = guesserAnswers.getOrNull(i).toAnswer()
            HowWellResult(q, actual, prediction, prediction.isMatch(actual), prediction.isClose(actual))
        }
        _uiState.update {
            it.copy(
                phase = HowWellPhase.COMPLETE,
                results = results,
                score = results.count { r -> r.isMatch }
            )
        }
        // Both have answered — release the one-game lock so a new game can start.
        finishSession()
    }

    private fun HowWellRawAnswer?.toAnswer(): HowWellAnswer =
        HowWellAnswer(this?.optionId, this?.scale)

    /** 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
        startedByUserId = null
        submitted = false
        myAnswers.clear()
        _uiState.value = HowWellUiState()
        load()
    }

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

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

    companion object {
        private const val TAG = "HowWellViewModel"
    }
}

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

@Composable
fun HowWellScreen(
    onNavigate: (String) -> Unit = {},
    viewModel: HowWellViewModel = 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) {
            HowWellPhase.LOADING -> CloserHeartLoader(
                modifier = Modifier.align(Alignment.Center)
            )
            HowWellPhase.ERROR -> HowWellErrorScreen(
                message = state.error ?: "Something didn't load. Go back and try again.",
                onBack = viewModel::quit,
                onRetry = if (state.submitFailed) viewModel::retrySubmit else null
            )
            HowWellPhase.SETUP -> HWSetupScreen(
                selectedLength = state.selectedLength,
                onLengthSelected = viewModel::setLength,
                onStart = viewModel::startGame,
                onBack = viewModel::quit
            )
            HowWellPhase.INTRO -> PlayerIntroScreen(
                amSubject = state.amSubject,
                partnerName = state.partnerName,
                total = state.questions.size,
                onReady = viewModel::startAnswering
            )
            HowWellPhase.ANSWER -> {
                val q = state.questions.getOrNull(state.currentIndex) ?: return@Box
                AnswerScreen(
                    question = q,
                    index = state.currentIndex,
                    total = state.questions.size,
                    isGuesser = !state.amSubject,
                    partnerName = state.partnerName,
                    selectedOptionId = state.selectedOptionId,
                    selectedScale = state.selectedScale,
                    onSelectOption = viewModel::selectOption,
                    onSelectScale = viewModel::selectScale,
                    onConfirm = viewModel::confirm,
                    onQuit = viewModel::quit
                )
            }
            HowWellPhase.WAITING -> HowWellWaitingScreen(
                amSubject = state.amSubject,
                partnerName = state.partnerName,
                onBack = viewModel::quit,
                onAbandon = viewModel::abandon
            )
            HowWellPhase.COMPLETE -> CompleteScreen(
                score = state.score,
                total = state.questions.size,
                results = state.results,
                amSubject = state.amSubject,
                partnerName = state.partnerName,
                onPlayAgain = viewModel::restart,
                onHome = viewModel::quit
            )
        }

        var hwCelebrate by remember { mutableStateOf(false) }
        var hwCelebrated by remember { mutableStateOf(false) }
        LaunchedEffect(state.phase) {
            if (state.phase == HowWellPhase.COMPLETE && !hwCelebrated) {
                hwCelebrated = true
                hwCelebrate = true
            }
        }
        val hwRatio = state.questions.size.takeIf { it > 0 }
            ?.let { state.score.toFloat() / it } ?: 0.5f
        CelebrationOverlay(
            visible = hwCelebrate,
            intensity = 0.5f + hwRatio,
            onFinished = { hwCelebrate = false }
        )
    }
}

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

@Composable
private fun HWSetupScreen(
    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. You'll answer about yourself and your partner will try to guess.",
            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.PurpleDeep else Color.Transparent,
                    border = if (len != selectedLength)
                        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 == selectedLength) Color.White else CloserPalette.PurpleDeep,
                        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.PurpleDeep)
        ) {
            Text("Create session")
        }
    }
}

@Composable
private fun PlayerIntroScreen(
    amSubject: Boolean,
    partnerName: String,
    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 = if (amSubject) Icons.Filled.Person else Icons.Filled.Psychology,
            tint = CloserPalette.PurpleDeep,
            container = CloserPalette.PurpleMist
        )
        Spacer(Modifier.height(20.dp))
        Text(
            text = if (amSubject)
                "Answer $total questions about yourself, honestly."
            else
                "Predict how $partnerName answered $total questions about themselves.",
            style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center,
            lineHeight = MaterialTheme.typography.headlineSmall.lineHeight
        )
        Spacer(Modifier.height(10.dp))
        Text(
            text = if (amSubject)
                "$partnerName guesses your answers on their own device. You'll both see how well they know you once you're both done."
            else
                "Answer on your own device — no peeking needed. You'll both see your score once you're both done.",
            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.PurpleDeep)
        ) { Text("I'm ready", color = Color.White) }
    }
}

@Composable
private fun HowWellWaitingScreen(amSubject: Boolean, 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.Sync,
            tint = CloserPalette.PurpleDeep,
            container = CloserPalette.PurpleMist
        )
        Text(
            text = "All done on your side!",
            style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center
        )
        Text(
            text = if (amSubject)
                "Waiting for $partnerName to finish guessing — then you'll both see how well they know you."
            else
                "Waiting for $partnerName to finish answering — then you'll both see your score.",
            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 HowWellErrorScreen(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 AnswerScreen(
    question: Question,
    index: Int,
    total: Int,
    isGuesser: Boolean,
    partnerName: String,
    selectedOptionId: String?,
    selectedScale: Int?,
    onSelectOption: (String) -> Unit,
    onSelectScale: (Int) -> Unit,
    onConfirm: () -> Unit,
    onQuit: () -> Unit
) {
    val hasSelection = selectedOptionId != null || selectedScale != null

    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.PurpleMist) {
                Text(
                    text = "${if (isGuesser) "Your guess" else "About you"}  ·  ${index + 1} / $total",
                    modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
                    style = MaterialTheme.typography.labelMedium,
                    color = CloserPalette.PurpleDeep,
                    fontWeight = FontWeight.SemiBold
                )
            }
            TextButton(onClick = onQuit) {
                Text("Quit", color = MaterialTheme.colorScheme.onSurfaceVariant)
            }
        }

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

        Card(
            modifier = Modifier.fillMaxWidth().weight(1f),
            shape = RoundedCornerShape(28.dp),
            colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.9f)),
            elevation = CardDefaults.cardElevation(8.dp)
        ) {
            Box(modifier = Modifier.fillMaxSize().padding(24.dp), contentAlignment = Alignment.Center) {
                Column(
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.spacedBy(10.dp)
                ) {
                    if (isGuesser) {
                        Surface(shape = RoundedCornerShape(999.dp), color = CloserPalette.PurpleMist) {
                            Text(
                                text = "How did $partnerName answer?",
                                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
                    )
                }
            }
        }

        when (val config = question.answerConfig) {
            is ChoiceAnswerConfigImpl -> SingleChoiceInput(
                options = config.config.options,
                selectedId = selectedOptionId,
                onSelect = onSelectOption
            )
            is ThisOrThatAnswerConfigImpl -> BinaryChoiceInput(
                optionA = config.config.optionA,
                optionB = config.config.optionB,
                selectedId = selectedOptionId,
                onSelect = onSelectOption
            )
            is ScaleAnswerConfigImpl -> ScaleInput(
                min = config.config.minScale,
                max = config.config.maxScale,
                minLabel = config.config.minLabel,
                maxLabel = config.config.maxLabel,
                selected = selectedScale,
                onSelect = onSelectScale
            )
            else -> {}
        }

        Button(
            onClick = onConfirm,
            enabled = hasSelection,
            modifier = Modifier.fillMaxWidth().heightIn(min = 56.dp),
            shape = RoundedCornerShape(18.dp),
            colors = ButtonDefaults.buttonColors(containerColor = CloserPalette.PurpleDeep)
        ) {
            Text(
                text = if (index + 1 >= total) "Done →" else "Confirm →",
                color = Color.White
            )
        }
    }
}

@Composable
private fun CompleteScreen(
    score: Int,
    total: Int,
    results: List<HowWellResult>,
    amSubject: Boolean,
    partnerName: String,
    onPlayAgain: () -> Unit,
    onHome: () -> Unit
) {
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .navigationBarsPadding()
            .padding(horizontal = 20.dp),
        verticalArrangement = Arrangement.spacedBy(10.dp)
    ) {
        item {
            Column(
                modifier = Modifier.fillMaxWidth().padding(top = 32.dp, bottom = 8.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(10.dp)
            ) {
                HowWellScoreRing(
                    score = score,
                    total = total,
                    modifier = Modifier.size(118.dp)
                )
                Text(
                    text = scoreLabel(score, total),
                    style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.SemiBold),
                    color = MaterialTheme.colorScheme.onSurface,
                    textAlign = TextAlign.Center
                )
                Text(
                    text = if (amSubject)
                        "$partnerName guessed $score of $total about you"
                    else
                        "You guessed $score of $total about $partnerName",
                    style = MaterialTheme.typography.bodyMedium,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    textAlign = TextAlign.Center
                )
            }
        }

        item {
            Text(
                text = "Breakdown",
                style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
                color = MaterialTheme.colorScheme.onBackground
            )
        }

        items(results) { result -> BreakdownRow(result) }

        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.PurpleDeep)
                ) { 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 HowWellProgressPill(
    progress: Float,
    modifier: Modifier = Modifier
) {
    Box(
        modifier = modifier
            .height(8.dp)
            .clip(RoundedCornerShape(999.dp))
            .background(CloserPalette.PurpleMist)
    ) {
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .fillMaxWidth(progress.coerceIn(0f, 1f))
                .clip(RoundedCornerShape(999.dp))
                .background(CloserPalette.PurpleDeep)
        )
    }
}

@Composable
private fun HowWellScoreRing(
    score: Int,
    total: Int,
    modifier: Modifier = Modifier
) {
    val progress = if (total == 0) 0f else score.toFloat() / total
    Box(
        modifier = modifier,
        contentAlignment = Alignment.Center
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            val strokeWidth = 11.dp.toPx()
            drawArc(
                color = CloserPalette.PurpleMist,
                startAngle = -90f,
                sweepAngle = 360f,
                useCenter = false,
                style = Stroke(width = strokeWidth, cap = StrokeCap.Round)
            )
            drawArc(
                color = CloserPalette.PurpleDeep,
                startAngle = -90f,
                sweepAngle = 360f * progress.coerceIn(0f, 1f),
                useCenter = false,
                style = Stroke(width = strokeWidth, cap = StrokeCap.Round)
            )
        }
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Text(
                text = "$score",
                style = MaterialTheme.typography.displaySmall.copy(fontWeight = FontWeight.Bold),
                color = CloserPalette.PurpleDeep
            )
            Text(
                text = "of $total",
                style = MaterialTheme.typography.labelMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                fontWeight = FontWeight.SemiBold
            )
        }
    }
}

@Composable
private fun BreakdownRow(result: HowWellResult) {
    val matchColor = Color(0xFF2E7D32)
    val closeColor = Color(0xFFF57F17)
    val missColor = Color(0xFFC62828)
    Card(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(14.dp),
        colors = CardDefaults.cardColors(
            containerColor = if (result.isMatch) Color(0xFFE8F5E9) else MaterialTheme.colorScheme.surface
        ),
        elevation = CardDefaults.cardElevation(2.dp)
    ) {
        Row(
            modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp),
            horizontalArrangement = Arrangement.spacedBy(10.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            ResultGlyph(
                isPositive = result.isMatch,
                isClose = result.isClose,
                size = 28.dp
            )
            Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
                Text(
                    text = result.question.text,
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis
                )
                val config = result.question.answerConfig
                if (result.isMatch) {
                    Text(
                        text = result.playerAAnswer.displayText(config),
                        style = MaterialTheme.typography.labelMedium.copy(fontWeight = FontWeight.SemiBold),
                        color = matchColor,
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis
                    )
                } else {
                    Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
                        Text(
                            text = result.playerAAnswer.displayText(config),
                            style = MaterialTheme.typography.labelMedium.copy(fontWeight = FontWeight.SemiBold),
                            color = if (result.isClose) closeColor else missColor,
                            maxLines = 1,
                            overflow = TextOverflow.Ellipsis,
                            modifier = Modifier.weight(1f, fill = false)
                        )
                        Text(
                            text = "→ ${result.playerBAnswer.displayText(config)}",
                            style = MaterialTheme.typography.labelMedium,
                            color = MaterialTheme.colorScheme.onSurfaceVariant,
                            maxLines = 1,
                            overflow = TextOverflow.Ellipsis,
                            modifier = Modifier.weight(1f, fill = false)
                        )
                    }
                }
            }
        }
    }
}

// ── Answer input widgets ──────────────────────────────────────────────────────

@Composable
private fun SingleChoiceInput(
    options: List<ChoiceOption>,
    selectedId: String?,
    onSelect: (String) -> Unit
) {
    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
        options.forEach { option ->
            val selected = selectedId == option.id
            Card(
                onClick = { onSelect(option.id) },
                modifier = Modifier.fillMaxWidth(),
                shape = RoundedCornerShape(14.dp),
                colors = CardDefaults.cardColors(
                    containerColor = if (selected) CloserPalette.PurpleDeep else MaterialTheme.colorScheme.surface
                ),
                elevation = CardDefaults.cardElevation(if (selected) 6.dp else 2.dp)
            ) {
                Text(
                    text = option.text,
                    modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp),
                    style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Medium),
                    color = if (selected) Color.White else MaterialTheme.colorScheme.onSurface,
                    maxLines = 2,
                    overflow = TextOverflow.Ellipsis
                )
            }
        }
    }
}

@Composable
private fun BinaryChoiceInput(
    optionA: ChoiceOption,
    optionB: ChoiceOption,
    selectedId: String?,
    onSelect: (String) -> Unit
) {
    Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(10.dp)) {
        listOf(optionA to "A", optionB to "B").forEach { (option, label) ->
            val selected = selectedId == option.id
            Card(
                onClick = { onSelect(option.id) },
                modifier = Modifier.weight(1f).heightIn(min = 80.dp),
                shape = RoundedCornerShape(14.dp),
                colors = CardDefaults.cardColors(
                    containerColor = if (selected) CloserPalette.PurpleDeep else MaterialTheme.colorScheme.surface
                ),
                elevation = CardDefaults.cardElevation(if (selected) 6.dp else 2.dp)
            ) {
                Column(
                    modifier = Modifier.fillMaxWidth().padding(12.dp),
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.spacedBy(4.dp)
                ) {
                    Text(
                        text = label,
                        style = MaterialTheme.typography.labelSmall,
                        fontWeight = FontWeight.Bold,
                        color = if (selected) Color.White.copy(alpha = 0.7f) else MaterialTheme.colorScheme.onSurfaceVariant
                    )
                    Text(
                        text = option.text,
                        style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.SemiBold),
                        color = if (selected) Color.White else MaterialTheme.colorScheme.onSurface,
                        textAlign = TextAlign.Center,
                        maxLines = 2,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }
        }
    }
}

@Composable
private fun ScaleInput(
    min: Int,
    max: Int,
    minLabel: String,
    maxLabel: String,
    selected: Int?,
    onSelect: (Int) -> Unit
) {
    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceEvenly
        ) {
            (min..max).forEach { value ->
                val isSelected = selected == value
                Surface(
                    onClick = { onSelect(value) },
                    shape = CircleShape,
                    color = if (isSelected) CloserPalette.PurpleDeep else MaterialTheme.colorScheme.surface,
                    modifier = Modifier.size(52.dp),
                    shadowElevation = if (isSelected) 6.dp else 2.dp
                ) {
                    Box(contentAlignment = Alignment.Center) {
                        Text(
                            text = "$value",
                            style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
                            color = if (isSelected) Color.White else MaterialTheme.colorScheme.onSurface
                        )
                    }
                }
            }
        }
        Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
            Text(minLabel, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
            Text(maxLabel, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
        }
    }
}

// ── Helpers ───────────────────────────────────────────────────────────────────

private fun scoreLabel(score: Int, total: Int): String = when {
    score == total -> "Perfect read"
    score >= total * 0.8 -> "You really know each other"
    score >= total * 0.6 -> "Pretty good!"
    score >= total * 0.4 -> "Getting there!"
    else -> "Room to grow"
}

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

sealed interface HWReplayPhase {
    object Loading : HWReplayPhase
    data class Ready(val score: Int, val total: Int, val results: List<HowWellResult>, val amSubject: Boolean) : HWReplayPhase
    data class Error(val message: String) : HWReplayPhase
}

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

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

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

    init {
        viewModelScope.launch {
            val uid = gameSessionManager.currentUserId ?: run {
                _uiState.update { it.copy(phase = HWReplayPhase.Error("Not signed in")) }
                return@launch
            }
            val couple = gameSessionManager.getCoupleForUser(uid) ?: run {
                _uiState.update { it.copy(phase = HWReplayPhase.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 = HWReplayPhase.Error("Session not found")) }
                return@launch
            }
            val answers = answerDataSource.getAnswers(couple.id, sessionId) ?: run {
                _uiState.update { it.copy(phase = HWReplayPhase.Error("Answers not found")) }
                return@launch
            }
            val questions = session.questionIds.mapNotNull { questionRepository.getQuestionById(it) }

            val subjectId = session.startedByUserId
            val guesserId = if (subjectId == uid) partnerId else uid
            val subjectAnswers = answers.byUser[subjectId].orEmpty()
            val guesserAnswers = guesserId?.let { answers.byUser[it] }.orEmpty()
            val results = questions.mapIndexed { i, q ->
                val actual = subjectAnswers.getOrNull(i).toAnswer()
                val prediction = guesserAnswers.getOrNull(i).toAnswer()
                HowWellResult(q, actual, prediction, prediction.isMatch(actual), prediction.isClose(actual))
            }
            val score = results.count { it.isMatch }
            val amSubject = subjectId == uid
            _uiState.update { it.copy(phase = HWReplayPhase.Ready(score, questions.size, results, amSubject)) }
        }
    }

    private fun HowWellRawAnswer?.toAnswer(): HowWellAnswer =
        HowWellAnswer(this?.optionId, this?.scale)
}

@Composable
fun HowWellReplayScreen(
    onNavigate: (String) -> Unit = {},
    viewModel: HowWellReplayViewModel = hiltViewModel()
) {
    val state by viewModel.uiState.collectAsState()
    val phase = state.phase
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(closerBackgroundBrush())
    ) {
        when (phase) {
            is HWReplayPhase.Loading -> CloserHeartLoader(
                modifier = Modifier.align(Alignment.Center)
            )
            is HWReplayPhase.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 HWReplayPhase.Ready -> CompleteScreen(
                score = phase.score,
                total = phase.total,
                results = phase.results,
                amSubject = phase.amSubject,
                partnerName = state.partnerName,
                onPlayAgain = { onNavigate(AppRoute.HOW_WELL) },
                onHome = { onNavigate(AppRoute.PLAY) }
            )
        }
    }
}
