package ru.durakscore.app

import android.os.Bundle
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.content.Context
import android.graphics.drawable.ColorDrawable
import android.view.HapticFeedbackConstants
import android.widget.Toast
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.compose.BackHandler
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import ru.durakscore.app.ui.theme.DurakScoreTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.material3.CircularProgressIndicator
import kotlinx.coroutines.delay
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.concurrent.thread


class MainActivity : ComponentActivity() {

    companion object {
        private const val UPDATE_LOG_TAG = "DurakUpdate"
        private const val VPS_LATEST_URL =
            "https://updates.bearmsk.ru/latest.json"
    }

    private val vpsUpdateStarted =
        AtomicBoolean(false)

    private var availableVpsRelease by
        mutableStateOf<VpsRelease?>(null)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Тёмный фон окна убирает белую вспышку до первого Compose-кадра.
        window.setBackgroundDrawable(
            ColorDrawable(0xFF000202.toInt())
        )

        DurakAuthSession.initialize(applicationContext)

        startVpsUpdateCheck()

        enableEdgeToEdge()

        setContent {

            DurakScoreTheme {

                DurakometrMaterialTheme {

                    Box(
                        modifier =
                            Modifier.fillMaxSize()
                    ) {

                        var splashFinished by rememberSaveable {
                            mutableStateOf(false)
                        }

                        LaunchedEffect(Unit) {
                            if (!splashFinished) {
                                delay(2_000L)
                                splashFinished = true
                            }
                        }

                        if (!splashFinished) {
                            AppStartupSplash()
                        } else {

                            var currentUser by remember {
                                mutableStateOf<DurakSession?>(
                                    DurakAuthSession.current()
                                )
                            }

                            if (
                                currentUser ==
                                null
                            ) {

                                AuthScreen(
                                    onAuthSuccess = {
                                            session ->

                                        DurakAuthSession.save(
                                            session
                                        )

                                        currentUser =
                                            session
                                    }
                                )

                            } else {

                                UserAccessGate(
                                    user =
                                        currentUser!!,
                                    onLogout = {

                                        currentUser
                                            ?.let {
                                                DurakServerApi.logout(
                                                    it
                                                )
                                            }

                                        DurakAuthSession.clear()

                                        currentUser =
                                            null
                                    }
                                )
                            }
                        }

                        availableVpsRelease
                            ?.let { release ->

                                AlertDialog(
                                    onDismissRequest = {
                                        if (!release.mandatory) {
                                            availableVpsRelease =
                                                null
                                        }
                                    },
                                    title = {
                                        Text(
                                            "Доступно обновление"
                                        )
                                    },
                                    text = {
                                        Column {
                                            Text(
                                                "Версия ${release.versionName}"
                                            )

                                            if (
                                                release.notes
                                                    .isNotBlank()
                                            ) {
                                                Spacer(
                                                    modifier =
                                                        Modifier.height(
                                                            12.dp
                                                        )
                                                )

                                                Text(
                                                    release.notes
                                                )
                                            }
                                        }
                                    },
                                    confirmButton = {
                                        Button(
                                            onClick = {
                                                openUpdateInBrowser(
                                                    release.apkUrl
                                                )
                                            }
                                        ) {
                                            Text(
                                                "Скачать обновление"
                                            )
                                        }
                                    },
                                    dismissButton = {
                                        if (
                                            !release.mandatory
                                        ) {
                                            TextButton(
                                                onClick = {
                                                    availableVpsRelease =
                                                        null
                                                }
                                            ) {
                                                Text(
                                                    "Позже"
                                                )
                                            }
                                        }
                                    }
                                )
                            }
                    }
                }
            }
        }
    }

    private fun startVpsUpdateCheck() {

        if (
            !vpsUpdateStarted
                .compareAndSet(
                    false,
                    true
                )
        ) {
            return
        }

        thread(
            name =
                "DurakVpsUpdateCheck",
            isDaemon =
                true
        ) {

            try {

                val release =
                    loadVpsRelease()

                val currentCode =
                    currentVersionCode()

                Log.d(
                    UPDATE_LOG_TAG,
                    "VPS version=${release.versionName} (${release.versionCode}), current=$currentCode"
                )

                if (
                    release.versionCode <=
                    currentCode
                ) {
                    return@thread
                }

                runOnUiThread {
                    availableVpsRelease =
                        release
                }

            } catch (
                error: Throwable
            ) {

                Log.e(
                    UPDATE_LOG_TAG,
                    "VPS update check failed",
                    error
                )

                runOnUiThread {

                    Toast.makeText(
                        this@MainActivity,
                        "Не удалось проверить обновление на сервере",
                        Toast.LENGTH_LONG
                    ).show()
                }
            }
        }
    }


    private fun loadVpsRelease(): VpsRelease {

        val connection =
            URL(VPS_LATEST_URL)
                .openConnection() as HttpURLConnection

        try {
            connection.requestMethod =
                "GET"

            connection.connectTimeout =
                10_000

            connection.readTimeout =
                10_000

            connection.useCaches =
                false

            connection.setRequestProperty(
                "Cache-Control",
                "no-cache"
            )

            connection.setRequestProperty(
                "Pragma",
                "no-cache"
            )

            connection.setRequestProperty(
                "Accept",
                "application/json"
            )

            val responseCode =
                connection.responseCode

            if (
                responseCode !in
                200..299
            ) {
                error(
                    "latest.json HTTP $responseCode"
                )
            }

            val jsonText =
                connection
                    .inputStream
                    .bufferedReader()
                    .use {
                        it.readText()
                    }

            val json =
                JSONObject(jsonText)

            val versionCode =
                json.optLong(
                    "versionCode",
                    -1L
                )

            val versionName =
                json.optString(
                    "versionName",
                    ""
                )
                    .trim()

            val apkUrl =
                json.optString(
                    "apkUrl",
                    ""
                )
                    .trim()

            if (versionCode < 1L) {
                error("Некорректный versionCode в latest.json")
            }

            if (versionName.isBlank()) {
                error("Нет versionName в latest.json")
            }

            if (apkUrl.isBlank()) {
                error("Нет apkUrl в latest.json")
            }

            return VpsRelease(
                versionCode =
                    versionCode,
                versionName =
                    versionName,
                apkUrl =
                    apkUrl,
                mandatory =
                    json.optBoolean(
                        "mandatory",
                        false
                    ),
                notes =
                    json.optString(
                        "notes",
                        ""
                    )
            )

        } finally {
            connection.disconnect()
        }
    }

    private fun currentVersionCode(): Long {

        val packageInfo =
            packageManager.getPackageInfo(
                packageName,
                0
            )

        return if (
            Build.VERSION.SDK_INT >=
            Build.VERSION_CODES.P
        ) {
            packageInfo.longVersionCode
        } else {
            @Suppress("DEPRECATION")
            packageInfo.versionCode.toLong()
        }
    }

    private fun openUpdateInBrowser(
        apkUrl: String
    ) {

        try {

            val uri =
                Uri.parse(
                    apkUrl
                )

            if (
                !uri.scheme.equals(
                    "https",
                    ignoreCase = true
                )
            ) {
                error(
                    "Update URL must use HTTPS"
                )
            }

            startActivity(
                Intent(
                    Intent.ACTION_VIEW,
                    uri
                )
            )

            availableVpsRelease =
                null

        } catch (
            error: Throwable
        ) {

            Log.e(
                UPDATE_LOG_TAG,
                "Failed to open update URL",
                error
            )

            Toast.makeText(
                this,
                "Не удалось открыть ссылку на обновление",
                Toast.LENGTH_LONG
            ).show()
        }
    }

    private data class VpsRelease(
        val versionCode: Long,
        val versionName: String,
        val apkUrl: String,
        val mandatory: Boolean,
        val notes: String
    )

}

@Composable
private fun AppStartupSplash() {

    /*
     * Отдельная полноэкранная картинка, а не launcher icon.
     * Файл должен лежать здесь:
     * app/src/main/res/drawable/splash_durakometr.png
     */
    Box(
        modifier =
            Modifier
                .fillMaxSize()
                .background(
                    Color.Black
                )
    ) {

        Image(
            painter =
                painterResource(
                    id =
                        R.drawable.splash_durakometr
                ),
            contentDescription =
                "Дуракометр",
            // Не режем арт на телефонах с немного разным соотношением сторон.
            // На типичном 9:20 он занимает весь экран без полос.
            contentScale =
                ContentScale.Fit,
            modifier =
                Modifier.fillMaxSize()
        )
    }
}

enum class AppScreen {
    MENU,
    NEW_GAME,
    GAME,
    USERS,
    HISTORY,
    ACHIEVEMENTS,
    LIVE_GAME,
    FINAL_GAME,
    MAIN_SCORE,
    AUDIT_LOG,
    DATA_AUDIT
}

