package com.correx.apps.server.health

import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.io.TempDir
import org.junit.jupiter.api.Test
import java.nio.file.Files
import java.nio.file.Path

class DiskWatermarkProbeTest {

    /** Hand-advanced clock so growth-rate math is deterministic. */
    private class FakeClock(var current: Instant) : Clock {
        override fun now(): Instant = current
    }

    private fun writeFile(dir: Path, name: String, bytes: Int) {
        Files.write(dir.resolve(name), ByteArray(bytes))
    }

    @Test
    fun `healthy when under both thresholds`(@TempDir dir: Path): Unit = runBlocking {
        writeFile(dir, "a.bin", 100)
        val probe = DiskWatermarkProbe(listOf(dir), warnBytes = 10_000, warnGrowthBytesPerMin = 10_000)
        val obs = probe.observe()
        assertEquals(HealthStatus.HEALTHY, obs.status)
        assertEquals(100, obs.observedValue)
    }

    @Test
    fun `degraded when total size crosses the watermark`(@TempDir dir: Path): Unit = runBlocking {
        writeFile(dir, "big.bin", 2_000)
        val probe = DiskWatermarkProbe(listOf(dir), warnBytes = 1_000, warnGrowthBytesPerMin = Long.MAX_VALUE)
        val obs = probe.observe()
        assertEquals(HealthStatus.DEGRADED, obs.status)
        assertEquals("total_bytes", obs.metric)
        assertEquals(2_000, obs.observedValue)
        assertEquals(1_000, obs.threshold)
    }

    @Test
    fun `sums nested files recursively`(@TempDir dir: Path): Unit = runBlocking {
        val sub = Files.createDirectories(dir.resolve("artifacts/seg"))
        writeFile(dir, "log.db", 500)
        writeFile(sub, "seg-0.dat", 700)
        val probe = DiskWatermarkProbe(listOf(dir), warnBytes = Long.MAX_VALUE, warnGrowthBytesPerMin = Long.MAX_VALUE)
        assertEquals(1_200, probe.observe().observedValue)
    }

    @Test
    fun `first tick reports no growth then degrades on a fast-growing second tick`(@TempDir dir: Path): Unit =
        runBlocking {
            val clock = FakeClock(Instant.parse("2026-06-13T00:00:00Z"))
            writeFile(dir, "a.bin", 1_000)
            val probe = DiskWatermarkProbe(
                monitoredPaths = listOf(dir),
                warnBytes = Long.MAX_VALUE, // isolate the growth dimension
                warnGrowthBytesPerMin = 5_000,
                clock = clock,
            )
            // First tick: no baseline -> growth treated as 0 -> healthy.
            assertEquals(HealthStatus.HEALTHY, probe.observe().status)

            // One minute later, +10_000 bytes => 10_000 B/min, over the 5_000 threshold.
            clock.current = clock.current.plus(kotlin.time.Duration.parse("60s"))
            writeFile(dir, "b.bin", 10_000)
            val obs = probe.observe()
            assertEquals(HealthStatus.DEGRADED, obs.status)
            assertEquals("growth_bytes_per_min", obs.metric)
            assertTrue(obs.observedValue >= 5_000, "growth ${obs.observedValue} should be >= threshold")
        }

    @Test
    fun `missing path contributes zero`(@TempDir dir: Path): Unit = runBlocking {
        val probe = DiskWatermarkProbe(
            listOf(dir.resolve("does-not-exist")),
            warnBytes = 1,
            warnGrowthBytesPerMin = Long.MAX_VALUE,
        )
        val obs = probe.observe()
        assertEquals(0, obs.observedValue)
        assertEquals(HealthStatus.HEALTHY, obs.status)
    }
}
