package tech.easyflow.admin.controller.system;

import com.alibaba.fastjson2.JSONObject;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.mockito.MockedStatic;
import org.testng.annotations.Test;
import tech.easyflow.auth.service.AuthCredentialKeyService;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.admin.controller.system.vo.SysAccountProfileVo;
import tech.easyflow.admin.service.system.SystemFormOptionService;
import tech.easyflow.system.entity.SysAccount;
import tech.easyflow.system.entity.SysRole;
import tech.easyflow.system.service.SysAccountService;
import tech.easyflow.system.service.SysRoleService;

import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

/**
 * {@link SysAccountController} 创建账号测试。
 */
public class SysAccountControllerTest {

    /**
     * 验证统一关键字覆盖账号字段和关联角色名称，并保留租户关联条件。
     */
    @Test
    public void buildQueryWrapperShouldSearchAccountFieldsAndRoleName() {
        SysAccountController controller = new SysAccountController(
                mock(SysAccountService.class),
                mock(AuthCredentialKeyService.class),
                mock(SysRoleService.class),
                mock(SystemFormOptionService.class)
        );
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getParameter("keyword")).thenReturn("  search-user  ");

        QueryWrapper queryWrapper = controller.buildQueryWrapper(request);

        String sql = queryWrapper.toSQL().toLowerCase(Locale.ROOT);
        assertTrue(sql.contains("login_name"), sql);
        assertTrue(sql.contains("nickname"), sql);
        assertTrue(sql.contains("mobile"), sql);
        assertTrue(sql.contains("email"), sql);
        assertTrue(sql.contains("exists"), sql);
        assertTrue(sql.contains("tb_sys_account_role"), sql);
        assertTrue(sql.contains("tb_sys_role"), sql);
        assertTrue(sql.contains("role_name"), sql);
        assertTrue(sql.contains("tenant_id"), sql);
        assertTrue(sql.contains("search-user"), sql);
    }

    /**
     * 验证空关键字不会产生额外筛选条件。
     */
    @Test
    public void buildQueryWrapperShouldIgnoreBlankKeyword() {
        SysAccountController controller = new SysAccountController(
                mock(SysAccountService.class),
                mock(AuthCredentialKeyService.class),
                mock(SysRoleService.class),
                mock(SystemFormOptionService.class)
        );
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getParameter("keyword")).thenReturn("  ");
        when(request.getParameterMap()).thenReturn(Collections.emptyMap());

        QueryWrapper queryWrapper = controller.buildQueryWrapper(request);

        assertFalse(queryWrapper.hasCondition());
    }

    /**
     * 验证 LIKE 通配符和反斜杠按普通字符参与搜索。
     */
    @Test
    public void buildQueryWrapperShouldEscapeLikePatternCharacters() {
        SysAccountController controller = new SysAccountController(
                mock(SysAccountService.class),
                mock(AuthCredentialKeyService.class),
                mock(SysRoleService.class),
                mock(SystemFormOptionService.class)
        );
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getParameter("keyword")).thenReturn("a_b%c\\d");

        String sql = controller.buildQueryWrapper(request).toSQL();

        assertTrue(sql.contains("a\\\\_b\\\\%c\\\\\\\\d"), sql);
    }

    /**
     * 验证创建用户时保留表单选择的部门，不被操作者部门覆盖。
     */
    @Test
    public void saveShouldKeepSubmittedDepartment() {
        BigInteger selectedDeptId = BigInteger.valueOf(200);
        BigInteger operatorDeptId = BigInteger.valueOf(100);
        BigInteger tenantId = BigInteger.valueOf(300);
        BigInteger operatorId = BigInteger.valueOf(400);
        SysAccountService accountService = mock(SysAccountService.class);
        AuthCredentialKeyService credentialKeyService = mock(AuthCredentialKeyService.class);
        SysRoleService roleService = mock(SysRoleService.class);
        SysAccountController controller = new SysAccountController(
                accountService,
                credentialKeyService,
                roleService,
                mock(SystemFormOptionService.class)
        );
        SysAccount entity = createAccount(selectedDeptId);
        LoginAccount loginAccount = createLoginAccount(operatorId, tenantId, operatorDeptId);
        AtomicReference<BigInteger> savedDeptId = new AtomicReference<>();
        AtomicReference<BigInteger> savedTenantId = new AtomicReference<>();
        AtomicReference<BigInteger> savedCreatedBy = new AtomicReference<>();
        AtomicReference<BigInteger> savedModifiedBy = new AtomicReference<>();

        when(accountService.count(any(QueryWrapper.class))).thenReturn(0L);
        when(roleService.listByIds(anyCollection())).thenReturn(List.of(enabledRole(BigInteger.ONE, "user")));
        when(accountService.save(any(SysAccount.class))).thenAnswer(invocation -> {
            SysAccount savedAccount = invocation.getArgument(0, SysAccount.class);
            savedDeptId.set(savedAccount.getDeptId());
            savedTenantId.set(savedAccount.getTenantId());
            savedCreatedBy.set(savedAccount.getCreatedBy());
            savedModifiedBy.set(savedAccount.getModifiedBy());
            savedAccount.setId(BigInteger.valueOf(500));
            return true;
        });
        when(credentialKeyService.decryptPayload(any()))
                .thenReturn(JSONObject.parseObject("{\"password\":\"Valid123!\"}"));

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount).thenReturn(loginAccount);

            Result<?> result = controller.save(entity);

            assertEquals(result.getErrorCode(), 0);
        }

        verify(accountService).save(any(SysAccount.class));
        assertEquals(savedDeptId.get(), selectedDeptId);
        assertEquals(savedTenantId.get(), tenantId);
        assertEquals(savedCreatedBy.get(), operatorId);
        assertEquals(savedModifiedBy.get(), operatorId);
    }

    /**
     * 验证创建用户时拒绝空角色。
     */
    @Test
    public void saveShouldRejectEmptyRoles() {
        SysAccountService accountService = mock(SysAccountService.class);
        AuthCredentialKeyService credentialKeyService = mock(AuthCredentialKeyService.class);
        SysRoleService roleService = mock(SysRoleService.class);
        SysAccountController controller = new SysAccountController(
                accountService,
                credentialKeyService,
                roleService,
                mock(SystemFormOptionService.class)
        );
        SysAccount entity = createAccount(BigInteger.valueOf(200));
        entity.setRoleIds(List.of());
        LoginAccount loginAccount = createLoginAccount(
                BigInteger.valueOf(400),
                BigInteger.valueOf(300),
                BigInteger.valueOf(100)
        );
        when(accountService.count(any(QueryWrapper.class))).thenReturn(0L);

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount).thenReturn(loginAccount);

            Result<?> result = controller.save(entity);

            assertEquals(result.getErrorCode(), 1);
            assertEquals(result.getMessage(), "角色不能为空");
        }

        verify(accountService, never()).save(any(SysAccount.class));
    }

    /**
     * 验证超级管理员默认进入工作台并返回角色标识。
     */
    @Test
    public void myProfileShouldReturnSuperAdminHomePath() {
        BigInteger accountId = BigInteger.ONE;
        SysAccountService accountService = mock(SysAccountService.class);
        AuthCredentialKeyService credentialKeyService = mock(AuthCredentialKeyService.class);
        SysRoleService roleService = mock(SysRoleService.class);
        SysAccountController controller = new SysAccountController(
                accountService,
                credentialKeyService,
                roleService,
                mock(SystemFormOptionService.class)
        );
        SysAccount account = new SysAccount();
        account.setId(accountId);
        when(accountService.getById(accountId)).thenReturn(account);
        when(roleService.getRolesByAccountId(accountId))
                .thenReturn(List.of(enabledRole(BigInteger.ONE, "super_admin")));

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount)
                    .thenReturn(createLoginAccount(accountId, BigInteger.ZERO, BigInteger.ZERO));

            Result<SysAccountProfileVo> result = controller.myProfile();

            assertEquals(result.getData().getHomePath(), "/dashboard/workspace");
            assertEquals(result.getData().getRoles(), List.of("super_admin"));
        }
    }

    /**
     * 验证普通账号默认进入智能体聊天页。
     */
    @Test
    public void myProfileShouldReturnAgentChatHomePathForRegularUser() {
        BigInteger accountId = BigInteger.valueOf(20);
        SysAccountService accountService = mock(SysAccountService.class);
        AuthCredentialKeyService credentialKeyService = mock(AuthCredentialKeyService.class);
        SysRoleService roleService = mock(SysRoleService.class);
        SysAccountController controller = new SysAccountController(
                accountService,
                credentialKeyService,
                roleService,
                mock(SystemFormOptionService.class)
        );
        SysAccount account = new SysAccount();
        account.setId(accountId);
        when(accountService.getById(accountId)).thenReturn(account);
        when(roleService.getRolesByAccountId(accountId))
                .thenReturn(List.of(enabledRole(BigInteger.TWO, "operator")));

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount)
                    .thenReturn(createLoginAccount(accountId, BigInteger.ZERO, BigInteger.ZERO));

            Result<SysAccountProfileVo> result = controller.myProfile();

            assertEquals(result.getData().getHomePath(), "/ai/agent-chat");
            assertEquals(result.getData().getRoles(), List.of("operator"));
        }
    }

    /**
     * 构造待创建账号。
     *
     * @param deptId 表单选择的部门 ID
     * @return 待创建账号
     */
    private SysAccount createAccount(BigInteger deptId) {
        SysAccount account = new SysAccount();
        account.setDeptId(deptId);
        account.setLoginName("department_test_user");
        account.setNickname("部门测试用户");
        account.setPasswordCredential(Map.of("keyId", "test-key"));
        account.setRoleIds(List.of(BigInteger.ONE));
        return account;
    }

    /**
     * 构造启用角色。
     *
     * @param id 角色 ID
     * @param roleKey 角色标识
     * @return 启用角色
     */
    private SysRole enabledRole(BigInteger id, String roleKey) {
        SysRole role = new SysRole();
        role.setId(id);
        role.setRoleKey(roleKey);
        role.setStatus(1);
        return role;
    }

    /**
     * 构造当前登录账号。
     *
     * @param id 操作人 ID
     * @param tenantId 租户 ID
     * @param deptId 操作人部门 ID
     * @return 当前登录账号
     */
    private LoginAccount createLoginAccount(BigInteger id, BigInteger tenantId, BigInteger deptId) {
        LoginAccount account = new LoginAccount();
        account.setId(id);
        account.setTenantId(tenantId);
        account.setDeptId(deptId);
        return account;
    }
}