@Composable
fun DurakScoreApp(
    access: UserAccess,
    onLogout: () -> Unit
) {
    var isGameActionPending by remember {
        mutableStateOf(false)
    }
    var pendingFinishLoserIndex by remember {
        mutableStateOf<Int?>(null)
    }

    var finishRequestSent by remember {
        mutableStateOf(false)
    }
    var isFinishingGame by remember {
        mutableStateOf(false)
    }
    var isRestoringGame by remember {
        mutableStateOf(true)
    }
    val gameActionQueue = remember {
        mutableStateListOf<QueuedGameAction>()
    }

    var isProcessingGameQueue by remember {
        mutableStateOf(false)
    }

    var gameActionError by remember {
        mutableStateOf<String?>(null)
    }
    var currentGameId by remember {
        mutableStateOf<String?>(null)
    }

    var finalGameId by remember {
        mutableStateOf<String?>(null)
    }

    var isCreatingGame by remember {
        mutableStateOf(false)
    }

    var gameCreationError by remember {
        mutableStateOf<String?>(null)
    }

    var gameStartedAt by remember {
        mutableStateOf<Timestamp?>(null)
    }

    var isResumedGame by remember {
        mutableStateOf(false)
    }

    var currentGamePlayers by remember {
        mutableStateOf<List<GamePlayer>>(emptyList())
    }

    var currentGameType by remember {
        mutableStateOf<GameType?>(null)
    }

    var currentScreen by remember {
        mutableStateOf(AppScreen.MENU)
    }

    var targetScore by remember {
        mutableIntStateOf(15)
    }

    val scores = remember {
        mutableStateListOf<Int>()
    }

    val roundHistory = remember {
        mutableStateListOf<Int>()
    }

    var gameActive by remember {
        mutableStateOf(false)
    }

    var loserIndex by remember {
        mutableStateOf<Int?>(null)
    }

    var showCancelGameDialog by remember {
        mutableStateOf(false)
    }

    var cancelGameText by remember {
        mutableStateOf("")
    }

    var isCancelingGame by remember {
        mutableStateOf(false)
    }

    // Крупный текст для Дениса и Дмитрия.
    // Настройка сохраняется локально на конкретном телефоне.
    val context = LocalContext.current
    val uiPrefs = remember {
        context.getSharedPreferences(
            "durakscore_ui",
            Context.MODE_PRIVATE
        )
    }

    val canUseLargeText =
        access.playerId == "denis" ||
                access.playerId == "dmitry"

    var largeTextEnabled by remember(access.playerId) {
        mutableStateOf(
            if (canUseLargeText) {
                uiPrefs.getBoolean(
                    "large_text_${access.playerId}",
                    false
                )
            } else {
                false
            }
        )
    }

    val baseDensity = LocalDensity.current
    fun applyServerGameState(
        serverScores: Map<String, Int>,
        serverHistory: List<String>
    ) {

        scores.clear()

        currentGamePlayers.forEach { player ->

            scores.add(
                serverScores[player.id] ?: 0
            )

        }

        val restoredLoserIndex =
            currentGamePlayers.indexOfFirst { player ->

                (serverScores[player.id] ?: 0) >=
                        targetScore
            }

        loserIndex =
            if (restoredLoserIndex >= 0) {
                restoredLoserIndex
            } else {
                null
            }



        roundHistory.clear()

        serverHistory.forEach { playerId ->

            val index =
                currentGamePlayers.indexOfFirst {
                    it.id == playerId
                }

            if (index >= 0) {
                roundHistory.add(index)
            }
        }
    }


    fun syncGameActionQueue() {

        if (
            isProcessingGameQueue ||
            gameActionQueue.isEmpty()
        ) {
            return
        }

        val gameId =
            currentGameId ?: return


        // Берём всё, что успели натапать.
        val actionsToSend =
            gameActionQueue.toList()

        isProcessingGameQueue = true


        GameRepository.applyActions(

            gameId = gameId,

            actions = actionsToSend,

            onSuccess = {

                // Убираем из очереди именно
                // те действия, которые сервер подтвердил.
                repeat(actionsToSend.size) {

                    if (gameActionQueue.isNotEmpty()) {
                        gameActionQueue.removeAt(0)
                    }
                }

                isProcessingGameQueue = false


                // Пока сервер сохранял первую пачку,
                // пользователь мог натапать ещё.
                if (gameActionQueue.isNotEmpty()) {
                    syncGameActionQueue()
                }
            },

            onError = { message ->

                // Что-то пошло не так.
                // Выкидываем неподтверждённые
                // optimistic-действия и читаем
                // настоящий счёт с сервера.

                gameActionQueue.clear()
                isProcessingGameQueue = false


                GameRepository.loadState(

                    gameId = gameId,

                    onSuccess = {
                            serverScores,
                            serverHistory ->

                        applyServerGameState(
                            serverScores,
                            serverHistory
                        )

                        loserIndex = null

                        gameActionError =
                            "Не удалось сохранить изменения. Счёт восстановлен из базы."
                    },

                    onError = {

                        gameActionError =
                            message
                    }
                )
            }
        )
    }
    fun applyRemoteUnfinishedGame(
        game: LoadedGame?
    ) {

        /*
         * Эта функция используется и при первом запуске,
         * и для фоновой синхронизации меню.
         *
         * ВАЖНО:
         * не трогаем локальное состояние, пока ведущий
         * находится за игровым столом / отправляет очередь действий.
         * Фоновая синхронизация нужна другому устройству прежде всего
         * для мгновенного появления/исчезновения кнопки "Продолжить".
         */
        if (
            currentScreen ==
            AppScreen.GAME ||
            isProcessingGameQueue ||
            gameActionQueue.isNotEmpty() ||
            isFinishingGame ||
            pendingFinishLoserIndex !=
            null
        ) {
            return
        }

        if (
            game !=
            null
        ) {

            currentGameId =
                game.id

            currentGameType =
                game.type

            currentGamePlayers =
                game.players

            targetScore =
                game.targetScore

            gameStartedAt =
                game.startedAt

            isResumedGame =
                true

            scores.clear()

            game.players.forEach {
                    player ->

                scores.add(
                    game.scores[
                        player.id
                    ] ?: 0
                )
            }

            val restoredLoserIndex =
                game.players
                    .indexOfFirst {
                            player ->

                        (
                            game.scores[
                                player.id
                            ] ?: 0
                        ) >=
                            game.targetScore
                    }

            loserIndex =
                if (
                    restoredLoserIndex >=
                    0
                ) {
                    restoredLoserIndex
                } else {
                    null
                }

            roundHistory.clear()

            game.history.forEach {
                    playerId ->

                val index =
                    game.players
                        .indexOfFirst {
                            it.id ==
                                playerId
                        }

                if (
                    index >=
                    0
                ) {
                    roundHistory.add(
                        index
                    )
                }
            }

            gameActive =
                true

        } else {

            /*
             * На другом телефоне партия могла только что завершиться
             * или быть отменена. Меню должно увидеть это без перезапуска.
             */
            gameActive =
                false

            currentGameId =
                null

            gameStartedAt =
                null

            isResumedGame =
                false

            currentGameType =
                null

            currentGamePlayers =
                emptyList()

            scores.clear()
            roundHistory.clear()

            loserIndex =
                null
        }
    }

    /*
     * Первоначальное восстановление текущей партии.
     */
    LaunchedEffect(
        access.playerId
    ) {

        GameRepository
            .loadLatestUnfinishedGame(
                onSuccess = {
                        game ->

                    applyRemoteUnfinishedGame(
                        game
                    )

                    isRestoringGame =
                        false
                },
                onError = {
                        message ->

                    isRestoringGame =
                        false

                    gameActionError =
                        message
                }
            )
    }

    /*
     * V1.5.1 — синхронизация состояния приложения между устройствами.
     *
     * Раньше наличие незавершённой партии проверялось только один раз
     * при входе в приложение. Поэтому если Денис создавал/завершал партию
     * на телефоне, второй клиент в главном меню узнавал об этом только
     * после перезапуска.
     *
     * Теперь, пока открыт главный экран, раз в ~1 секунду спрашиваем VPS.
     * Запрос идёт через GameRepository -> DurakServerApi, а не напрямую
     * в Firestore. При уходе из меню LaunchedEffect автоматически отменяется.
     */
    LaunchedEffect(
        access.playerId,
        currentScreen
    ) {

        if (
            currentScreen !=
            AppScreen.MENU
        ) {
            return@LaunchedEffect
        }

        while (
            true
        ) {

            delay(
                1_000L
            )

            if (
                currentScreen !=
                AppScreen.MENU ||
                isRestoringGame ||
                isCreatingGame ||
                isGameActionPending ||
                isProcessingGameQueue ||
                isFinishingGame ||
                gameActionQueue.isNotEmpty() ||
                pendingFinishLoserIndex !=
                null
            ) {
                continue
            }

            GameRepository
                .loadLatestUnfinishedGame(
                    onSuccess = {
                            game ->

                        applyRemoteUnfinishedGame(
                            game
                        )
                    },
                    onError = {
                            message ->

                        /*
                         * Фоновая синхронизация не должна пугать
                         * пользователя кратким сетевым сбоем.
                         * Следующая попытка будет через секунду.
                         */
                        Log.w(
                            "DurakScore",
                            "Background game-state sync failed: $message"
                        )
                    }
                )
        }
    }
    if (isRestoringGame) {

        Column(
            modifier = Modifier.fillMaxSize(),
            horizontalAlignment =
                Alignment.CenterHorizontally,
            verticalArrangement =
                Arrangement.Center
        ) {

            CircularProgressIndicator()

            Spacer(
                modifier =
                    Modifier.height(16.dp)
            )

            Text(
                text =
                    "Проверяю текущую партию..."
            )
        }

        return
    }
    LaunchedEffect(
        isProcessingGameQueue,
        gameActionQueue.size,
        pendingFinishLoserIndex,
        finishRequestSent
    ) {

        val finishIndex =
            pendingFinishLoserIndex

        if (
            finishIndex != null &&
            !isProcessingGameQueue &&
            gameActionQueue.isEmpty() &&
            !finishRequestSent
        ) {

            val gameId =
                currentGameId

            if (gameId == null) {

                isFinishingGame = false
                pendingFinishLoserIndex = null

                gameActionError =
                    "Не удалось определить текущую партию"

                return@LaunchedEffect
            }

            val loserPlayer =
                currentGamePlayers[
                    finishIndex
                ]

            finishRequestSent = true


            GameRepository.finishGame(

                gameId = gameId,

                loserId =
                    loserPlayer.id,

                finishedByUid =
                    DurakAuthSession.current()
                        ?.uid
                        .orEmpty(),

                finishedByName =
                    access.name,

                onSuccess = {

                    isFinishingGame = false
                    finishRequestSent = false

                    pendingFinishLoserIndex =
                        null

                    gameActive = false

                    finalGameId =
                        gameId

                    loserIndex =
                        null

                    currentScreen =
                        AppScreen.FINAL_GAME

                    currentGameId = null
                    gameStartedAt = null
                    isResumedGame = false
                    currentGameType = null
                    currentGamePlayers =
                        emptyList()

                    scores.clear()
                    roundHistory.clear()
                    gameActionQueue.clear()
                },

                onError = { message ->

                    isFinishingGame = false
                    finishRequestSent = false

                    pendingFinishLoserIndex =
                        null

                    // Возвращаем пользователя
                    // обратно в игру.
                    gameActive = true

                    finalGameId =
                        null

                    loserIndex =
                        finishIndex

                    currentScreen =
                        AppScreen.GAME

                    gameActionError =
                        "Не удалось завершить партию: $message"
                }
            )
        }
    }
    // Системная кнопка Android «Назад» на внутренних экранах
    // возвращает в меню, а не закрывает приложение.
    BackHandler(
        enabled =
            currentScreen != AppScreen.MENU &&
                    loserIndex == null &&
                    !showCancelGameDialog
    ) {
        currentScreen = AppScreen.MENU
    }

    CompositionLocalProvider(
        LocalDensity provides Density(
            density = baseDensity.density,
            // Системный крупный шрифт и наш режим «Крупный текст» не должны
            // перемножаться. Иначе, например, 1.30 × 1.35 превращается в 1.75
            // и ломает даже нормальные адаптивные карточки. Наш переключатель
            // теперь гарантирует минимум 1.35, но уважает ещё более крупный
            // системный fontScale пользователя.
            fontScale =
                if (largeTextEnabled) {
                    maxOf(baseDensity.fontScale, 1.35f)
                } else {
                    baseDensity.fontScale
                }
        )
    ) {

        AnimatedContent(
            modifier =
                Modifier
                    .fillMaxSize()
                    .background(
                        PokerPalette.BackgroundTop
                    ),
            targetState =
                currentScreen,
            transitionSpec = {
                fadeIn(
                    animationSpec =
                        tween(160)
                ) togetherWith
                fadeOut(
                    animationSpec =
                        tween(120)
                )
            },
            label =
                "screen_transition"
        ) { screen ->

        when (screen) {

            AppScreen.DATA_AUDIT -> {

                DataAuditScreen(
                    access = access,
                    onBack = {
                        currentScreen = AppScreen.MENU
                    }
                )
            }

            AppScreen.AUDIT_LOG -> {

                AuditLogScreen(
                    access = access,
                    onBack = {
                        currentScreen = AppScreen.MENU
                    }
                )
            }

            AppScreen.MAIN_SCORE -> {

                MainScoreScreen(
                    access = access,
                    onBack = {
                        currentScreen = AppScreen.MENU
                    }
                )
            }

            AppScreen.LIVE_GAME -> {

                LiveGameScreen(
                    access = access,
                    onResultReached = { gameId ->

                        finalGameId =
                            gameId

                        currentScreen =
                            AppScreen.FINAL_GAME
                    },
                    onBack = {
                        currentScreen =
                            AppScreen.MENU
                    }
                )
            }

            AppScreen.FINAL_GAME -> {

                val resultGameId =
                    finalGameId

                if (
                    resultGameId == null
                ) {
                    LaunchedEffect(Unit) {
                        currentScreen =
                            AppScreen.MENU
                    }
                } else {

                    val localLoserIndex =
                        loserIndex

                    val preview =
                        if (
                            currentGameId ==
                                resultGameId &&
                            localLoserIndex != null &&
                            localLoserIndex in
                                currentGamePlayers.indices
                        ) {

                            FinalGamePreview(
                                id =
                                    resultGameId,
                                type =
                                    if (
                                        currentGameType ==
                                        GameType.CLASSIC
                                    ) {
                                        "classic"
                                    } else {
                                        "custom"
                                    },
                                targetScore =
                                    targetScore,
                                players =
                                    currentGamePlayers,
                                scores =
                                    currentGamePlayers
                                        .mapIndexed {
                                                index,
                                                player ->
                                            player.id to
                                                (
                                                    scores
                                                        .getOrNull(
                                                            index
                                                        )
                                                        ?: 0
                                                    )
                                        }
                                        .toMap(),
                                history =
                                    roundHistory
                                        .mapNotNull {
                                            index ->
                                            currentGamePlayers
                                                .getOrNull(
                                                    index
                                                )
                                                ?.id
                                        },
                                startedAt =
                                    gameStartedAt,
                                loserId =
                                    currentGamePlayers[
                                        localLoserIndex
                                    ].id
                            )
                        } else {
                            null
                        }

                    val controlsAvailable =
                        (
                            access.role ==
                                "admin" ||
                            access.role ==
                                "scorekeeper"
                            ) &&
                        currentGameId ==
                            resultGameId &&
                        localLoserIndex != null

                    FinalGameScreen(
                        gameId =
                            resultGameId,
                        preview =
                            preview,
                        showResultControls =
                            controlsAvailable,
                        canConfirmResult =
                            controlsAvailable &&
                            !isProcessingGameQueue &&
                            gameActionQueue.isEmpty() &&
                            !isGameActionPending &&
                            !isFinishingGame,
                        isFinishingResult =
                            isFinishingGame,
                        onConfirmResult = {

                            val currentLoserIndex =
                                loserIndex

                            if (
                                currentLoserIndex !=
                                    null &&
                                !isFinishingGame
                            ) {

                                pendingFinishLoserIndex =
                                    currentLoserIndex

                                finishRequestSent =
                                    false

                                isFinishingGame =
                                    true
                            }
                        },
                        onUndoLastPoint = {

                            if (
                                !isFinishingGame &&
                                roundHistory
                                    .isNotEmpty()
                            ) {

                                val lastPlayerIndex =
                                    roundHistory
                                        .removeAt(
                                            roundHistory
                                                .lastIndex
                                        )

                                if (
                                    scores[
                                        lastPlayerIndex
                                    ] > 0
                                ) {
                                    scores[
                                        lastPlayerIndex
                                    ]--
                                }

                                loserIndex =
                                    null

                                finalGameId =
                                    null

                                gameActionQueue.add(
                                    QueuedGameAction(
                                        type =
                                            GameActionType
                                                .UNDO_LAST
                                    )
                                )

                                syncGameActionQueue()

                                currentScreen =
                                    AppScreen.GAME
                            }
                        },
                        onResultRolledBack = {

                            finalGameId =
                                null

                            currentScreen =
                                AppScreen.LIVE_GAME
                        },
                        onBackToMenu = {

                            finalGameId =
                                null

                            currentScreen =
                                AppScreen.MENU
                        }
                    )
                }
            }

            AppScreen.HISTORY -> {

                HistoryScreen(
                    access =
                        access,
                    onBack = {
                        currentScreen =
                            AppScreen.MENU
                    }
                )
            }

            AppScreen.ACHIEVEMENTS -> {

                AchievementsScreen(
                    access =
                        access,
                    onBack = {
                        currentScreen =
                            AppScreen.MENU
                    }
                )
            }

            AppScreen.USERS -> {

                AdminUsersScreen(
                    access = access,

                    onBack = {
                        currentScreen = AppScreen.MENU
                    }
                )
            }

            AppScreen.MENU -> {

                MainMenuScreen(

                    onMainScore = {
                        currentScreen = AppScreen.MAIN_SCORE
                    },

                    onAuditLog = {
                        currentScreen = AppScreen.AUDIT_LOG
                    },

                    onDataAudit = {
                        currentScreen = AppScreen.DATA_AUDIT
                    },

                    onLiveGame = {
                        currentScreen =
                            AppScreen.LIVE_GAME
                    },
                    onHistory = {
                        currentScreen =
                            AppScreen.HISTORY
                    },

                    onAchievements = {
                        currentScreen =
                            AppScreen.ACHIEVEMENTS
                    },

                    isFinishingGame = isFinishingGame,
                    largeTextEnabled = largeTextEnabled,
                    onToggleLargeText = {
                        val newValue = !largeTextEnabled
                        largeTextEnabled = newValue
                        uiPrefs.edit()
                            .putBoolean(
                                "large_text_${access.playerId}",
                                newValue
                            )
                            .apply()
                    },
                    access = access,
                    gameActive = gameActive,

                    onUsers = {
                        currentScreen = AppScreen.USERS
                    },

                    onNewGame = {
                        currentScreen = AppScreen.NEW_GAME
                    },

                    onContinueGame = {

                        val gameId = currentGameId

                        if (
                            gameId != null &&
                            !isGameActionPending
                        ) {

                            isGameActionPending = true
                            gameActionError = null

                            fun openCurrentGame() {
                                isGameActionPending = false
                                isResumedGame = true
                                gameActive = true

                                val reachedLimitIndex =
                                    scores.indexOfFirst {
                                        it >= targetScore
                                    }

                                loserIndex =
                                    if (reachedLimitIndex >= 0) {
                                        reachedLimitIndex
                                    } else {
                                        null
                                    }

                                if (
                                    reachedLimitIndex >= 0
                                ) {
                                    finalGameId =
                                        gameId

                                    currentScreen =
                                        AppScreen.FINAL_GAME
                                } else {
                                    currentScreen =
                                        AppScreen.GAME
                                }
                            }

                            // Если партия всё ещё active — просто открываем экран.
                            // Только paused-партию реально переводим обратно в active.
                            GameRepository.loadGameStatus(
                                gameId = gameId,
                                onSuccess = { status ->
                                    when (status) {
                                        "active" -> {
                                            openCurrentGame()
                                        }

                                        "paused" -> {
                                            GameRepository.resumeGame(
                                                gameId = gameId,
                                                onSuccess = {
                                                    openCurrentGame()
                                                },
                                                onError = { message ->
                                                    isGameActionPending = false
                                                    gameActionError = message
                                                }
                                            )
                                        }

                                        else -> {
                                            isGameActionPending = false
                                            gameActive = false
                                            gameActionError =
                                                "Текущая партия уже недоступна"
                                        }
                                    }
                                },
                                onError = { message ->
                                    isGameActionPending = false
                                    gameActionError = message
                                }
                            )
                        }
                    },

                    onLogout = onLogout
                )
            }

            AppScreen.NEW_GAME -> {

                NewGameSetupScreen(

                    onBack = {
                        if (!isCreatingGame) {
                            currentScreen = AppScreen.MENU
                        }
                    },

                    onStartGame = { setup ->

                        if (!isCreatingGame) {

                            isCreatingGame = true
                            gameCreationError = null

                            GameRepository.createGame(

                                setup = setup,

                                onSuccess = { game ->

                                    currentGameId =
                                        game.id

                                    gameStartedAt =
                                        game.startedAt

                                    isResumedGame =
                                        false

                                    currentGamePlayers =
                                        game.players

                                    currentGameType =
                                        game.type

                                    targetScore =
                                        game.targetScore

                                    scores.clear()

                                    game.players.forEach {
                                            player ->

                                        scores.add(
                                            game.scores[
                                                player.id
                                            ] ?: 0
                                        )
                                    }

                                    roundHistory.clear()

                                    game.history.forEach {
                                            playerId ->

                                        val index =
                                            game.players
                                                .indexOfFirst {
                                                    it.id ==
                                                        playerId
                                                }

                                        if (
                                            index >=
                                            0
                                        ) {
                                            roundHistory.add(
                                                index
                                            )
                                        }
                                    }

                                    loserIndex =
                                        null

                                    gameActive =
                                        true

                                    isCreatingGame =
                                        false

                                    currentScreen =
                                        AppScreen.GAME
                                },

                                onError = {
                                        message ->

                                    isCreatingGame =
                                        false

                                    gameCreationError =
                                        message
                                }
                            )
                        }
                    }
                )
            }

            AppScreen.GAME -> {

                GameScreen(
                    gameId = currentGameId,
                    startedAt = gameStartedAt,
                    isResumedGame = isResumedGame,
                    playerIds = currentGamePlayers.map {
                        it.id
                    },
                    playerNames = currentGamePlayers.map {
                        it.name
                    },
                    scores = scores,
                    targetScore = targetScore,
                    history = roundHistory,
                    reactionSenderName =
                        access.name,

                    onAddPoint = { playerIndex ->

                        val alreadyFinishedIndex =
                            scores.indexOfFirst {
                                it >= targetScore
                            }

                        if (alreadyFinishedIndex >= 0) {

                            // Уже есть проигравший.
                            // Никаких дополнительных очков.
                            loserIndex =
                                alreadyFinishedIndex

                            finalGameId =
                                currentGameId

                            currentScreen =
                                AppScreen.FINAL_GAME

                        } else {

                            val player =
                                currentGamePlayers[playerIndex]

                            scores[playerIndex]++

                            roundHistory.add(
                                playerIndex
                            )

                            gameActionQueue.add(
                                QueuedGameAction(
                                    type =
                                        GameActionType.ADD_POINT,
                                    playerId =
                                        player.id
                                )
                            )

                            syncGameActionQueue()

                            if (
                                scores[playerIndex] >=
                                targetScore
                            ) {
                                loserIndex =
                                    playerIndex

                                finalGameId =
                                    currentGameId

                                currentScreen =
                                    AppScreen.FINAL_GAME
                            }
                        }
                    },

                    onUndo = {

                        if (roundHistory.isNotEmpty()) {

                            // Сразу исправляем экран.
                            val lastPlayerIndex =
                                roundHistory.removeAt(
                                    roundHistory.lastIndex
                                )


                            if (
                                scores[lastPlayerIndex] > 0
                            ) {

                                scores[lastPlayerIndex]--
                            }


                            loserIndex = null


                            // Сервер догонит нас в фоне.
                            gameActionQueue.add(
                                QueuedGameAction(
                                    type =
                                        GameActionType.UNDO_LAST
                                )
                            )


                            syncGameActionQueue()
                        }
                    },

                    onPause = {

                        if (
                            isProcessingGameQueue ||
                            gameActionQueue.isNotEmpty()
                        ) {

                            gameActionError =
                                "Сохраняю последние очки. Попробуй ещё раз через секунду."

                        } else {

                            val gameId =
                                currentGameId

                            if (
                                gameId != null &&
                                !isGameActionPending
                            ) {

                                isGameActionPending = true
                                gameActionError = null

                                GameRepository.pauseGame(

                                    gameId = gameId,

                                    onSuccess = {

                                        isGameActionPending = false
                                        gameActive = true

                                        currentScreen =
                                            AppScreen.MENU
                                    },

                                    onError = { message ->

                                        isGameActionPending = false
                                        gameActionError = message
                                    }
                                )
                            }
                        }
                    },

                    onChangeTargetScore = { newTarget ->

                        if (
                            newTarget !in 2..99 ||
                            isProcessingGameQueue ||
                            gameActionQueue.isNotEmpty() ||
                            isGameActionPending
                        ) {
                            if (newTarget !in 2..99) {
                                gameActionError =
                                    "Лимит должен быть от 2 до 99"
                            } else {
                                gameActionError =
                                    "Сохраняю последние изменения. Попробуй сменить лимит через секунду."
                            }
                        } else {
                            val gameId = currentGameId

                            if (gameId != null) {
                                isGameActionPending = true
                                gameActionError = null

                                GameRepository.updateTargetScore(
                                    gameId = gameId,
                                    newTargetScore = newTarget,
                                    onSuccess = {
                                        targetScore = newTarget
                                        isGameActionPending = false

                                        val reachedLimitIndex =
                                            scores.indexOfFirst {
                                                it >= newTarget
                                            }

                                        loserIndex =
                                            if (reachedLimitIndex >= 0) {
                                                reachedLimitIndex
                                            } else {
                                                null
                                            }

                                        if (
                                            reachedLimitIndex >= 0
                                        ) {
                                            finalGameId =
                                                gameId

                                            currentScreen =
                                                AppScreen.FINAL_GAME
                                        }
                                    },
                                    onError = { message ->
                                        isGameActionPending = false
                                        gameActionError = message
                                    }
                                )
                            }
                        }
                    },

                    onBackToMenu = {
                        currentScreen = AppScreen.MENU
                    },

                    onCancelGame = {
                        if (
                            isProcessingGameQueue ||
                            gameActionQueue.isNotEmpty() ||
                            isGameActionPending
                        ) {
                            gameActionError =
                                "Сохраняю последние изменения. Попробуй отменить партию через секунду."
                        } else {
                            cancelGameText = ""
                            showCancelGameDialog = true
                        }
                    }
                )
            }
        }
        }

        if (gameActionError != null) {

            AlertDialog(
                onDismissRequest = {
                    gameActionError = null
                },
                title = {
                    Text("Ошибка")
                },
                text = {
                    Text(gameActionError!!)
                },
                confirmButton = {
                    Button(
                        onClick = {
                            gameActionError = null
                        }
                    ) {
                        Text("ОК")
                    }
                }
            )
        }

        if (showCancelGameDialog) {

            AlertDialog(
                onDismissRequest = {
                    if (!isCancelingGame) {
                        showCancelGameDialog = false
                        cancelGameText = ""
                    }
                },
                title = {
                    Text("Отменить партию?")
                },
                text = {
                    Column {
                        Text(
                            text = "Партия будет удалена и не попадёт в историю. Информация об отмене останется в журнале."
                        )

                        Spacer(
                            modifier = Modifier.height(18.dp)
                        )

                        Text(
                            text = "Для подтверждения введите «подтвердить»",
                            fontWeight = FontWeight.Bold
                        )

                        Spacer(
                            modifier = Modifier.height(8.dp)
                        )

                        OutlinedTextField(
                            value = cancelGameText,
                            onValueChange = { cancelGameText = it },
                            label = { Text("Подтверждение") },
                            singleLine = true,
                            enabled = !isCancelingGame
                        )
                    }
                },
                confirmButton = {
                    val confirmed =
                        cancelGameText.trim().lowercase() == "подтвердить"

                    Button(
                        enabled = confirmed && !isCancelingGame,
                        onClick = {
                            val gameId = currentGameId

                            if (gameId != null) {
                                isCancelingGame = true
                                gameActionError = null

                                GameRepository.cancelGame(
                                    gameId = gameId,
                                    cancelledByUid = DurakAuthSession.current()
                                        ?.uid
                                        .orEmpty(),
                                    cancelledByName = access.name,
                                    onSuccess = {
                                        isCancelingGame = false
                                        showCancelGameDialog = false
                                        cancelGameText = ""

                                        currentGameId = null
                                        gameStartedAt = null
                                        isResumedGame = false
                                        currentGameType = null
                                        currentGamePlayers = emptyList()

                                        scores.clear()
                                        roundHistory.clear()
                                        gameActionQueue.clear()

                                        loserIndex = null
                                        gameActive = false
                                        currentScreen = AppScreen.MENU
                                    },
                                    onError = { message ->
                                        isCancelingGame = false
                                        gameActionError =
                                            "Не удалось отменить партию: $message"
                                    }
                                )
                            }
                        }
                    ) {
                        Text(
                            if (isCancelingGame) {
                                "Удаляю..."
                            } else {
                                "Отменить партию"
                            }
                        )
                    }
                },
                dismissButton = {
                    TextButton(
                        enabled = !isCancelingGame,
                        onClick = {
                            showCancelGameDialog = false
                            cancelGameText = ""
                        }
                    ) {
                        Text("Назад")
                    }
                }
            )
        }


    }
}

