package ru.durakscore.app

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import java.util.Date

data class LiveGame(
    val id: String,
    val type: String,
    val status: String,
    val targetScore: Int,
    val players: List<GamePlayer>,
    val scores: Map<String, Int>,
    val history: List<String>,
    val startedAt: Timestamp,
    val lastActionType: String? = null,
    val lastActionAt: Timestamp? = null,
    val finishedAt: Timestamp? = null,
    val loserId: String? = null,
    val loserName: String? = null,
    val loserGender: String? = null
)

private fun parseLiveGame(
    game: ServerLiveGame?
): LiveGame? {

    game
        ?: return null

    if (
        game.isTest !=
        TEST_MODE
    ) {
        return null
    }

    if (
        game.startedAtEpochMillis <=
        0L
    ) {
        return null
    }

    val scoreSource =
        if (
            game.status ==
            "finished" &&
            game.finalScores
                .isNotEmpty()
        ) {
            game.finalScores
        } else {
            game.scores
        }

    return LiveGame(
        id =
            game.id,
        type =
            game.gameType,
        status =
            game.status,
        targetScore =
            game.targetScore,
        players =
            game.players.map {
                    player ->

                GamePlayer(
                    id =
                        player.id,
                    name =
                        player.name,
                    gender =
                        player.gender,
                    isCore =
                        player.isCore,
                    isGuest =
                        player.isGuest
                )
            },
        scores =
            scoreSource,
        history =
            game.history,
        startedAt =
            Timestamp(
                Date(
                    game.startedAtEpochMillis
                )
            ),
        lastActionType =
            game.lastActionType,
        lastActionAt =
            game.lastActionAtEpochMillis
                ?.let {
                    Timestamp(
                        Date(
                            it
                        )
                    )
                },
        finishedAt =
            game.finishedAtEpochMillis
                ?.let {
                    Timestamp(
                        Date(
                            it
                        )
                    )
                },
        loserId =
            game.loserId,
        loserName =
            game.loserName,
        loserGender =
            game.loserGender
    )
}

