package com.ruoyi.framework.web.service;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.cache.InMemoryCacheStore;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.framework.interceptor.impl.SameUrlDataInterceptor;
import com.ruoyi.system.service.ISysConfigService;

class TokenServiceLocalCacheTest
{
    @AfterEach
    void clearRequestContext()
    {
        RequestContextHolder.resetRequestAttributes();
    }

    @Test
    void shouldStoreLoginUserWithTokenTtl()
    {
        RedisCache redisCache = new RedisCache(new InMemoryCacheStore());
        TokenService tokenService = new TokenService();
        ReflectionTestUtils.setField(tokenService, "redisCache", redisCache);
        ReflectionTestUtils.setField(tokenService, "header", "Authorization");
        ReflectionTestUtils.setField(tokenService, "secret", "unit-test-secret");
        ReflectionTestUtils.setField(tokenService, "expireTime", 30);

        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRemoteAddr("127.0.0.1");
        request.addHeader("User-Agent", "Mozilla/5.0");
        RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));

        SysUser user = new SysUser();
        user.setUserName("admin");
        user.setPassword("password");
        LoginUser loginUser = new LoginUser();
        loginUser.setUser(user);

        String jwt = tokenService.createToken(loginUser);

        assertNotNull(jwt);
        assertNotNull(redisCache.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + loginUser.getToken()));
        assertTrue(redisCache.getExpire(CacheConstants.LOGIN_TOKEN_KEY + loginUser.getToken()) > 0);
    }

    @Test
    void shouldDeleteCaptchaAfterValidation()
    {
        RedisCache redisCache = new RedisCache(new InMemoryCacheStore());
        SysLoginService loginService = new SysLoginService();
        ISysConfigService configService = mock(ISysConfigService.class);
        when(configService.selectCaptchaEnabled()).thenReturn(true);
        ReflectionTestUtils.setField(loginService, "redisCache", redisCache);
        ReflectionTestUtils.setField(loginService, "configService", configService);

        redisCache.setCacheObject(CacheConstants.CAPTCHA_CODE_KEY + "uuid", "ABCD", 1, TimeUnit.MINUTES);

        loginService.validateCaptcha("admin", "ABCD", "uuid");

        assertFalse(redisCache.hasKey(CacheConstants.CAPTCHA_CODE_KEY + "uuid"));
    }

    @Test
    void shouldSupportMillisecondRepeatSubmitWindow() throws Exception
    {
        RedisCache redisCache = new RedisCache(new InMemoryCacheStore());
        SameUrlDataInterceptor interceptor = new SameUrlDataInterceptor();
        ReflectionTestUtils.setField(interceptor, "redisCache", redisCache);
        ReflectionTestUtils.setField(interceptor, "header", "Authorization");

        RepeatSubmit repeatSubmit = RepeatSubmitTarget.class.getDeclaredMethod("submit")
                .getAnnotation(RepeatSubmit.class);

        MockHttpServletRequest firstRequest = createRepeatRequest();
        assertFalse(interceptor.isRepeatSubmit(firstRequest, repeatSubmit));

        MockHttpServletRequest secondRequest = createRepeatRequest();
        assertTrue(interceptor.isRepeatSubmit(secondRequest, repeatSubmit));

        Thread.sleep(70);
        MockHttpServletRequest thirdRequest = createRepeatRequest();
        assertFalse(interceptor.isRepeatSubmit(thirdRequest, repeatSubmit));
    }

    private MockHttpServletRequest createRepeatRequest()
    {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/loan/pricing");
        request.addHeader("Authorization", "token-1");
        request.addParameter("loanId", "1001");
        return request;
    }

    static class RepeatSubmitTarget
    {
        @RepeatSubmit(interval = 50)
        public void submit()
        {
        }
    }
}