@Composable
fun MainMenuScreen(
    access: UserAccess,
    gameActive: Boolean,
    isFinishingGame: Boolean,
    largeTextEnabled: Boolean,
    onToggleLargeText: () -> Unit,
    onNewGame: () -> Unit,
    onContinueGame: () -> Unit,
    onHistory: () -> Unit,
    onAchievements: () -> Unit,
    onLiveGame: () -> Unit,
    onLogout: () -> Unit,
    onUsers: () -> Unit,
    onMainScore: () -> Unit,
    onAuditLog: () -> Unit,
    onDataAudit: () -> Unit,
) {

    val canManageGames =
        access.role ==
            "admin" ||
            access.role ==
            "scorekeeper"

    val canSeeJournal =
        access.role ==
            "admin" ||
            access.role ==
            "scorekeeper"

    val menuFontScale =
        LocalDensity.current.fontScale

    val heroMinHeight =
        when {
            menuFontScale >= 1.55f -> 220.dp
            menuFontScale >= 1.35f -> 205.dp
            menuFontScale >= 1.18f -> 188.dp
            else -> 170.dp
        }

    val heroTextEndPadding =
        when {
            menuFontScale >= 1.45f -> 34.dp
            menuFontScale >= 1.20f -> 54.dp
            else -> 88.dp
        }

    val heroTitleSize =
        when {
            menuFontScale >= 1.45f -> 20.sp
            menuFontScale >= 1.20f -> 22.sp
            else -> 24.sp
        }

    val heroTitleLineHeight =
        when {
            menuFontScale >= 1.45f -> 22.sp
            menuFontScale >= 1.20f -> 24.sp
            else -> 26.sp
        }

    val canWatch =
        access.role ==
            "viewer" ||
            (
                canManageGames &&
                gameActive
            )

    PokerScreen {

        /*
         * Адаптивная шапка. Она больше не имеет жёсткой высоты:
         * при системном увеличении шрифта карточка растёт, а тексту
         * оставляется больше места поверх затемнённой части арта.
         */
        Card(
            modifier =
                Modifier
                    .fillMaxWidth()
                    .heightIn(
                        min = heroMinHeight
                    ),
            colors =
                CardDefaults.cardColors(
                    containerColor =
                        Color(
                            0xF20A0D0B
                        )
                ),
            border =
                BorderStroke(
                    1.dp,
                    PokerPalette.GoldDark
                ),
            shape =
                RoundedCornerShape(
                    22.dp
                )
        ) {

            Box(
                modifier =
                    Modifier
                        .fillMaxWidth()
                        .heightIn(
                            min = heroMinHeight
                        )
            ) {

                Image(
                    painter =
                        painterResource(
                            id =
                                R.drawable
                                    .menu_header_jester
                        ),
                    contentDescription =
                        null,
                    contentScale =
                        ContentScale.Fit,
                    modifier =
                        Modifier
                            .align(
                                Alignment.CenterEnd
                            )
                            .size(
                                width =
                                    188.dp,
                                height =
                                    170.dp
                            )
                )

                Box(
                    modifier =
                        Modifier
                            .align(
                                Alignment.CenterEnd
                            )
                            .size(
                                width =
                                    210.dp,
                                height =
                                    heroMinHeight
                            )
                            .background(
                                brush =
                                    androidx.compose.ui.graphics.Brush
                                        .horizontalGradient(
                                            colors =
                                                listOf(
                                                    Color(
                                                        0xF20A0D0B
                                                    ),
                                                    Color(
                                                        0x990A0D0B
                                                    ),
                                                    Color.Transparent
                                                )
                                        )
                            )
                )

                Column(
                    modifier =
                        Modifier
                            .fillMaxWidth()
                            .padding(
                                start =
                                    18.dp,
                                top =
                                    17.dp,
                                end =
                                    heroTextEndPadding,
                                bottom =
                                    14.dp
                            )
                ) {

                    Text(
                        text =
                            "ДУРАКОМЕТР",
                        color =
                            PokerPalette.TextPrimary,
                        fontSize =
                            heroTitleSize,
                        fontFamily =
                            PokerDisplayFont,
                        fontWeight =
                            FontWeight.Black,
                        maxLines =
                            2,
                        softWrap =
                            true,
                        lineHeight =
                            heroTitleLineHeight
                    )

                    Spacer(
                        modifier =
                            Modifier.height(
                                3.dp
                            )
                    )

                    Text(
                        text =
                            "Стол всё помнит.",
                        color =
                            PokerPalette.CrimsonBright,
                        fontSize =
                            14.sp,
                        fontWeight =
                            FontWeight.Bold,
                        maxLines =
                            2
                    )

                    Spacer(
                        modifier =
                            Modifier.height(
                                13.dp
                            )
                    )

                    Text(
                        text =
                            access.name,
                        color =
                            PokerPalette.Gold,
                        fontSize =
                            18.sp,
                        fontWeight =
                            FontWeight.Bold,
                        maxLines =
                            2
                    )

                    Text(
                        text =
                            if (
                                gameActive
                            ) {
                                "Партия уже идёт. Кто-то явно встрял."
                            } else {
                                "Можно начинать охоту на дурака."
                            },
                        color =
                            PokerPalette.TextSecondary,
                        fontSize =
                            11.sp,
                        lineHeight =
                            14.sp,
                        maxLines =
                            3
                    )
                }
            }
        }

        if (
            isFinishingGame
        ) {

            Spacer(
                modifier =
                    Modifier.height(
                        12.dp
                    )
            )

            PokerPanel {

                Text(
                    text =
                        "☠  Фиксируем приговор...",
                    color =
                        PokerPalette.CrimsonBright,
                    fontSize =
                        15.sp,
                    fontWeight =
                        FontWeight.Bold
                )
            }
        }

        Spacer(
            modifier =
                Modifier.height(
                    16.dp
                )
        )

        /*
         * Главное действие — не просто очередная кнопка.
         */
        PokerMenuArtTile(
            iconRes =
                R.drawable.menu_new_game,
            title =
                "НОВАЯ ПАРТИЯ",
            subtitle =
                if (
                    gameActive
                ) {
                    "Сначала закончи текущую катку."
                } else {
                    "Проверим, кто сегодня заслужит корону дурака."
                },
            onClick =
                onNewGame,
            enabled =
                canManageGames &&
                    !gameActive &&
                    !isFinishingGame,
            redAccent =
                true
        )

        if (
            canManageGames &&
            gameActive
        ) {

            Spacer(
                modifier =
                    Modifier.height(
                        10.dp
                    )
            )

            PokerMenuArtTile(
                iconRes =
                    R.drawable.menu_continue,
                title =
                    "ПРОДОЛЖИТЬ ПАРТИЮ",
                subtitle =
                    "Вернуться за стол и продолжить раздачу.",
                onClick =
                    onContinueGame,
                enabled =
                    !isFinishingGame,
                redAccent =
                    true
            )
        }

        Spacer(
            modifier =
                Modifier.height(
                    14.dp
                )
        )

        Text(
            text =
                "СТОЛ И ПОЗОР",
            color =
                PokerPalette.Gold,
            fontSize =
                13.sp,
            fontWeight =
                FontWeight.Black
        )

        Spacer(
            modifier =
                Modifier.height(
                    8.dp
                )
        )

        BoxWithConstraints(
            modifier =
                Modifier.fillMaxWidth()
        ) {

            /*
             * При очень узкой логической ширине экрана или большом
             * системном fontScale сетка 2×2 сама превращается в одну
             * колонку. На обычных настройках остаётся привычная 2×2.
             */
            val singleColumn =
                maxWidth < 340.dp ||
                    menuFontScale >= 1.55f

            if (
                singleColumn
            ) {

                Column(
                    modifier =
                        Modifier.fillMaxWidth(),
                    verticalArrangement =
                        Arrangement.spacedBy(
                            10.dp
                        )
                ) {

                    PokerMenuArtTile(
                        iconRes =
                            R.drawable.menu_score,
                        title =
                            "Общий счёт",
                        subtitle =
                            "Кто чаще всех облажался.",
                        onClick =
                            onMainScore
                    )

                    PokerMenuArtTile(
                        iconRes =
                            R.drawable.menu_achievements,
                        title =
                            "Достижения",
                        subtitle =
                            "Слава и сомнительные заслуги.",
                        onClick =
                            onAchievements
                    )

                    PokerMenuArtTile(
                        iconRes =
                            R.drawable.menu_history,
                        title =
                            "История",
                        subtitle =
                            "Архив старых преступлений.",
                        onClick =
                            onHistory
                    )

                    PokerMenuArtTile(
                        iconRes =
                            R.drawable.menu_watch,
                        title =
                            if (
                                access.role ==
                                "viewer"
                            ) {
                                "Текущая"
                            } else {
                                "Наблюдать"
                            },
                        subtitle =
                            if (
                                canWatch
                            ) {
                                "Смотреть стол вживую."
                            } else {
                                "Сейчас смотреть нечего."
                            },
                        onClick =
                            onLiveGame,
                        enabled =
                            canWatch &&
                                !isFinishingGame
                    )
                }

            } else {

                Column(
                    modifier =
                        Modifier.fillMaxWidth(),
                    verticalArrangement =
                        Arrangement.spacedBy(
                            10.dp
                        )
                ) {

                    Row(
                        modifier =
                            Modifier.fillMaxWidth(),
                        horizontalArrangement =
                            Arrangement.spacedBy(
                                10.dp
                            )
                    ) {

                        PokerMenuArtTile(
                            iconRes =
                                R.drawable.menu_score,
                            title =
                                "Общий счёт",
                            subtitle =
                                "Кто чаще всех облажался.",
                            onClick =
                                onMainScore,
                            compact =
                                true,
                            modifier =
                                Modifier.weight(
                                    1f
                                )
                        )

                        PokerMenuArtTile(
                            iconRes =
                                R.drawable.menu_achievements,
                            title =
                                "Достижения",
                            subtitle =
                                "Слава и сомнительные заслуги.",
                            onClick =
                                onAchievements,
                            compact =
                                true,
                            modifier =
                                Modifier.weight(
                                    1f
                                )
                        )
                    }

                    Row(
                        modifier =
                            Modifier.fillMaxWidth(),
                        horizontalArrangement =
                            Arrangement.spacedBy(
                                10.dp
                            )
                    ) {

                        PokerMenuArtTile(
                            iconRes =
                                R.drawable.menu_history,
                            title =
                                "История",
                            subtitle =
                                "Архив старых преступлений.",
                            onClick =
                                onHistory,
                            compact =
                                true,
                            modifier =
                                Modifier.weight(
                                    1f
                                )
                        )

                        PokerMenuArtTile(
                            iconRes =
                                R.drawable.menu_watch,
                            title =
                                if (
                                    access.role ==
                                    "viewer"
                                ) {
                                    "Текущая"
                                } else {
                                    "Наблюдать"
                                },
                            subtitle =
                                if (
                                    canWatch
                                ) {
                                    "Смотреть стол вживую."
                                } else {
                                    "Сейчас смотреть нечего."
                                },
                            onClick =
                                onLiveGame,
                            enabled =
                                canWatch &&
                                    !isFinishingGame,
                            compact =
                                true,
                            modifier =
                                Modifier.weight(
                                    1f
                                )
                        )
                    }
                }
            }
        }

        if (
            canSeeJournal ||
            access.role ==
            "admin"
        ) {

            Spacer(
                modifier =
                    Modifier.height(
                        16.dp
                    )
            )

            Text(
                text =
                    "СЛУЖЕБНОЕ",
                color =
                    PokerPalette.CrimsonBright,
                fontSize =
                    12.sp,
                fontWeight =
                    FontWeight.Black
            )

            Spacer(
                modifier =
                    Modifier.height(
                        8.dp
                    )
            )
        }

        if (
            canSeeJournal
        ) {

            PokerMenuArtTile(
                iconRes =
                    R.drawable.menu_audit,
                title =
                    "Журнал действий",
                subtitle =
                    "Кто, когда и что нахимичил.",
                onClick =
                    onAuditLog
            )
        }

        if (
            access.role ==
            "admin"
        ) {

            Spacer(
                modifier =
                    Modifier.height(
                        9.dp
                    )
            )

            PokerMenuArtTile(
                iconRes =
                    R.drawable.menu_users,
                title =
                    "Пользователи",
                subtitle =
                    "Допуски к этому балагану.",
                onClick =
                    onUsers
            )

            Spacer(
                modifier =
                    Modifier.height(
                        9.dp
                    )
            )

            PokerSecondaryButton(
                text =
                    "🔎  Проверка данных",
                onClick =
                    onDataAudit
            )
        }

        if (
            access.playerId ==
            "denis" ||
            access.playerId ==
            "dmitry"
        ) {

            Spacer(
                modifier =
                    Modifier.height(
                        12.dp
                    )
            )

            PokerSecondaryButton(
                text =
                    if (
                        largeTextEnabled
                    ) {
                        "Aa  Крупный текст: ВКЛ"
                    } else {
                        "Aa  Крупный текст"
                    },
                onClick =
                    onToggleLargeText
            )
        }

        Spacer(
            modifier =
                Modifier.height(
                    16.dp
                )
        )

        PokerDangerButton(
            text =
                "🚪  Выйти из аккаунта",
            onClick =
                onLogout
        )

        Spacer(
            modifier =
                Modifier.height(
                    19.dp
                )
        )

        Text(
            text =
                "♠     ♥     ♦     ♣",
            color =
                PokerPalette.Crimson,
            fontSize =
                17.sp,
            fontWeight =
                FontWeight.Bold,
            modifier =
                Modifier.align(
                    Alignment.CenterHorizontally
                )
        )

        Spacer(
            modifier =
                Modifier.height(
                    8.dp
                )
        )

        val versionContext =
            LocalContext.current

        val versionName =
            remember(
                versionContext
            ) {
                runCatching {
                    versionContext
                        .packageManager
                        .getPackageInfo(
                            versionContext.packageName,
                            0
                        )
                        .versionName
                }
                    .getOrNull()
                    ?: "?"
            }

        Text(
            text =
                "Дуракометр • v$versionName",
            color =
                Color(
                    0xFF606763
                ),
            fontSize =
                10.sp,
            modifier =
                Modifier.align(
                    Alignment.CenterHorizontally
                )
        )
    }
}

