package kr.or.bok.rtgs.gucci

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue

/** Gucci 관문 가드(유량제어·재전송·JWT) 결정론적 검증. */
class GucciGuardsTest {

    @Test
    fun `rate limiter allows up to limit per window then blocks, resets next window`() {
        val rl = RateLimiter(limit = 3, windowSeconds = 10)
        val t = 100_000L // 100초 시점(같은 10s 윈도우)
        assertTrue(rl.allow("kookmin_s", t))
        assertTrue(rl.allow("kookmin_s", t))
        assertTrue(rl.allow("kookmin_s", t))
        assertTrue(!rl.allow("kookmin_s", t))            // 4번째 초과 → 차단
        assertTrue(rl.allow("shinhan_r", t))             // 다른 기관은 독립 버킷
        assertTrue(rl.allow("kookmin_s", t + 10_000))    // 다음 윈도우 → 리셋
    }

    @Test
    fun `replay guard rejects reuse, missing, and out-of-window`() {
        val rg = ReplayGuard(skewSeconds = 30, nonceTtlSeconds = 120)
        val nowMs = 1_000_000_000_000L
        val nowSec = nowMs / 1000
        assertTrue(rg.check("n1", nowSec, nowMs) is ReplayGuard.Result.Ok)
        assertTrue(rg.check("n1", nowSec, nowMs) is ReplayGuard.Result.Reject)   // 재사용
        assertTrue(rg.check(null, nowSec, nowMs) is ReplayGuard.Result.Reject)   // nonce 없음
        assertTrue(rg.check("n2", nowSec - 60, nowMs) is ReplayGuard.Result.Reject) // 시각오차 초과
        assertTrue(rg.check("n3", nowSec, nowMs) is ReplayGuard.Result.Ok)
    }

    @Test
    fun `jwt round-trips, rejects tamper and expiry`() {
        val jwt = JwtService(jacksonObjectMapper(), secret = "unit-test-secret-key-0123456789", ttlSeconds = 1800)
        val now = 1_000_000_000_000L
        val token = jwt.issue(Principal("kookmin_s", "ORG_S", "1001"), now)
        val p = jwt.verify(token, now)
        assertEquals("kookmin_s", p.subject)
        assertEquals("ORG_S", p.role)
        assertEquals("1001", p.orgCode)
        assertFailsWith<JwtException> { jwt.verify(token + "x", now) }              // 변조
        assertFailsWith<JwtException> { jwt.verify(token, now + 1_801_000L) }       // 만료(ttl 초과)
    }
}