@Composable
fun LiveGameScreen(
    access: UserAccess,
    onResultReached: (String) -> Unit,
    onBack: () -> Unit
) {

    // Системная кнопка Android "Назад"
    // возвращает в меню, а не закрывает приложение.
    BackHandler(
        enabled = true,
        onBack = onBack
    )

    var activeGame by remember {
        mutableStateOf<LiveGame?>(null)
    }

    var lastFinishedGame by remember {
        mutableStateOf<LiveGame?>(null)
    }

    var activeLoaded by remember {
        mutableStateOf(false)
    }

    var finishedLoaded by remember {
        mutableStateOf(false)
    }

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

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

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

    DisposableEffect(Unit) {

        val user =
            DurakAuthSession.current()

        if (
            user ==
            null
        ) {
            activeLoaded =
                true

            finishedLoaded =
                true

            errorText =
                "Пользователь не авторизован"

            onDispose {
            }

        } else {

            val registration =
                DurakServerApi
                    .startLivePolling(
                        user =
                            user,
                        isTest =
                            TEST_MODE,
                        onSnapshot = {
                                snapshot ->

                            val newActive =
                                parseLiveGame(
                                    snapshot
                                        .activeGame
                                )

                            val newFinished =
                                parseLiveGame(
                                    snapshot
                                        .lastFinishedGame
                                )

                            activeGame =
                                newActive

                            lastFinishedGame =
                                newFinished

                            if (
                                newActive !=
                                null
                            ) {
                                observedGameId =
                                    newActive.id
                            }

                            activeLoaded =
                                true

                            finishedLoaded =
                                true

                            errorText =
                                null
                        },
                        onError = {
                                message ->

                            activeLoaded =
                                true

                            finishedLoaded =
                                true

                            if (
                                activeGame ==
                                null &&
                                lastFinishedGame ==
                                null
                            ) {
                                errorText =
                                    message.ifBlank {
                                        "Не удалось получить текущую партию"
                                    }
                            }
                        }
                    )

            onDispose {
                registration.remove()
            }
        }
    }

    /*
     * Как только в live-счёте кто-то достигает лимита,
     * наблюдатель сразу уходит на общий экран результата.
     * Не ждём, пока ведущий нажмёт "Завершить партию".
     */
    LaunchedEffect(
        activeGame?.id,
        activeGame?.scores,
        activeGame?.targetScore
    ) {

        val game =
            activeGame
                ?: return@LaunchedEffect

        val reachedLimit =
            game.scores
                .values
                .any {
                    it >=
                    game.targetScore
                }

        if (
            reachedLimit &&
            resultNavigationGameId !=
                game.id
        ) {
            resultNavigationGameId =
                game.id

            onResultReached(
                game.id
            )
        }
    }

    /*
     * Запасной путь: если сеть прислала finished раньше,
     * чем мы успели увидеть последний active snapshot.
     */
    LaunchedEffect(
        activeGame?.id,
        lastFinishedGame?.id,
        observedGameId
    ) {

        val watchedId =
            observedGameId
                ?: return@LaunchedEffect

        if (
            activeGame == null &&
            lastFinishedGame?.id ==
                watchedId &&
            resultNavigationGameId !=
                watchedId
        ) {
            resultNavigationGameId =
                watchedId

            onResultReached(
                watchedId
            )
        }
    }

    val isLoading =
        !activeLoaded ||
        !finishedLoaded

    Box(
        modifier =
            Modifier.fillMaxSize()
    ) {

        PokerScreen {

        // Верхнюю кнопку "Назад" убрали.
        // Навигация — системная кнопка Android
        // и большая кнопка "В меню" снизу.

        PokerHeader(
            title = "Наблюдение за столом",
            subtitle = "Живой счёт, последние очки и весь бардак в реальном времени"
        )

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

        LiveObservationHero(
            activeGame = activeGame,
            finishedGame = lastFinishedGame
        )

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

        when {

            isLoading -> {

                Row(
                    modifier =
                        Modifier.fillMaxWidth(),
                    horizontalArrangement =
                        Arrangement.Center
                ) {

                    CircularProgressIndicator(
                        color =
                            PokerPalette.Gold
                    )
                }
            }

            activeGame != null -> {

                CurrentLiveGameContent(
                    game = activeGame!!,
                    senderName =
                        access.name
                )
            }

            lastFinishedGame != null -> {

                FinishedLiveGameContent(
                    game =
                        lastFinishedGame!!
                )
            }

            errorText != null -> {

                PokerPanel {

                    Text(
                        text =
                            errorText!!,
                        color =
                            PokerPalette.Danger,
                        fontSize =
                            17.sp
                    )
                }
            }

            else -> {

                PokerPanel {

                    Text(
                        text =
                            "Сейчас активной партии нет",
                        color =
                            PokerPalette.TextPrimary,
                        fontSize =
                            21.sp,
                        fontWeight =
                            FontWeight.Bold
                    )

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

                    Text(
                        text =
                            "Когда партия начнётся, счёт появится здесь автоматически.",
                        color =
                            PokerPalette.TextSecondary,
                        fontSize =
                            15.sp
                    )
                }
            }
        }

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

        PokerSecondaryButton(
            text = "←  В меню",
            onClick = onBack
        )
        }

        if (activeGame != null) {
            ReactionRainHost(
                gameId =
                    activeGame!!.id,
                modifier =
                    Modifier.fillMaxSize()
            )
        }
    }
}

