package com.jsjdesigns.poker

import com.jsjdesigns.poker.bot.BotProfile
import com.jsjdesigns.poker.bot.PersonaCueId
import com.jsjdesigns.poker.bot.PlayStyle
import com.jsjdesigns.poker.bot.SkillLevel
import com.jsjdesigns.poker.bot.TableTalkLine
import com.jsjdesigns.poker.game.Action
import com.jsjdesigns.poker.game.ActionType
import com.jsjdesigns.poker.game.HandEvent
import com.jsjdesigns.poker.game.SeatSnapshot
import com.jsjdesigns.poker.game.Street
import com.jsjdesigns.poker.game.TableSnapshot
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test

class PersonaTalkTest {

    private val bruno = BotProfile(
        "Bruno",
        SkillLevel.ADVANCED,
        PlayStyle.LOOSE_AGGRESSIVE,
        "Relentless pressure.",
    )

    private fun snapshot(
        handNumber: Int = 4,
        street: Street = Street.FLOP,
        actionNumber: Int = 3,
        event: HandEvent = HandEvent(
            Street.FLOP,
            1,
            "Bruno",
            Action(ActionType.RAISE, 12),
        ),
        board: List<Int> = listOf(2, 7, 9),
        opponentHole: List<Int>? = listOf(40, 41),
        phase: TableSnapshot.Phase = TableSnapshot.Phase.BETTING,
    ) = TableSnapshot(
        handNumber = handNumber,
        street = street,
        phase = phase,
        board = board,
        pot = 25,
        currentBet = 12,
        minRaiseSize = 8,
        button = 0,
        seats = listOf(
            seat(0, "You", hole = listOf(0, 1)),
            seat(1, "Bruno", hole = opponentHole),
        ),
        toAct = null,
        toActToken = null,
        lastAction = event,
        actionNumber = actionNumber,
    )

    private fun seat(index: Int, name: String, hole: List<Int>?) = SeatSnapshot(
        index = index,
        name = name,
        stack = 180,
        committedThisRound = if (index == 1) 12 else 2,
        committedThisHand = if (index == 1) 12 else 2,
        folded = false,
        allIn = false,
        hole = hole,
        revealed = false,
        isButton = index == 0,
    )

    @Test
    fun `narration cue is independent of every card in the rich snapshot`() {
        val profiles = mapOf(1 to bruno)
        val first = personaCueFor(snapshot(), profiles)
        val differentCards = personaCueFor(
            snapshot(
                board = listOf(14, 22, 38),
                opponentHole = listOf(10, 11),
            ),
            profiles,
        )

        assertEquals(first, differentCards)
        assertEquals(PersonaCueId(4, 3), first!!.id)
        assertEquals(ActionType.RAISE, first.actionType)
        assertEquals(12, first.amount)
        assertEquals(25, first.pot)
    }

    @Test
    fun `hero actions and stale street events never become persona cues`() {
        val heroAction = HandEvent(
            Street.FLOP,
            HERO_SEAT,
            "You",
            Action(ActionType.CALL, 10),
        )
        val staleAction = HandEvent(
            Street.PREFLOP,
            1,
            "Bruno",
            Action(ActionType.RAISE, 6),
        )

        assertNull(personaCueFor(snapshot(event = heroAction), mapOf(1 to bruno)))
        assertNull(personaCueFor(snapshot(event = staleAction), mapOf(1 to bruno)))
        assertNull(
            personaCueFor(
                snapshot(phase = TableSnapshot.Phase.STREET_COMPLETE),
                mapOf(1 to bruno),
            ),
        )
    }

    @Test
    fun `identical actions remain distinct through authoritative action number`() {
        val profiles = mapOf(1 to bruno)
        val first = personaCueFor(snapshot(actionNumber = 3), profiles)!!
        val later = personaCueFor(snapshot(actionNumber = 7), profiles)!!

        assertEquals(first.copy(id = PersonaCueId(4, 7)), later)
    }

    @Test
    fun `table talk is visible only beside the exact action frame`() {
        val line = TableTalkLine(
            cueId = PersonaCueId(4, 3),
            street = Street.FLOP,
            speakerSeat = 1,
            speakerName = "Bruno",
            text = "Let's turn the dial.",
        )
        val matching = UiState(snapshot = snapshot(), tableTalk = line)

        assertEquals(line, matching.visibleTableTalk())
        assertNull(matching.copy(snapshot = snapshot(actionNumber = 4)).visibleTableTalk())
        assertNull(matching.copy(snapshot = snapshot(handNumber = 5)).visibleTableTalk())
        assertNull(
            matching.copy(
                snapshot = snapshot(
                    street = Street.TURN,
                    event = HandEvent(
                        Street.TURN,
                        1,
                        "Bruno",
                        Action(ActionType.RAISE, 12),
                    ),
                ),
            ).visibleTableTalk(),
        )
        assertTrue(matching.acceptsTableTalk(line))
    }
}