@Composable
fun GameScreen(
    gameId: String?,
    playerIds: List<String>,
    playerNames: List<String>,
    scores: List<Int>,
    targetScore: Int,
    history: List<Int>,
    reactionSenderName: String,
    onAddPoint: (Int) -> Unit,
    onUndo: () -> Unit,
    onPause: () -> Unit,
    onChangeTargetScore: (Int) -> Unit,
    onBackToMenu: () -> Unit,
    onCancelGame: () -> Unit,
    startedAt: Timestamp?,
    isResumedGame: Boolean
) {
    var showTargetDialog by remember {
        mutableStateOf(false)
    }

    var targetText by remember(targetScore) {
        mutableStateOf(targetScore.toString())
    }

    val view = LocalView.current

    val lastPlayerIndex = history.lastOrNull()
    val lastPlayerName =
        lastPlayerIndex?.let { index ->
            playerNames.getOrNull(index)
        }

    val timeText = when {
        startedAt == null -> null
        isResumedGame ->
            "Доигрываем от ${formatMoscowDateTime(startedAt)}"
        else ->
            "Начало: ${formatMoscowDateTime(startedAt)}"
    }

    val subtitle =
        if (timeText != null) {
            "До $targetScore  •  $timeText"
        } else {
            "Играем до $targetScore"
        }

    Box(
        modifier =
            Modifier.fillMaxSize()
    ) {

        PokerScreen {
            GothicGameTableHero(
                targetScore = targetScore,
                subtitle = subtitle
            )

            Spacer(modifier = Modifier.height(10.dp))

            Text(
                text = "КТО СЕГОДНЯ ДУРАК",
                color = PokerPalette.CrimsonBright,
                fontSize = 11.sp,
                fontWeight = FontWeight.Black
            )

        if (isResumedGame && lastPlayerName != null) {
            Spacer(modifier = Modifier.height(12.dp))

            PokerPanel {
                Text(
                    text = "Последнее очко перед продолжением — $lastPlayerName",
                    color = PokerPalette.Gold,
                    fontSize = 15.sp,
                    fontWeight = FontWeight.SemiBold
                )
            }
        }

        QueuedStreakPraiseBanner(
            gameKey =
                gameId ?: "local_game",
            players =
                playerIds.mapIndexedNotNull {
                        playerIndex,
                        playerId ->

                    val name =
                        playerNames.getOrNull(
                            playerIndex
                        )
                            ?: return@mapIndexedNotNull null

                    playerId to name
                },
            history =
                history.mapNotNull { historyIndex ->
                    playerIds.getOrNull(
                        historyIndex
                    )
                },
            includeLatestOnStart =
                false,
            topSpacingDp =
                12
        )

        Spacer(modifier = Modifier.height(16.dp))

        playerNames.forEachIndexed { index, playerName ->
            PokerPlayerCard(
                name = playerName,
                score = scores.getOrElse(index) { 0 },
                targetScore = targetScore,
                onAddPoint = {
                    view.performHapticFeedback(
                        HapticFeedbackConstants.CLOCK_TICK
                    )

                    onAddPoint(index)
                },
                isLastPoint = index == lastPlayerIndex
            )

            Spacer(modifier = Modifier.height(10.dp))
        }

        Spacer(modifier = Modifier.height(6.dp))

        if (gameId != null) {
            StyledReactionBar(
                gameId = gameId,
                senderName =
                    reactionSenderName
            )

            Spacer(
                modifier =
                    Modifier.height(12.dp)
            )
        }

        PokerPanel {
            PokerSecondaryButton(
                text = "↶  Отменить последнее",
                onClick = onUndo,
                enabled = history.isNotEmpty()
            )

            Spacer(modifier = Modifier.height(10.dp))

            PokerSecondaryButton(
                text = "Изменить лимит  •  сейчас до $targetScore",
                onClick = {
                    targetText = targetScore.toString()
                    showTargetDialog = true
                }
            )

            Spacer(modifier = Modifier.height(10.dp))

            PokerPrimaryButton(
                text = "Поставить на паузу",
                onClick = onPause
            )

            Spacer(modifier = Modifier.height(10.dp))

            PokerBottomBackButton(
                text = "←  В меню",
                onClick = onBackToMenu
            )

            Spacer(modifier = Modifier.height(14.dp))

            PokerDangerButton(
                text = "Отменить партию",
                onClick = onCancelGame
            )
        }

        Spacer(modifier = Modifier.height(18.dp))

        Text(
            text = "♣   Счёт сохраняется автоматически   ♦",
            color = PokerPalette.TextSecondary,
            fontSize = 13.sp,
            modifier = Modifier.align(Alignment.CenterHorizontally)
        )
        }

        if (gameId != null) {
            ReactionRainHost(
                gameId =
                    gameId,
                modifier =
                    Modifier.fillMaxSize(),
                skipExistingOnStart =
                    true
            )
        }
    }

    if (showTargetDialog) {
        val newTarget = targetText.toIntOrNull()
        val targetValid = newTarget != null && newTarget in 2..99

        AlertDialog(
            onDismissRequest = {
                showTargetDialog = false
            },
            title = {
                Text("До скольки играем?")
            },
            text = {
                Column {
                    Text(
                        text = "Можно изменить лимит прямо во время партии.",
                        fontSize = 15.sp
                    )

                    Spacer(modifier = Modifier.height(14.dp))

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.spacedBy(10.dp)
                    ) {
                        OutlinedButton(
                            onClick = { targetText = "10" },
                            modifier = Modifier.weight(1f)
                        ) {
                            Text("10")
                        }

                        OutlinedButton(
                            onClick = { targetText = "15" },
                            modifier = Modifier.weight(1f)
                        ) {
                            Text("15")
                        }
                    }

                    Spacer(modifier = Modifier.height(12.dp))

                    OutlinedTextField(
                        value = targetText,
                        onValueChange = { value ->
                            targetText = value.filter { it.isDigit() }
                        },
                        label = { Text("Лимит") },
                        singleLine = true,
                        keyboardOptions = KeyboardOptions(
                            keyboardType = KeyboardType.Number
                        ),
                        isError = targetText.isNotEmpty() && !targetValid
                    )

                    if (targetText.isNotEmpty() && !targetValid) {
                        Spacer(modifier = Modifier.height(6.dp))
                        Text(
                            text = "Введите число от 2 до 99",
                            color = MaterialTheme.colorScheme.error,
                            fontSize = 13.sp
                        )
                    }
                }
            },
            confirmButton = {
                Button(
                    enabled = targetValid,
                    onClick = {
                        val value = newTarget
                        if (value != null) {
                            showTargetDialog = false
                            if (value != targetScore) {
                                onChangeTargetScore(value)
                            }
                        }
                    }
                ) {
                    Text("Сохранить")
                }
            },
            dismissButton = {
                TextButton(
                    onClick = {
                        showTargetDialog = false
                    }
                ) {
                    Text("Отмена")
                }
            }
        )
    }
}


