package com.jsjdesigns.poker

import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test

/**
 * Seat geometry must hold on real phones, not just the development emulator.
 *
 * The previous layout paired a fixed 88dp seat with fractional centres. That
 * overlapped by 30dp at 384dp wide (Galaxy S24+) while looking merely tight at
 * 427dp (Pixel emulator) — which is why the crowding stayed invisible in
 * testing. These widths are asserted explicitly so the narrow case cannot
 * regress unnoticed again.
 */
class TableOrbitTest {

    /** Small phone, common phone, Galaxy S24+, Pixel, emulator, large phone. */
    private val widths = listOf(320f, 360f, 384f, 411f, 427f, 480f)

    @Test
    fun `five seats never overlap at any supported width`() {
        for (width in widths) {
            val seatWidth = opponentSeatWidthDp(width)
            val placements = opponentSeatPlacements(width, seatWidth)
            assertEquals(5, placements.size)

            placements.zipWithNext { left, right ->
                val gap = right.leftDp - (left.leftDp + seatWidth)
                assertTrue(
                    "at ${width}dp seats overlap by ${-gap}dp (seat width $seatWidth)",
                    gap >= -0.01f,
                )
            }
        }
    }

    @Test
    fun `seats stay inside the table at any supported width`() {
        for (width in widths) {
            val seatWidth = opponentSeatWidthDp(width)
            val placements = opponentSeatPlacements(width, seatWidth)
            assertTrue("at ${width}dp the first seat is off-screen", placements.first().leftDp >= 0f)
            assertTrue(
                "at ${width}dp the last seat overflows by " +
                    "${placements.last().leftDp + seatWidth - width}dp",
                placements.last().leftDp + seatWidth <= width + 0.01f,
            )
        }
    }

    @Test
    fun `seat width grows with the table but stays legible`() {
        val narrow = opponentSeatWidthDp(320f)
        val phone = opponentSeatWidthDp(384f)
        val wide = opponentSeatWidthDp(480f)
        assertTrue("a wider table should never shrink seats", narrow <= phone && phone <= wide)
        assertTrue("a seat must stay wide enough to read a name", narrow >= 52f)
    }

    @Test
    fun `seats are staggered into an arc rather than one straight row`() {
        val lanes = opponentSeatPlacements(384f).map { it.centerYFraction }
        assertTrue("outer seats should sit lower than the crown", lanes[0] > lanes[2])
        assertEquals("the arc should be symmetric", lanes[0], lanes[4], 0.0005f)
        assertEquals("the arc should be symmetric", lanes[1], lanes[3], 0.0005f)
        assertTrue("the middle seat crowns the arc", lanes[2] < lanes[1])
    }

    /**
     * Every seat must land on the felt's edge. Hand-picked lanes left the
     * shoulder seats floating on the felt interior while the crown and side
     * seats straddled the rail, so the row looked inconsistent.
     */
    @Test
    fun `every seat sits on the felt rail at its own horizontal position`() {
        for (width in widths) {
            val seatWidth = opponentSeatWidthDp(width)
            opponentSeatPlacements(width, seatWidth).forEach { placement ->
                val centerX = (placement.leftDp + seatWidth / 2f) / width
                assertEquals(
                    "at ${width}dp a seat drifted off the rail",
                    railCenterYFraction(centerX),
                    placement.centerYFraction,
                    0.0001f,
                )
            }
        }
    }

    @Test
    fun `the rail solution is a real ellipse, not a flat line`() {
        val crown = railCenterYFraction(0.5f)
        val shoulder = railCenterYFraction(0.30f)
        val side = railCenterYFraction(0.06f)
        assertTrue("the crown is the highest point", crown < shoulder)
        assertTrue("the rail keeps dropping toward the sides", shoulder < side)
    }

    @Test
    fun `spacing is even so the row reads as a deliberate arrangement`() {
        val width = 384f
        val seatWidth = opponentSeatWidthDp(width)
        val gaps = opponentSeatPlacements(width, seatWidth)
            .zipWithNext { a, b -> b.leftDp - (a.leftDp + seatWidth) }
        val first = gaps.first()
        assertTrue("uneven seat gaps: $gaps", gaps.all { kotlin.math.abs(it - first) < 0.01f })
    }

    @Test
    fun `visible chips accumulate monotonically but remain bounded`() {
        val amounts = listOf(0, 1, 3, 7, 20, 50, 100, 500)
        val counts = amounts.map(::visibleChipCount)

        assertEquals(0, counts.first())
        assertEquals(7, counts.last())
        assertTrue(counts.zipWithNext().all { (left, right) -> left <= right })
    }
}
