package com.correx.apps.server.health

import com.correx.infrastructure.persistence.InMemoryEventStore
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class EventStoreLatencyProbeTest {

    /** Hand-advanced nano source: the [measure] lambda decides how much wall-clock the probe "saw". */
    private class FakeNanos {
        var now: Long = 0L
        fun read(): Long = now
        fun advanceMs(ms: Long) {
            now += ms * 1_000_000L
        }
    }

    @Test
    fun `healthy when latency under threshold`(): Unit = runBlocking {
        val nanos = FakeNanos()
        val probe = EventStoreLatencyProbe(
            warnLatencyMs = 50,
            nanoTime = nanos::read,
        ) { nanos.advanceMs(10) }
        val obs = probe.observe()
        assertEquals(HealthStatus.HEALTHY, obs.status)
        assertEquals("latency_ms", obs.metric)
        assertEquals(10, obs.observedValue)
    }

    @Test
    fun `degraded when latency crosses threshold`(): Unit = runBlocking {
        val nanos = FakeNanos()
        val probe = EventStoreLatencyProbe(
            warnLatencyMs = 50,
            nanoTime = nanos::read,
        ) { nanos.advanceMs(120) }
        val obs = probe.observe()
        assertEquals(HealthStatus.DEGRADED, obs.status)
        assertEquals("latency_ms", obs.metric)
        assertEquals(120, obs.observedValue)
        assertEquals(50, obs.threshold)
    }

    @Test
    fun `degraded when the timed read throws`(): Unit = runBlocking {
        val probe = EventStoreLatencyProbe(warnLatencyMs = 50) { error("store down") }
        val obs = probe.observe()
        assertEquals(HealthStatus.DEGRADED, obs.status)
        assertEquals("latency_ms", obs.metric)
        assertTrue(obs.detail.contains("store down"), "detail should name the failure: ${obs.detail}")
    }

    @Test
    fun `forStore reads a real store and stays healthy under a generous watermark`(): Unit = runBlocking {
        val store = InMemoryEventStore()
        val probe = EventStoreLatencyProbe.forStore(store, warnLatencyMs = Long.MAX_VALUE)
        assertEquals(HealthStatus.HEALTHY, probe.observe().status)
    }
}
