package me.khodak.claudeusage

import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test

/**
 * Pure-JVM tests for PaceCalc.compute. The clock is injected via the `now` parameter, so these are
 * deterministic and require no Android framework.
 */
class PaceCalcTest {

    private val window = PaceCalc.SESSION_WINDOW_MS // 5h

    @Test
    fun computeReturnsPaceForValidFutureReset() {
        // Place "now" halfway through the window (elapsedFraction = 0.5, within [0.03, 1.0)).
        val resetEpoch = 10_000_000_000L
        val now = resetEpoch - window / 2
        val pace = PaceCalc.compute(usedPct = 25f, resetEpoch = resetEpoch, windowMs = window, now = now)

        assertNotNull(pace)
        val p = pace!!
        // markerPct mirrors elapsedFraction*100 → ~50, and must stay in 0..100.
        assertTrue("markerPct in range", p.markerPct in 0..100)
        assertEquals50(p.markerPct)
    }

    @Test
    fun markerPctAlwaysInRangeNearWindowEnd() {
        // Near the end of the window (95% elapsed, still < 1.0) marker approaches 100, never exceeds.
        // Kept off the exact boundary so Float rounding of (window-elapsed)/window can't tip it to 1.0.
        val resetEpoch = 10_000_000_000L
        val now = resetEpoch - (window * 5L / 100L) // 95% elapsed
        val pace = PaceCalc.compute(usedPct = 80f, resetEpoch = resetEpoch, windowMs = window, now = now)

        assertNotNull(pace)
        assertTrue(pace!!.markerPct in 0..100)
    }

    @Test
    fun computeReturnsNullForPastReset() {
        // Reset already passed → elapsedFraction >= 1.0 → null.
        val resetEpoch = 10_000_000_000L
        val now = resetEpoch + 60_000L
        assertNull(PaceCalc.compute(usedPct = 50f, resetEpoch = resetEpoch, windowMs = window, now = now))
    }

    @Test
    fun computeReturnsNullTooEarlyInWindow() {
        // Less than 3% elapsed → not enough signal → null.
        val resetEpoch = 10_000_000_000L
        val now = resetEpoch - window + (window * 0.01f).toLong() // 1% elapsed
        assertNull(PaceCalc.compute(usedPct = 5f, resetEpoch = resetEpoch, windowMs = window, now = now))
    }

    @Test
    fun computeReturnsNullForNonPositiveReset() {
        assertNull(PaceCalc.compute(usedPct = 50f, resetEpoch = 0L, windowMs = window))
        assertNull(PaceCalc.compute(usedPct = 50f, resetEpoch = -1L, windowMs = window))
    }

    @Test
    fun computeReturnsNullForNegativeUsedPct() {
        val resetEpoch = 10_000_000_000L
        val now = resetEpoch - window / 2
        assertNull(PaceCalc.compute(usedPct = -1f, resetEpoch = resetEpoch, windowMs = window, now = now))
    }

    private fun assertEquals50(markerPct: Int) {
        // elapsedFraction 0.5 → (0.5*100).toInt() == 50.
        org.junit.Assert.assertEquals(50, markerPct)
    }
}
