package wang.yaojia.webterm.components

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import wang.yaojia.webterm.wire.PrInfo
import wang.yaojia.webterm.wire.RateInfo
import wang.yaojia.webterm.wire.StatusTelemetry
import wang.yaojia.webterm.wire.Tunables

/**
 * [TelemetryChips] pure logic (A22): the stale-TTL threshold ([isTelemetryStale]) and the chip
 * derivation ([telemetryChipModels]) — formatting, warning thresholds, absent-metric skipping. The
 * Compose row (greying, scroll) is device-QA.
 */
class TelemetryChipsTest {

    private val ttl = Tunables.TELEMETRY_STALE_TTL_MS

    // ── Staleness threshold ──────────────────────────────────────────────────────

    @Test
    fun `fresh telemetry is not stale`() {
        assertFalse(isTelemetryStale(atMs = 1_000L, nowMs = 1_000L))
        assertFalse(isTelemetryStale(atMs = 1_000L, nowMs = 1_000L + ttl / 2))
    }

    @Test
    fun `exactly at the TTL boundary is not yet stale (older-than, not at)`() {
        assertFalse(isTelemetryStale(atMs = 1_000L, nowMs = 1_000L + ttl))
    }

    @Test
    fun `one ms past the TTL is stale`() {
        assertTrue(isTelemetryStale(atMs = 1_000L, nowMs = 1_000L + ttl + 1))
    }

    // ── Chip derivation ──────────────────────────────────────────────────────────

    @Test
    fun `absent metrics produce no chips`() {
        assertEquals(emptyList<TelemetryChipModel>(), telemetryChipModels(StatusTelemetry(at = 0L)))
    }

    @Test
    fun `context cost model PR and rate render in order with formatting`() {
        val models = telemetryChipModels(
            StatusTelemetry(
                contextUsedPct = 41.6,
                costUsd = 0.5,
                model = "opus-4.8",
                pr = PrInfo(number = 7, url = "", reviewState = null),
                rate = RateInfo(fiveHourPct = 12.0),
                at = 0L,
            ),
        )
        assertEquals(
            listOf("ctx 42%", "$0.50", "opus-4.8", "PR #7", "5h 12%"),
            models.map { it.text },
        )
        assertTrue(models.none { it.isWarning }) // all below thresholds
    }

    @Test
    fun `near-full context turns the chip amber`() {
        val models = telemetryChipModels(StatusTelemetry(contextUsedPct = 90.0, at = 0L))
        assertEquals("ctx 90%", models.single().text)
        assertTrue(models.single().isWarning)
    }

    @Test
    fun `near-limit rate turns the chip amber`() {
        val models = telemetryChipModels(StatusTelemetry(rate = RateInfo(fiveHourPct = 95.0), at = 0L))
        assertTrue(models.single().isWarning)
    }

    @Test
    fun `a changes-requested PR turns the chip amber`() {
        val models = telemetryChipModels(
            StatusTelemetry(pr = PrInfo(number = 3, url = "", reviewState = "CHANGES_REQUESTED"), at = 0L),
        )
        assertEquals("PR #3", models.single().text)
        assertTrue(models.single().isWarning)
    }

    @Test
    fun `a blank model string is skipped`() {
        assertEquals(emptyList<TelemetryChipModel>(), telemetryChipModels(StatusTelemetry(model = "   ", at = 0L)))
    }
}
