package com.cf.imes.module.system.dal.redis.oauth2;

import com.alibaba.fastjson.JSON;
import com.cf.imes.framework.test.core.ut.BaseRedisUnitTest;
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.bean.override.mockito.MockitoBean;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * {@link OAuth2AccessTokenRedisDAO} 单元测试类
 *
 * @author Gqr
 * @since 2025/12/15 10:51
 */
@Import(OAuth2AccessTokenRedisDAO.class)
class OAuth2AccessTokenRedisDAOTest extends BaseRedisUnitTest {
    @Resource
    private OAuth2AccessTokenRedisDAO redisDAO;

    @MockitoBean
    private StringRedisTemplate stringRedisTemplate;

    private ValueOperations<String, String> valueOperations;

    @BeforeEach
    void setUp() {
        valueOperations = mock(ValueOperations.class);

        when(stringRedisTemplate.opsForValue()).thenReturn(valueOperations);
    }

    @Test
    void testGet() {
        String accessToken = "token123";
        String redisKey = String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, accessToken);

        OAuth2AccessTokenDO tokenDO = new OAuth2AccessTokenDO();
        tokenDO.setAccessToken(accessToken);

        when(valueOperations.get(redisKey)).thenReturn(JSON.toJSONString(tokenDO));

        OAuth2AccessTokenDO result = redisDAO.get(accessToken);

        assertEquals(accessToken, result.getAccessToken());
        verify(valueOperations).get(redisKey);
    }

    @Test
    void testGetWithKey() {
        String redisKey = "oauth2_access_token:token123";

        OAuth2AccessTokenDO tokenDO = new OAuth2AccessTokenDO();
        tokenDO.setAccessToken("token123");

        when(valueOperations.get(redisKey)).thenReturn(JSON.toJSONString(tokenDO));

        OAuth2AccessTokenDO result = redisDAO.getWithKey(redisKey);

        assertEquals("token123", result.getAccessToken());
        verify(valueOperations).get(redisKey);
    }

    @Test
    void testSet() {
        OAuth2AccessTokenDO tokenDO = new OAuth2AccessTokenDO();
        tokenDO.setAccessToken("token123");

        redisDAO.set(tokenDO);

        ArgumentCaptor<String> keyCaptor = ArgumentCaptor.forClass(String.class);
        ArgumentCaptor<String> valueCaptor = ArgumentCaptor.forClass(String.class);

        verify(valueOperations).set(
                keyCaptor.capture(),
                valueCaptor.capture(),
                eq(1L),
                eq(TimeUnit.DAYS)
        );

        assertEquals(
                String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, "token123"),
                keyCaptor.getValue()
        );
    }

    @Test
    void testDelete() {
        redisDAO.delete("token123");

        verify(stringRedisTemplate).delete(
                String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, "token123")
        );
    }

    @Test
    void testDeleteWithKey() {
        redisDAO.deleteWithKey("oauth2_access_token:token123");

        verify(stringRedisTemplate).delete("oauth2_access_token:token123");
    }

    @Test
    void testDeleteList() {
        redisDAO.deleteList(List.of("t1", "t2"));

        verify(stringRedisTemplate).delete(List.of(
                String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, "t1"),
                String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, "t2")
        ));
    }

    @Test
    void testExpire() {
        redisDAO.expire("token123", 10, TimeUnit.MINUTES);

        verify(stringRedisTemplate).expire(
                String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, "token123"),
                10,
                TimeUnit.MINUTES
        );
    }

    @Test
    void testScan() {
        Cursor<String> cursor = mock(Cursor.class);
        when(stringRedisTemplate.scan(any(ScanOptions.class))).thenReturn(cursor);

        Cursor<String> result = redisDAO.scan();

        assertEquals(cursor, result);
        verify(stringRedisTemplate).scan(any(ScanOptions.class));
    }
}
