package com.jsjdesigns.poker

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.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.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.jsjdesigns.poker.game.SeatSnapshot
import com.jsjdesigns.poker.game.TableSnapshot

private val Felt = Color(0xFF0B3D26)

@Composable
fun TableScreen(vm: PokerViewModel) {
    val state by vm.state.collectAsStateWithLifecycle()
    val rawOffer by vm.offer.collectAsStateWithLifecycle()
    val snap = state.snapshot
    // Only show an action bar that belongs to the table currently on screen.
    val offer = state.liveOffer(rawOffer)
    val handSummary = state.visibleHandSummary()
    val coachReview = state.visibleCoachReview()
    val status = snap?.let(::tableStatus)

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Felt)
            .systemBarsPadding()
            .padding(12.dp),
    ) {
        Text(
            // snapshot.handNumber, not handsPlayed + 1: the counter increments when
            // the hand *finishes*, so during the showdown hold it would label the
            // result of hand 1 as "Hand 2".
            text = "Hand ${snap?.handNumber ?: 1}   ${snap?.street ?: ""}",
            color = Color.White.copy(alpha = 0.6f),
            fontSize = 12.sp,
        )

        Spacer(Modifier.height(8.dp))

        // Opponents
        // Weighted rather than fixed-width: five opponents must all fit on a
        // phone, and a horizontally scrolling table hides players from you.
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.spacedBy(4.dp),
        ) {
            snap?.seats?.filter { it.index != HERO_SEAT }?.forEach { seat ->
                Box(Modifier.weight(1f)) {
                    OpponentSeat(seat, isTurn = snap.toAct == seat.index)
                }
            }
        }

        Spacer(Modifier.weight(1f))

        // Board and pot
        Column(
            modifier = Modifier.fillMaxWidth(),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            Text(
                text = "POT  ${snap?.pot ?: 0}",
                color = Color(0xFFE3C179),
                fontWeight = FontWeight.Bold,
                fontSize = 18.sp,
            )
            Text(
                text = status?.text.orEmpty(),
                color = when (status?.tone) {
                    TableStatusTone.ACCENT -> Color(0xFFE3C179)
                    TableStatusTone.NORMAL -> Color.White.copy(alpha = 0.78f)
                    TableStatusTone.MUTED, null -> Color.White.copy(alpha = 0.48f)
                },
                fontWeight = if (status?.tone == TableStatusTone.ACCENT) {
                    FontWeight.Bold
                } else {
                    FontWeight.Normal
                },
                fontSize = 13.sp,
                modifier = Modifier.height(24.dp),
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
            )
            Spacer(Modifier.height(4.dp))
            Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
                val board = snap?.board.orEmpty()
                repeat(5) { i ->
                    if (i < board.size) {
                        CardImage(board[i], Modifier.size(54.dp, 81.dp))
                    } else {
                        Box(
                            Modifier
                                .size(54.dp, 81.dp)
                                .clip(RoundedCornerShape(4.dp))
                                .background(Color.White.copy(alpha = 0.06f)),
                        )
                    }
                }
            }
        }

        Spacer(Modifier.weight(1f))

        // Hero
        val hero = snap?.seats?.firstOrNull { it.index == HERO_SEAT }
        HeroSeat(hero, isTurn = snap?.toAct == HERO_SEAT)

        Spacer(Modifier.height(12.dp))

        ActionBar(
            offer = offer,
            handSummary = handSummary,
            nextHandRequested = state.nextHandRequested,
            rebuyRequired = state.heroNeedsRebuy,
            buyIn = state.gameConfig?.buyIn ?: DEFAULT_BUY_IN,
            coachReview = coachReview,
            heroFolded = hero?.folded == true,
            onFold = vm::fold,
            onCheckCall = vm::checkOrCall,
            onRaise = vm::raiseTo,
            onNextHand = vm::nextHand,
        )
    }
}