@Composable
private fun LiveObservationHero(
    activeGame: LiveGame?,
    finishedGame: LiveGame?
) {

    val heroTitle =
        when {
            activeGame?.status == "paused" ->
                "Стол замер. Джокер караулит."

            activeGame != null ->
                "Джокер видит каждое очко"

            finishedGame != null ->
                "Раздача добита. Джокер помнит всё."

            else ->
                "Джокер ждёт новую жертву"
        }

    val heroSubtitle =
        when {
            activeGame?.status == "paused" ->
                "Партия на паузе, но стол никуда не делся. Как только движ пойдёт дальше, ты увидишь это первым."

            activeGame != null ->
                "Следи, кому залетает очередное очко, кто уже начинает пылать и кому скоро будет совсем не до смеха."

            finishedGame != null ->
                "Активной катки сейчас нет, но последняя раздача уже оставила после себя красивый финал и итоговый счёт."

            else ->
                "Пока за столом тихо. Как только кто-то запустит новую игру, экран оживёт автоматически."
        }

    val badgeText =
        when {
            activeGame?.status == "paused" ->
                "СТОЛ НА ПАУЗЕ"

            activeGame != null ->
                "ЖИВОЕ НАБЛЮДЕНИЕ"

            finishedGame != null ->
                "ПОСЛЕДНИЙ РЕЗУЛЬТАТ"

            else ->
                "ОЖИДАНИЕ НОВОЙ ИГРЫ"
        }

    val borderColor =
        when {
            activeGame != null ->
                PokerPalette.Crimson

            finishedGame != null ->
                PokerPalette.GoldDark

            else ->
                PokerPalette.PanelBorder
        }

    Card(
        modifier =
            Modifier.fillMaxWidth(),
        colors =
            CardDefaults.cardColors(
                containerColor =
                    Color(0xF2080A09)
            ),
        border =
            BorderStroke(
                width = 1.2.dp,
                color = borderColor
            ),
        shape =
            RoundedCornerShape(22.dp)
    ) {

        Box(
            modifier =
                Modifier
                    .fillMaxWidth()
                    .background(
                        brush =
                            Brush.horizontalGradient(
                                colors =
                                    listOf(
                                        Color(0xFF070908),
                                        Color(0xFF140708),
                                        Color(0xFF24090C)
                                    )
                            )
                    )
                    .padding(18.dp)
        ) {

            Image(
                painter =
                    painterResource(
                        id =
                            R.drawable.watch_hero_jester
                    ),
                contentDescription =
                    null,
                contentScale =
                    ContentScale.Fit,
                modifier =
                    Modifier
                        .fillMaxWidth()
                        .height(212.dp)
                        .align(Alignment.CenterEnd)
                        .alpha(0.90f)
            )

            Box(
                modifier =
                    Modifier
                        .fillMaxWidth(0.74f)
                        .height(212.dp)
                        .background(
                            brush =
                                Brush.horizontalGradient(
                                    colors =
                                        listOf(
                                            Color(0xFF090A09),
                                            Color(0xFF0B0909),
                                            Color(0xE50C0808),
                                            Color(0x700C0808),
                                            Color.Transparent
                                        )
                                )
                        )
            )

            androidx.compose.foundation.layout.Column(
                modifier =
                    Modifier
                        .fillMaxWidth(0.58f)
                        .padding(top = 4.dp, bottom = 4.dp),
                verticalArrangement =
                    Arrangement.Center
            ) {

                PokerBadge(
                    text = badgeText
                )

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

                Text(
                    text = heroTitle,
                    color = PokerPalette.TextPrimary,
                    fontSize = 24.sp,
                    fontWeight = FontWeight.Bold,
                    lineHeight = 28.sp
                )

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

                Text(
                    text = heroSubtitle,
                    color = PokerPalette.TextSecondary,
                    fontSize = 14.sp,
                    lineHeight = 19.sp
                )
            }
        }
    }
}

