package com.ruoyi.web.controller.system;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.util.Collections;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysPostService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;

class SysUserControllerPasswordTransferTest
{
    @AfterEach
    void tearDown()
    {
        SecurityContextHolder.clearContext();
    }

    @Test
    void shouldDecryptPasswordBeforeAddingUser() throws Exception
    {
        ISysUserService userService = mock(ISysUserService.class);
        ISysRoleService roleService = mock(ISysRoleService.class);
        ISysDeptService deptService = mock(ISysDeptService.class);
        ISysPostService postService = mock(ISysPostService.class);
        PasswordTransferCryptoService passwordTransferCryptoService = mock(PasswordTransferCryptoService.class);
        when(passwordTransferCryptoService.decrypt("cipher")).thenReturn("initPwd");
        when(userService.checkUserNameUnique(org.mockito.ArgumentMatchers.any(SysUser.class))).thenReturn(true);
        when(userService.insertUser(org.mockito.ArgumentMatchers.any(SysUser.class))).thenReturn(1);

        setAuthentication();

        SysUserController controller = new SysUserController();
        ReflectionTestUtils.setField(controller, "userService", userService);
        ReflectionTestUtils.setField(controller, "roleService", roleService);
        ReflectionTestUtils.setField(controller, "deptService", deptService);
        ReflectionTestUtils.setField(controller, "postService", postService);
        ReflectionTestUtils.setField(controller, "passwordTransferCryptoService", passwordTransferCryptoService);

        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();

        mockMvc.perform(post("/system/user")
                .contentType("application/json")
                .content("{\"userName\":\"u1\",\"nickName\":\"n1\",\"deptId\":1,\"password\":\"cipher\"}"))
                .andExpect(status().isOk());

        verify(passwordTransferCryptoService).decrypt("cipher");
        ArgumentCaptor<SysUser> captor = ArgumentCaptor.forClass(SysUser.class);
        verify(userService).insertUser(captor.capture());
        assertTrue(SecurityUtils.matchesPassword("initPwd", captor.getValue().getPassword()));
    }

    @Test
    void shouldDecryptPasswordBeforeResettingUserPassword() throws Exception
    {
        ISysUserService userService = mock(ISysUserService.class);
        ISysRoleService roleService = mock(ISysRoleService.class);
        ISysDeptService deptService = mock(ISysDeptService.class);
        ISysPostService postService = mock(ISysPostService.class);
        PasswordTransferCryptoService passwordTransferCryptoService = mock(PasswordTransferCryptoService.class);
        when(passwordTransferCryptoService.decrypt("cipher")).thenReturn("resetPwd");
        when(userService.resetPwd(org.mockito.ArgumentMatchers.any(SysUser.class))).thenReturn(1);

        setAuthentication();

        SysUserController controller = new SysUserController();
        ReflectionTestUtils.setField(controller, "userService", userService);
        ReflectionTestUtils.setField(controller, "roleService", roleService);
        ReflectionTestUtils.setField(controller, "deptService", deptService);
        ReflectionTestUtils.setField(controller, "postService", postService);
        ReflectionTestUtils.setField(controller, "passwordTransferCryptoService", passwordTransferCryptoService);

        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();

        mockMvc.perform(put("/system/user/resetPwd")
                .contentType("application/json")
                .content("{\"userId\":2,\"password\":\"cipher\"}"))
                .andExpect(status().isOk());

        verify(passwordTransferCryptoService).decrypt("cipher");
        ArgumentCaptor<SysUser> captor = ArgumentCaptor.forClass(SysUser.class);
        verify(userService).resetPwd(captor.capture());
        assertTrue(SecurityUtils.matchesPassword("resetPwd", captor.getValue().getPassword()));
    }

    private void setAuthentication()
    {
        SysUser currentUser = new SysUser();
        currentUser.setUserId(1L);
        currentUser.setUserName("admin");
        LoginUser loginUser = new LoginUser(1L, 1L, currentUser, Collections.emptySet());
        SecurityContextHolder.getContext()
                .setAuthentication(new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList()));
    }
}