@Composable
private fun OpponentSeat(seat: SeatSnapshot, isTurn: Boolean) {
    Card(
        colors = CardDefaults.cardColors(
            containerColor = if (isTurn) Color(0xFF1D6B45) else Color.White.copy(alpha = 0.07f),
        ),
        modifier = Modifier.fillMaxWidth(),
    ) {
        Column(
            modifier = Modifier.padding(6.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
                if (seat.folded) {
                    Box(Modifier.size(24.dp, 36.dp))
                } else {
                    val hole = seat.hole
                    repeat(2) { i ->
                        CardImage(hole?.getOrNull(i), Modifier.size(24.dp, 36.dp))
                    }
                }
            }
            Spacer(Modifier.height(4.dp))
            Text(
                seat.name + if (seat.isButton) "  ⏺" else "",
                color = Color.White,
                fontSize = 11.sp,
                fontWeight = FontWeight.Medium,
                modifier = Modifier.alpha(if (seat.folded) 0.4f else 1f),
            )
            Text(
                if (seat.allIn) "ALL IN" else "${seat.stack}",
                color = if (seat.allIn) Color(0xFFE3C179) else Color.White.copy(alpha = 0.7f),
                fontSize = 11.sp,
            )
            if (seat.committedThisRound > 0) {
                Text(
                    "bet ${seat.committedThisRound}",
                    color = Color(0xFFE3C179),
                    fontSize = 10.sp,
                )
            }
        }
    }
}

@Composable
private fun HeroSeat(seat: SeatSnapshot?, isTurn: Boolean) {
    val folded = seat?.folded == true
    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        // A processed fold has to *look* folded, or a correct fold reads as a bug.
        Row(
            horizontalArrangement = Arrangement.spacedBy(4.dp),
            modifier = Modifier.alpha(if (folded) 0.25f else 1f),
        ) {
            repeat(2) { i ->
                CardImage(seat?.hole?.getOrNull(i), Modifier.size(64.dp, 96.dp))
            }
        }
        Column {
            Text(
                (seat?.name ?: "You") + if (seat?.isButton == true) "  ⏺" else "",
                color = when {
                    folded -> Color.White.copy(alpha = 0.4f)
                    isTurn -> Color(0xFFE3C179)
                    else -> Color.White
                },
                fontWeight = FontWeight.Bold,
            )
            if (folded) {
                Text("FOLDED", color = Color(0xFFC9545B), fontSize = 12.sp, fontWeight = FontWeight.Bold)
            }
            Text("${seat?.stack ?: 0}", color = Color.White.copy(alpha = 0.75f))
            if ((seat?.committedThisRound ?: 0) > 0) {
                Text("bet ${seat?.committedThisRound}", color = Color(0xFFE3C179), fontSize = 12.sp)
            }
        }
    }
}

@Composable
private fun ActionBar(
    offer: com.jsjdesigns.poker.game.DecisionOffer?,
    handSummary: HandSummary?,
    nextHandRequested: Boolean,
    rebuyRequired: Boolean,
    buyIn: Int,
    coachReview: com.jsjdesigns.poker.bot.CoachingReview?,
    heroFolded: Boolean,
    onFold: (Long) -> Unit,
    onCheckCall: (Long) -> Unit,
    onRaise: (Long, Int) -> Unit,
    onNextHand: (Int) -> Unit,
) {
    if (offer == null) {
        if (handSummary != null) {
            HandResultBar(
                summary = handSummary,
                nextHandRequested = nextHandRequested,
                rebuyRequired = rebuyRequired,
                buyIn = buyIn,
                onNextHand = onNextHand,
            )
        } else if (coachReview != null) {
            CoachBar(coachPresentation(coachReview))
        } else {
            Box(Modifier.fillMaxWidth().height(96.dp), contentAlignment = Alignment.Center) {
                Text(
                    if (heroFolded) "You folded — sitting out this hand" else "Waiting…",
                    color = Color.White.copy(alpha = 0.4f),
                )
            }
        }
        return
    }

    val buttons = buttonsFor(offer)
    var raiseTo by remember { mutableIntStateOf(buttons.fixedRaiseTo) }
    // Reset whenever a new decision arrives, or it keeps the previous one's value.
    LaunchedEffect(offer.token) { raiseTo = buttons.fixedRaiseTo }

    Column(Modifier.fillMaxWidth()) {
        if (buttons.showRaise && buttons.showSlider) {
            Text("Raise to $raiseTo", color = Color.White, fontSize = 13.sp)
            Slider(
                value = raiseTo.coerceIn(buttons.sliderMin, buttons.sliderMax).toFloat(),
                onValueChange = { raiseTo = it.toInt() },
                valueRange = buttons.sliderMin.toFloat()..buttons.sliderMax.toFloat(),
                colors = SliderDefaults.colors(
                    thumbColor = Color(0xFFE3C179),
                    activeTrackColor = Color(0xFFE3C179),
                    inactiveTrackColor = Color.White.copy(alpha = 0.22f),
                ),
            )
        }
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            if (buttons.showFold) {
                Button(
                    onClick = { onFold(offer.token) },
                    modifier = Modifier.weight(1f),
                    colors = ButtonDefaults.buttonColors(
                        containerColor = Color(0xFF8F1218),
                        contentColor = Color.White,
                    ),
                ) { Text("Fold") }
            }

            Button(
                onClick = { onCheckCall(offer.token) },
                modifier = Modifier.weight(1f),
                colors = ButtonDefaults.buttonColors(
                    containerColor = Color(0xFF2C6E49),
                    contentColor = Color.White,
                ),
            ) { Text(buttons.checkOrCallLabel) }

            if (buttons.showRaise) {
                val amount = if (buttons.showSlider) {
                    raiseTo.coerceIn(buttons.sliderMin, buttons.sliderMax)
                } else {
                    buttons.fixedRaiseTo
                }
                Button(
                    onClick = { onRaise(offer.token, amount) },
                    modifier = Modifier.weight(1f),
                    colors = ButtonDefaults.buttonColors(
                        containerColor = Color(0xFF14357A),
                        contentColor = Color.White,
                    ),
                ) { Text(raiseLabel(buttons, amount)) }
            }
        }
    }
}

