package org.dromara.web.controller;

import cn.dev33.satoken.stp.StpUtil;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mail.config.properties.MailProperties;
import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;

import java.lang.reflect.Method;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@Tag("dev")
class CaptchaControllerUnitTest {

    @Mock
    private MailProperties mailProperties;
    @Mock
    private ISysUserService userService;

    @Test
    void accountCancelCode_sendsSmsCodeWhenCurrentUserHasPhone() {
        CaptchaController controller = spy(new CaptchaController(null, mailProperties, userService));
        SysUserVo user = new SysUserVo();
        user.setPhonenumber("13305376054");
        user.setEmail("demo@example.com");
        when(userService.selectUserById(100L)).thenReturn(user);
        doReturn(R.ok("操作成功")).when(controller).sendSmsCode("13305376054", "zhangsan");

        try (MockedStatic<StpUtil> stpUtil = mockStatic(StpUtil.class);
             MockedStatic<SpringUtils> springUtils = mockStatic(SpringUtils.class);
             MockedStatic<LoginHelper> loginHelper = mockStatic(LoginHelper.class)) {
            loginHelper.when(LoginHelper::getUserId).thenReturn(100L);
            loginHelper.when(LoginHelper::getUsername).thenReturn("zhangsan");
            springUtils.when(() -> SpringUtils.getAopProxy(controller)).thenReturn(controller);

            R<Void> result = controller.accountCancelCode();

            assertThat(result.getCode()).isEqualTo(200);
            stpUtil.verify(StpUtil::checkLogin);
            verify(controller).sendSmsCode("13305376054", "zhangsan");
            verify(controller, never()).emailCodeImpl("demo@example.com");
        }
    }

    @Test
    void accountCancelCode_sendsEmailCodeWhenCurrentUserHasNoPhone() {
        CaptchaController controller = spy(new CaptchaController(null, mailProperties, userService));
        SysUserVo user = new SysUserVo();
        user.setEmail("demo@example.com");
        when(userService.selectUserById(100L)).thenReturn(user);
        when(mailProperties.getEnabled()).thenReturn(true);
        doNothing().when(controller).emailCodeImpl("demo@example.com", "zhangsan");

        try (MockedStatic<StpUtil> stpUtil = mockStatic(StpUtil.class);
             MockedStatic<SpringUtils> springUtils = mockStatic(SpringUtils.class);
             MockedStatic<LoginHelper> loginHelper = mockStatic(LoginHelper.class)) {
            loginHelper.when(LoginHelper::getUserId).thenReturn(100L);
            loginHelper.when(LoginHelper::getUsername).thenReturn("zhangsan");
            springUtils.when(() -> SpringUtils.getAopProxy(controller)).thenReturn(controller);

            R<Void> result = controller.accountCancelCode();

            assertThat(result.getCode()).isEqualTo(200);
            stpUtil.verify(StpUtil::checkLogin);
            springUtils.verify(() -> SpringUtils.getAopProxy(controller));
            verify(controller).emailCodeImpl("demo@example.com", "zhangsan");
            verify(controller, never()).sendSmsCode("demo@example.com", "zhangsan");
        }
    }


    @Test
    void accountCancelCode_hasPerUserRateLimiter() throws NoSuchMethodException {
        Method method = CaptchaController.class.getDeclaredMethod("accountCancelCode");

        RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class);

        assertThat(rateLimiter).isNotNull();
        assertThat(rateLimiter.time()).isEqualTo(60);
        assertThat(rateLimiter.count()).isEqualTo(1);
        assertThat(rateLimiter.key())
            .isEqualTo("#{T(org.dromara.common.satoken.utils.LoginHelper).getUserId()}");
    }

    @Test
    void accountCancelCode_failsWhenCurrentUserHasNoPhoneOrEmail() {
        CaptchaController controller = spy(new CaptchaController(null, mailProperties, userService));
        SysUserVo user = new SysUserVo();
        when(userService.selectUserById(100L)).thenReturn(user);

        try (MockedStatic<StpUtil> stpUtil = mockStatic(StpUtil.class);
             MockedStatic<SpringUtils> springUtils = mockStatic(SpringUtils.class);
             MockedStatic<LoginHelper> loginHelper = mockStatic(LoginHelper.class)) {
            loginHelper.when(LoginHelper::getUserId).thenReturn(100L);
            loginHelper.when(LoginHelper::getUsername).thenReturn("zhangsan");
            springUtils.when(() -> SpringUtils.getAopProxy(controller)).thenReturn(controller);

            R<Void> result = controller.accountCancelCode();

            assertThat(result.getCode()).isEqualTo(500);
            assertThat(result.getMsg()).isEqualTo("当前账号未绑定手机号或邮箱");
            stpUtil.verify(StpUtil::checkLogin);
            verify(controller, never()).sendSmsCode("13305376054", "zhangsan");
            verify(controller, never()).emailCodeImpl("demo@example.com", "zhangsan");
        }
    }
}
