package kr.xit.biz.nice.service;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.*;
import org.springframework.transaction.annotation.Transactional;

import kr.xit.biz.cmm.service.*;
import kr.xit.biz.common.ApiConstants;
import kr.xit.biz.ens.model.nice.*;
import kr.xit.biz.ens.model.nice.NiceCiDTO.NiceCiInfo;
import kr.xit.biz.ens.model.nice.NiceCiDTO.NiceCiRequest;
import kr.xit.biz.ens.model.nice.NiceCiDTO.NiceTokenResponse;
import kr.xit.biz.ens.model.nice.NiceCiDTO.ResponseDataHeader;
import kr.xit.biz.ens.model.nice.NiceCiDTO.TokenRevokeResponse;
import kr.xit.biz.ens.model.nice.NiceCiDTO.TokenResDataBody;
import kr.xit.biz.nice.mapper.IBizNiceCiMapper;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.ens.nice.service.*;

import static kr.xit.core.support.utils.JsonUtils.toJson;

@SpringBootTest
@ActiveProfiles("local-ccn")
class BizNiceCiServiceTest {

    @Mock
    private INiceCiService niceCiService;

    @Mock
    private CmmEnsCacheService cacheService;

    @Mock
    private IBizNiceCiMapper niceCiMapper;

    @InjectMocks
    private BizNiceCiService bizNiceCiService;

    public BizNiceCiServiceTest() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    @Transactional
    void testGenerateToken_whenTokenGeneratedSuccessfully_DoesNotRevokeTokens() {
        NiceCiRequest reqDTO = NiceCiRequest.builder()
            .signguCode("88328")
            .ffnlgCode("11")
            .build();

        ResponseDataHeader responseHeader = new ResponseDataHeader();
        responseHeader.setGwRsltCd("1200");

        TokenResDataBody responseBody = new TokenResDataBody();
        responseBody.setAccessToken("ThisIsToken");
        responseBody.setExpiresIn(3600);
        responseBody.setTokenType("Bearer");
        responseBody.setScope("read");

        NiceTokenResponse initialResponse = new NiceTokenResponse();
        initialResponse.setDataHeader(responseHeader);
        initialResponse.setDataBody(responseBody);

        when(niceCiService.generateToken(reqDTO)).thenReturn(initialResponse);

        NiceTokenResponse result = bizNiceCiService.generateToken(reqDTO);

        assertNotNull(result);
        assertEquals("1200", result.getDataHeader().getGwRsltCd());
        verify(niceCiService, times(1)).generateToken(reqDTO);
        verify(niceCiMapper, times(1)).updateNiceCrtfToken(any(NiceCiInfo.class));
        verify(cacheService, times(1)).removeNiceCiInfoCache(reqDTO.getSignguCode(), reqDTO.getFfnlgCode());
    }

    @Test
    @Transactional
    void testGenerateToken_whenPreviousTokenRevokedAndNewTokenGenerated() {
        NiceCiRequest reqDTO = NiceCiRequest.builder()
            .signguCode("88328")
            .ffnlgCode("11")
            .build();

        ResponseDataHeader initialHeader = new ResponseDataHeader();
        initialHeader.setGwRsltCd("1800");

        NiceTokenResponse initialResponse = new NiceTokenResponse();
        initialResponse.setDataHeader(initialHeader);

        ResponseDataHeader revokeHeader = new ResponseDataHeader();
        revokeHeader.setGwRsltCd("1200");

        TokenRevokeResponse revokedResponse = new TokenRevokeResponse();
        revokedResponse.setDataHeader(revokeHeader);
        revokedResponse.setDataBody(new NiceCiDTO.TokenRevokeResDataBody(true));

        ResponseDataHeader finalHeader = new ResponseDataHeader();
        finalHeader.setGwRsltCd("1200");

        TokenResDataBody finalBody = new TokenResDataBody();
        finalBody.setAccessToken("NewAccessToken");
        finalBody.setExpiresIn(7200);
        finalBody.setTokenType("Bearer");
        finalBody.setScope("write");

        NiceTokenResponse finalResponse = new NiceTokenResponse();
        finalResponse.setDataHeader(finalHeader);
        finalResponse.setDataBody(finalBody);

        when(niceCiService.generateToken(reqDTO))
            .thenReturn(initialResponse)
            .thenReturn(finalResponse);

        when(niceCiService.revokeToken(reqDTO)).thenReturn(revokedResponse);

        NiceTokenResponse result = bizNiceCiService.generateToken(reqDTO);

        assertNotNull(result);
        assertEquals("1200", result.getDataHeader().getGwRsltCd());
        assertEquals("NewAccessToken", result.getDataBody().getAccessToken());
        verify(niceCiService, times(2)).generateToken(reqDTO);
        verify(niceCiService, times(1)).revokeToken(reqDTO);
        verify(niceCiMapper, times(1)).updateNiceCrtfToken(any(NiceCiInfo.class));
        verify(cacheService, times(1)).removeNiceCiInfoCache(reqDTO.getSignguCode(), reqDTO.getFfnlgCode());
    }

    @Test
    @Transactional
    void testGenerateToken_whenTokenRevocationFails_ThrowsException() {
        NiceCiRequest reqDTO = NiceCiRequest.builder()
            .signguCode("88328")
            .ffnlgCode("11")
            .build();

        ResponseDataHeader initialHeader = new ResponseDataHeader();
        initialHeader.setGwRsltCd("1800");

        NiceTokenResponse initialResponse = new NiceTokenResponse();
        initialResponse.setDataHeader(initialHeader);

        ResponseDataHeader revokeHeader = new ResponseDataHeader();
        revokeHeader.setGwRsltCd("1200");

        TokenRevokeResponse failedRevokeResponse = new TokenRevokeResponse();
        failedRevokeResponse.setDataHeader(revokeHeader);
        failedRevokeResponse.setDataBody(new NiceCiDTO.TokenRevokeResDataBody(false));

        when(niceCiService.generateToken(reqDTO)).thenReturn(initialResponse);
        when(niceCiService.revokeToken(reqDTO)).thenReturn(failedRevokeResponse);

        BizRuntimeException exception = assertThrows(BizRuntimeException.class, () -> {
            bizNiceCiService.generateToken(reqDTO);
        });

        assertNotNull(exception);
        verify(niceCiService, times(1)).generateToken(reqDTO);
        verify(niceCiService, times(1)).revokeToken(reqDTO);
        verify(niceCiMapper, never()).updateNiceCrtfToken(any(NiceCiInfo.class));
        verify(cacheService, never()).removeNiceCiInfoCache(eq(reqDTO.getSignguCode()), eq(reqDTO.getFfnlgCode()));
    }
}