@Composable
private fun CoachBar(presentation: CoachPresentation) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        colors = CardDefaults.cardColors(containerColor = Color(0xFF14357A).copy(alpha = 0.48f)),
    ) {
        Column(Modifier.fillMaxWidth().padding(12.dp)) {
            Text(
                "COACH  •  ${presentation.title}",
                color = Color(0xFFE3C179),
                fontWeight = FontWeight.Bold,
                fontSize = 12.sp,
            )
            Text(
                presentation.detail,
                color = Color.White.copy(alpha = 0.78f),
                fontSize = 12.sp,
                maxLines = 2,
                overflow = TextOverflow.Ellipsis,
            )
        }
    }
}

@Composable
private fun HandResultBar(
    summary: HandSummary,
    nextHandRequested: Boolean,
    rebuyRequired: Boolean,
    buyIn: Int,
    onNextHand: (Int) -> Unit,
) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        colors = CardDefaults.cardColors(containerColor = Color.White.copy(alpha = 0.09f)),
    ) {
        Column(
            modifier = Modifier.fillMaxWidth().padding(12.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            Text(
                summary.title,
                color = Color(0xFFE3C179),
                fontWeight = FontWeight.Bold,
                fontSize = 18.sp,
            )
            Text(
                summary.detail,
                color = Color.White.copy(alpha = 0.7f),
                fontSize = 12.sp,
            )
            if (rebuyRequired) {
                Text(
                    "Your stack cannot cover the big blind.",
                    color = Color(0xFFC9545B),
                    fontSize = 12.sp,
                    fontWeight = FontWeight.Medium,
                )
            }
            Spacer(Modifier.height(8.dp))
            Button(
                onClick = { onNextHand(summary.handNumber) },
                enabled = !nextHandRequested,
                modifier = Modifier.fillMaxWidth(),
                colors = ButtonDefaults.buttonColors(
                    containerColor = Color(0xFF2C6E49),
                    contentColor = Color.White,
                    disabledContainerColor = Color(0xFF2C6E49).copy(alpha = 0.45f),
                    disabledContentColor = Color.White.copy(alpha = 0.65f),
                ),
            ) {
                Text(continuationButtonLabel(nextHandRequested, rebuyRequired, buyIn))
            }
        }
    }
}

@Composable
private fun CardImage(index: Int?, modifier: Modifier) {
    Image(
        painter = painterResource(if (index != null) cardFace(index) else cardBack()),
        contentDescription = null,
        modifier = modifier.clip(RoundedCornerShape(4.dp)),
    )
}
