package com.zioinfo.kintex.ticket.smart;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * F-B1 회전 QR 토큰(TOTP) 단위테스트 — 30초마다 회전·현재/직전 창 검증·타 창 무효(스크린샷 방지).
 */
class TotpTest {

    private static final String SECRET = "0123456789abcdef0123456789abcdef";

    @Test
    void codeIsDeterministicWithinSameStep() {
        // 같은 30초 창(동일 counter=40, 창 [1200,1230)) 안에서는 동일 토큰.
        assertEquals(Totp.code(SECRET, 1200), Totp.code(SECRET, 1200 + 29));
        assertEquals(8, Totp.code(SECRET, 1200).length());
    }

    @Test
    void codeRotatesAcrossSteps() {
        // 다음 창(다음 30초)에는 토큰이 회전.
        assertNotEquals(Totp.code(SECRET, 1000), Totp.code(SECRET, 1000 + Totp.STEP_SECONDS));
    }

    @Test
    void verifyAcceptsCurrentAndPreviousWindow() {
        long now = 5_000;
        String current = Totp.code(SECRET, now);
        String prev = Totp.code(SECRET, now - Totp.STEP_SECONDS);
        assertTrue(Totp.verify(SECRET, current, now, 1));
        assertTrue(Totp.verify(SECRET, prev, now, 1));      // 직전 창 유예 허용
        assertTrue(Totp.verify(SECRET, current.toLowerCase(), now, 1));  // 대소문자 무시
    }

    @Test
    void verifyRejectsOldOrForeignToken() {
        long now = 5_000;
        // 캡처 스크린샷 시나리오: 2창 이전 토큰은 유예(1창) 밖 → 무효.
        String twoStepsAgo = Totp.code(SECRET, now - 2L * Totp.STEP_SECONDS);
        assertFalse(Totp.verify(SECRET, twoStepsAgo, now, 1));
        // 다른 시크릿(양도된 코드) → 무효.
        assertFalse(Totp.verify("ffffffffffffffffffffffffffffffff", Totp.code(SECRET, now), now, 1));
    }

    @Test
    void remainingSecondsWithinStep() {
        int r = Totp.remainingSeconds(1000);
        assertTrue(r >= 1 && r <= Totp.STEP_SECONDS);
    }
}