@Composable
private fun GothicGameTableHero(
    targetScore: Int,
    subtitle: String
) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .height(132.dp),
        colors = CardDefaults.cardColors(
            containerColor = Color(0xF2080908)
        ),
        border = BorderStroke(
            1.dp,
            PokerPalette.Crimson
        ),
        shape = RoundedCornerShape(18.dp)
    ) {
        Box(
            modifier = Modifier.fillMaxSize()
        ) {
            Image(
                painter = painterResource(
                    id = R.drawable.gothic_game_table_jester
                ),
                contentDescription = null,
                contentScale = ContentScale.Fit,
                modifier = Modifier
                    .align(Alignment.CenterEnd)
                    .size(
                        width = 150.dp,
                        height = 132.dp
                    )
            )

            Box(
                modifier = Modifier
                    .align(Alignment.CenterEnd)
                    .size(
                        width = 170.dp,
                        height = 132.dp
                    )
                    .background(
                        brush = androidx.compose.ui.graphics.Brush
                            .horizontalGradient(
                                listOf(
                                    Color(0xF2080908),
                                    Color(0x99080908),
                                    Color.Transparent
                                )
                            )
                    )
            )

            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(
                        start = 15.dp,
                        top = 14.dp,
                        end = 116.dp,
                        bottom = 10.dp
                    )
            ) {
                Text(
                    text = "ПАРТИЯ",
                    color = PokerPalette.TextPrimary,
                    fontFamily = PokerDisplayFont,
                    fontSize = 27.sp,
                    fontWeight = FontWeight.Black,
                    maxLines = 1
                )

                Text(
                    text = "ДО $targetScore",
                    color = PokerPalette.CrimsonBright,
                    fontSize = 17.sp,
                    fontWeight = FontWeight.Black
                )

                Spacer(modifier = Modifier.height(5.dp))

                Text(
                    text = subtitle,
                    color = PokerPalette.Gold,
                    fontSize = 9.5.sp,
                    lineHeight = 12.sp,
                    maxLines = 2
                )
            }
        }
    }
}

