package kr.or.bok.rtgs.hermes

import kr.or.bok.rtgs.common.CoreMessage
import kr.or.bok.rtgs.common.JournalEntry
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

/** B3 순차 게이트(버퍼링·재정렬·phantom skip) 규율 고정 테스트. */
class GapBufferTest {

    private fun entry(seq: Long): JournalEntry = JournalEntry(
        globalSeq = seq,
        seqEpochMillis = 1_000L + seq,
        originCenter = "DC1",
        core = CoreMessage(
            bmi = "bmi%022d".format(seq), msgType = "pacs.008.001.08",
            senderCode = "1001", receiverCode = "1002",
            debtorAcct = "1001", creditorAcct = "1002",
            amount = 100, origHash = "h",
        ),
    )

    private fun seqs(list: List<JournalEntry>) = list.map { it.globalSeq }

    @Test
    fun `순서대로 오면 즉시 적용`() {
        val g = GapBuffer()
        assertEquals(listOf(1L), seqs(g.offer(entry(1), 10)))
        assertEquals(listOf(2L), seqs(g.offer(entry(2), 11)))
        assertEquals(3L, g.expected)
    }

    @Test
    fun `역전 도착은 버퍼링 후 결번 채워지면 한꺼번에 방출`() {
        val g = GapBuffer()
        assertEquals(listOf(1L), seqs(g.offer(entry(1), 10)))
        assertEquals(emptyList<Long>(), seqs(g.offer(entry(3), 11))) // gap: 2 미도착 → 버퍼
        assertEquals(emptyList<Long>(), seqs(g.offer(entry(4), 12))) // 버퍼
        assertEquals(2, g.pendingCount())
        assertEquals(listOf(2L, 3L, 4L), seqs(g.offer(entry(2), 13))) // 2 도착 → 2,3,4 연속 방출
        assertEquals(5L, g.expected)
        assertEquals(0, g.pendingCount())
    }

    @Test
    fun `과거 중복 seq는 무시`() {
        val g = GapBuffer()
        g.offer(entry(1), 10); g.offer(entry(2), 11)
        assertEquals(emptyList<Long>(), seqs(g.offer(entry(1), 12))) // 이미 지난 seq
        assertEquals(3L, g.expected)
    }

    @Test
    fun `첫 엔트리가 중간 seq여도 그 값으로 앵커링`() {
        val g = GapBuffer()
        assertEquals(listOf(5L), seqs(g.offer(entry(5), 10))) // 재기동 후 earliest=5
        assertEquals(6L, g.expected)
    }

    @Test
    fun `정체 감지 phantom skip으로 진행 재개`() {
        val g = GapBuffer()
        g.offer(entry(1), 100)                 // 적용, expected=2, lastAdvance=100
        g.offer(entry(3), 101)                 // 2 결번 → 3 버퍼
        assertEquals(50L, g.stalledMillis(150)) // 150-100 정체
        // 타임아웃: 2는 phantom → skip → 3 방출, expected=4
        assertEquals(listOf(3L), seqs(g.skipExpected(160)))
        assertEquals(4L, g.expected)
        assertEquals(0, g.stalledMillis(200))   // 대기자 없음 → 정체 아님
    }

    @Test
    fun `대기자 없으면 정체 0 이고 skip은 무동작`() {
        val g = GapBuffer()
        g.offer(entry(1), 10)
        assertEquals(0L, g.stalledMillis(9999))
        assertEquals(emptyList<Long>(), seqs(g.skipExpected(9999)))
        assertEquals(2L, g.expected) // 변화 없음
    }
}