@Composable
private fun CurrentLiveGameContent(
    game: LiveGame,
    senderName: String
) {

    val lastPointPlayerId =
        game.history.lastOrNull()

    val lastPointPlayer =
        game.players.firstOrNull {
            it.id == lastPointPlayerId
        }


    PokerPanel {

        Text(
            text =
                if (game.type == "classic") {
                    "Классическая партия"
                } else {
                    "Другой состав"
                },
            color =
                PokerPalette.Gold,
            fontSize =
                22.sp,
            fontWeight =
                FontWeight.Bold
        )

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

        Text(
            text =
                "Начало: ${formatMoscowDateTime(game.startedAt)}",
            color =
                PokerPalette.TextSecondary,
            fontSize =
                14.sp
        )

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

        Text(
            text =
                "Играем до ${game.targetScore}",
            color =
                PokerPalette.TextPrimary,
            fontSize =
                18.sp,
            fontWeight =
                FontWeight.SemiBold
        )

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

        PokerBadge(
            text =
                if (game.status == "paused") {
                    "ПАУЗА"
                } else {
                    "ИДЁТ СЕЙЧАС"
                }
        )

        if (lastPointPlayer != null) {

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

            Text(
                text =
                    "Последнее очко — ${lastPointPlayer.name}",
                color =
                    PokerPalette.Gold,
                fontSize =
                    16.sp,
                fontWeight =
                    FontWeight.Bold
            )
        }
    }

    val latestActionAgeMs =
        game.lastActionAt
            ?.let { actionAt ->
                (
                    System.currentTimeMillis() -
                    actionAt.toDate().time
                ).coerceAtLeast(0L)
            }

    QueuedStreakPraiseBanner(
        gameKey =
            game.id,
        players =
            game.players.map {
                it.id to it.name
            },
        history =
            game.history,
        includeLatestOnStart =
            game.lastActionType ==
                "add_point" &&
            latestActionAgeMs != null &&
            latestActionAgeMs <
                STREAK_PRAISE_DURATION_MS,
        topSpacingDp =
            14
    )

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

    game.players.forEach { player ->

        val isLastPoint =
            player.id ==
                lastPointPlayerId

        LiveScoreCard(
            player = player,
            score =
                game.scores[player.id]
                    ?: 0,
            targetScore =
                game.targetScore,
            isLastPoint =
                isLastPoint
        )

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

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

    StyledReactionBar(
        gameId =
            game.id,
        senderName =
            senderName
    )

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

    Text(
        text =
            "Счёт обновляется автоматически",
        color =
            PokerPalette.TextSecondary,
        fontSize =
            14.sp
    )
}

@Composable
fun StyledReactionBar(
    gameId: String,
    senderName: String
) {
    /*
     * Только делегируем в настоящий ReactionBar.
     * Вся логика отправки, cooldown и haptic
     * остаётся внутри GameReactions.kt.
     * Сеть теперь идёт через VPS/PostgreSQL.
     */
    ReactionBar(
        gameId = gameId,
        senderName = senderName
    )
}

@Composable
private fun FinishedLiveGameContent(
    game: LiveGame
) {

    val loser =
        game.players.firstOrNull {
            it.id == game.loserId
        }

    val loserName =
        game.loserName
            ?: loser?.name
            ?: "Игрок"

    val loserGender =
        game.loserGender
            ?: loser?.gender
            ?: "male"

    val loserWord =
        if (loserGender == "female") {
            "проиграла"
        } else {
            "проиграл"
        }

    PokerPanel {

        PokerBadge(
            text =
                "ПАРТИЯ ОКОНЧЕНА"
        )

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

        Text(
            text =
                "$loserName $loserWord",
            color =
                PokerPalette.Gold,
            fontSize =
                28.sp,
            fontWeight =
                FontWeight.Bold
        )

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

        Text(
            text =
                if (game.type == "classic") {
                    "Классическая партия"
                } else {
                    "Другой состав"
                },
            color =
                PokerPalette.TextPrimary,
            fontSize =
                17.sp,
            fontWeight =
                FontWeight.SemiBold
        )

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

        Text(
            text =
                "Начало: ${formatMoscowDateTime(game.startedAt)}",
            color =
                PokerPalette.TextSecondary,
            fontSize =
                14.sp
        )

        if (game.finishedAt != null) {

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

            Text(
                text =
                    "Завершена: ${formatMoscowDateTime(game.finishedAt)}",
                color =
                    PokerPalette.TextSecondary,
                fontSize =
                    14.sp
            )
        }
    }

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

    Text(
        text =
            "Итоговый счёт",
        color =
            PokerPalette.Gold,
        fontSize =
            20.sp,
        fontWeight =
            FontWeight.Bold
    )

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

    game.players.forEach { player ->

        FinishedScoreCard(
            player = player,
            score =
                game.scores[player.id]
                    ?: 0,
            isLoser =
                player.id ==
                    game.loserId
        )

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

    Text(
        text =
            "Результат сохранён в истории партий",
        color =
            PokerPalette.TextSecondary,
        fontSize =
            14.sp
    )
}

@Composable
private fun LiveScoreCard(
    player: GamePlayer,
    score: Int,
    targetScore: Int,
    isLastPoint: Boolean
) {

    PokerPanel {

        Row(
            modifier =
                Modifier.fillMaxWidth(),
            horizontalArrangement =
                Arrangement.SpaceBetween,
            verticalAlignment =
                Alignment.CenterVertically
        ) {

            Text(
                text =
                    if (player.isGuest) {
                        "${player.name} · гость"
                    } else {
                        player.name
                    },
                color =
                    if (isLastPoint) {
                        PokerPalette.Gold
                    } else {
                        PokerPalette.TextPrimary
                    },
                fontSize =
                    22.sp,
                fontWeight =
                    FontWeight.Bold
            )

            AnimatedScoreValue(
                score =
                    score,
                targetScore =
                    targetScore,
                color =
                    if (isLastPoint) {
                        PokerPalette.Gold
                    } else {
                        PokerPalette.TextPrimary
                    }
            )
        }

        if (isLastPoint) {

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

            PokerBadge(
                text =
                    "ПОСЛЕДНЕЕ ОЧКО"
            )
        }
    }
}

@Composable
private fun FinishedScoreCard(
    player: GamePlayer,
    score: Int,
    isLoser: Boolean
) {

    PokerPanel {

        Row(
            modifier =
                Modifier.fillMaxWidth(),
            horizontalArrangement =
                Arrangement.SpaceBetween,
            verticalAlignment =
                Alignment.CenterVertically
        ) {

            Text(
                text =
                    if (player.isGuest) {
                        "${player.name} · гость"
                    } else {
                        player.name
                    },
                color =
                    if (isLoser) {
                        PokerPalette.Gold
                    } else {
                        PokerPalette.TextPrimary
                    },
                fontSize =
                    22.sp,
                fontWeight =
                    FontWeight.Bold
            )

            Text(
                text =
                    score.toString(),
                color =
                    if (isLoser) {
                        PokerPalette.Gold
                    } else {
                        PokerPalette.TextPrimary
                    },
                fontSize =
                    32.sp,
                fontWeight =
                    FontWeight.Bold
            )
        }

        if (isLoser) {

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

            PokerBadge(
                text =
                    "ПРОИГРАЛ(А)"
            )
        }
    }
}
