package com.zioinfo.kintex.webhook;

import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import com.zioinfo.kintex.security.SecretCipher;
import com.zioinfo.kintex.security.SecurityPolicyProperties;
import com.zioinfo.kintex.webhook.dto.WebhookDtos.InboundReceipt;
import org.junit.jupiter.api.Test;

import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * F099 인바운드 수신 서비스 — 토큰 + HMAC 이중 검증 흐름 단위테스트.
 */
class WebhookInboundServiceTest {

    private final WebhookSignature signature = new WebhookSignature();
    private final SecretCipher cipher = new SecretCipher(new SecurityPolicyProperties());

    private WebhookService service(WebhookMapper mapper) {
        return new WebhookService(mapper, signature, cipher);
    }

    @Test
    void acceptsValidSignatureAndStores() {
        WebhookMapper mapper = mock(WebhookMapper.class);
        String secret = "whsec_inbound_test";
        // 저장된 봉인 시크릿 반환.
        when(mapper.findInboundSecretEnc("tok-1"))
                .thenReturn(Map.of("secretEnc", cipher.seal(secret)));

        String body = "{\"type\":\"invoice.paid\"}";
        String validSig = signature.sign(secret, body);

        InboundReceipt receipt = service(mapper).receiveInbound("tok-1", body, validSig, "invoice.paid");

        assertTrue(receipt.accepted());
        verify(mapper, times(1)).insertInboundEvent(anyString(), eq("tok-1"),
                eq("invoice.paid"), eq(body), eq(true));
    }

    @Test
    void rejectsInvalidSignatureWithoutStoring() {
        WebhookMapper mapper = mock(WebhookMapper.class);
        when(mapper.findInboundSecretEnc("tok-1"))
                .thenReturn(Map.of("secretEnc", cipher.seal("real-secret")));

        ApiException ex = assertThrows(ApiException.class,
                () -> service(mapper).receiveInbound("tok-1", "{}", "deadbeef", "x"));
        assertEquals(ErrorCode.UNAUTHORIZED, ex.getCode());
        // 무효 서명은 적재하지 않는다(DoS/스팸 완화).
        verify(mapper, never()).insertInboundEvent(anyString(), anyString(), anyString(), anyString(), anyBoolean());
    }

    @Test
    void rejectsUnknownToken() {
        WebhookMapper mapper = mock(WebhookMapper.class);
        when(mapper.findInboundSecretEnc(anyString())).thenReturn(null);

        ApiException ex = assertThrows(ApiException.class,
                () -> service(mapper).receiveInbound("nope", "{}", "sig", "x"));
        assertEquals(ErrorCode.UNAUTHORIZED, ex.getCode());
    }
}