data class UserAccess(
    val name: String,
    val role: String,
    val playerId: String
)

@Composable
fun UserAccessGate(
    user: DurakSession,
    onLogout: () -> Unit
) {

    var isLoading by remember {
        mutableStateOf(true)
    }

    var access by remember {
        mutableStateOf<UserAccess?>(null)
    }

    var errorText by remember {
        mutableStateOf<String?>(null)
    }

    var isPending by remember {
        mutableStateOf(false)
    }

    LaunchedEffect(user.uid) {

        /*
         * ЭТАП МИГРАЦИИ НА VPS:
         *
         * Сначала проверяем пользователя через наш HTTPS API.
         * Авторизация, роли и игровые данные теперь идут через наш HTTPS API
         * и PostgreSQL. В приложении больше нет сервер-зависимостей.
         */

        fun loadAccessStatusFromVps() {

            DurakServerApi
                .loadAccessStatus(
                    user =
                        user,
                    onSuccess = {
                            result ->

                        when (
                            result.status
                        ) {

                            "pending" -> {
                                isPending =
                                    true

                                errorText =
                                    null
                            }

                            "active" -> {
                                /*
                                 * Редкий race: статус уже active,
                                 * а первый /me попал в момент обновления.
                                 * Просто повторяем /me один раз через
                                 * текущий LaunchedEffect не нужно —
                                 * показываем понятную просьбу перезапустить.
                                 */
                                errorText =
                                    "Доступ уже выдан. Перезапустите приложение."
                            }

                            else -> {
                                errorText =
                                    "Для этого аккаунта доступ не назначен"
                            }
                        }

                        isLoading =
                            false
                    },
                    onError = {
                            message ->

                        errorText =
                            message.ifBlank {
                                "Не удалось проверить доступ через сервер"
                            }

                        isLoading =
                            false
                    }
                )
        }

        DurakServerApi.loadMe(
            user = user,
            onSuccess = { serverMe ->

                access =
                    UserAccess(
                        name =
                            serverMe.name.ifBlank {
                                "Пользователь"
                            },
                        role =
                            serverMe.role.ifBlank {
                                "viewer"
                            },
                        playerId =
                            serverMe.playerId
                    )

                Log.i(
                    "DurakServerApi",
                    "User access loaded from VPS: role=${serverMe.role}, playerId=${serverMe.playerId}"
                )

                isLoading = false
            },
            onForbidden = {

                loadAccessStatusFromVps()
            },
            onError = { message ->

                errorText =
                    message.ifBlank {
                        "Не удалось проверить доступ через сервер"
                    }

                isLoading =
                    false
            }
        )
    }

    when {

        isLoading -> {

            Box(
                modifier =
                    Modifier
                        .fillMaxSize()
                        .background(
                            PokerPalette.BackgroundTop
                        ),
                contentAlignment =
                    Alignment.Center
            ) {

                CircularProgressIndicator(
                    color =
                        PokerPalette.CrimsonBright
                )
            }
        }
        isPending -> {

            PendingAccessScreen(
                onLogout = onLogout
            )
        }
        errorText != null -> {

            Box(
                modifier =
                    Modifier
                        .fillMaxSize()
                        .background(
                            PokerPalette.BackgroundTop
                        )
                        .padding(
                            24.dp
                        ),
                contentAlignment =
                    Alignment.Center
            ) {

                Card(
                    colors =
                        CardDefaults.cardColors(
                            containerColor =
                                PokerPalette.Panel
                        ),
                    border =
                        BorderStroke(
                            1.dp,
                            PokerPalette.Crimson
                        ),
                    shape =
                        RoundedCornerShape(
                            20.dp
                        )
                ) {

                    Column(
                        modifier =
                            Modifier.padding(
                                20.dp
                            ),
                        horizontalAlignment =
                            Alignment.CenterHorizontally
                    ) {

                        Text(
                            text =
                                "☠  ДОСТУПА НЕТ",
                            color =
                                PokerPalette.CrimsonBright,
                            fontSize =
                                20.sp,
                            fontWeight =
                                FontWeight.Black
                        )

                        Spacer(
                            modifier =
                                Modifier.height(
                                    10.dp
                                )
                        )

                        Text(
                            text =
                                errorText!!,
                            color =
                                PokerPalette.TextSecondary,
                            fontSize =
                                15.sp
                        )

                        Spacer(
                            modifier =
                                Modifier.height(
                                    18.dp
                                )
                        )

                        PokerSecondaryButton(
                            text =
                                "Выйти",
                            onClick =
                                onLogout
                        )
                    }
                }
            }
        }
        access != null -> {

            DurakScoreApp(
                access = access!!,
                onLogout = onLogout
            )
        }
    }
}

@Composable
fun PendingAccessScreen(
    onLogout: () -> Unit
) {

    Box(
        modifier =
            Modifier
                .fillMaxSize()
                .background(
                    PokerPalette.BackgroundTop
                )
                .padding(
                    22.dp
                ),
        contentAlignment =
            Alignment.Center
    ) {

        Card(
            colors =
                CardDefaults.cardColors(
                    containerColor =
                        PokerPalette.Panel
                ),
            border =
                BorderStroke(
                    1.dp,
                    PokerPalette.GoldDark
                ),
            shape =
                RoundedCornerShape(
                    22.dp
                )
        ) {

            Column(
                modifier =
                    Modifier.padding(
                        22.dp
                    ),
                horizontalAlignment =
                    Alignment.CenterHorizontally
            ) {

                Text(
                    text =
                        "ДУРАКОМЕТР",
                    color =
                        PokerPalette.TextPrimary,
                    fontFamily =
                        PokerDisplayFont,
                    fontSize =
                        30.sp,
                    fontWeight =
                        FontWeight.Black
                )

                Spacer(
                    modifier =
                        Modifier.height(
                            4.dp
                        )
                )

                Text(
                    text =
                        "Стол всё помнит.",
                    color =
                        PokerPalette.CrimsonBright,
                    fontSize =
                        14.sp,
                    fontWeight =
                        FontWeight.Bold
                )

                Spacer(
                    modifier =
                        Modifier.height(
                            24.dp
                        )
                )

                Text(
                    text =
                        "Заявка отправлена",
                    color =
                        PokerPalette.Gold,
                    fontSize =
                        22.sp,
                    fontWeight =
                        FontWeight.Bold
                )

                Spacer(
                    modifier =
                        Modifier.height(
                            8.dp
                        )
                )

                Text(
                    text =
                        "Ожидаем подтверждение администратора.",
                    color =
                        PokerPalette.TextSecondary,
                    fontSize =
                        15.sp
                )

                Spacer(
                    modifier =
                        Modifier.height(
                            22.dp
                        )
                )

                PokerDangerButton(
                    text =
                        "Выйти",
                    onClick =
                        onLogout
                )
            }
        }
    }
}
