package com.njcn.rdms.module.system.service.permission;

import com.njcn.rdms.framework.common.exception.ServiceException;
import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.rdms.module.system.controller.admin.permission.vo.userVisibilityConfig.UserVisibilityConfigSaveReqVO;
import com.njcn.rdms.module.system.dal.dataobject.permission.UserVisibilityConfigDO;
import com.njcn.rdms.module.system.dal.mysql.permission.UserVisibilityConfigMapper;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import java.util.List;

import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.USER_VISIBILITY_CONFIG_TYPE_FIELD_MISMATCH;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class UserVisibilityConfigServiceImplTest extends BaseMockitoUnitTest {

    @Mock
    private UserVisibilityConfigMapper userVisibilityConfigMapper;

    @InjectMocks
    private UserVisibilityConfigServiceImpl userVisibilityConfigService;

    /** 用户无配置时执行 insert，返回新记录 id */
    @Test
    void saveOrUpdate_insertNew_whenUserHasNoConfig() {
        UserVisibilityConfigSaveReqVO reqVO = buildReqVO(1L, "all", null, null);
        when(userVisibilityConfigMapper.selectByUserId(1L)).thenReturn(null);

        // insert 由 BaseMapper 填充 id，这里验证确实调了 insert（而非 updateById）
        userVisibilityConfigService.saveOrUpdate(reqVO);

        verify(userVisibilityConfigMapper).insert(any(UserVisibilityConfigDO.class));
        verify(userVisibilityConfigMapper, never()).updateById(any(UserVisibilityConfigDO.class));
    }

    /** 用户已有配置时执行 update，返回已有记录 id */
    @Test
    void saveOrUpdate_updateExisting_whenUserAlreadyHasConfig() {
        Long existingId = 999L;
        UserVisibilityConfigDO existing = buildDO(existingId, 2L, "all");
        UserVisibilityConfigSaveReqVO reqVO = buildReqVO(2L, "directions", List.of(101L, 102L), null);
        when(userVisibilityConfigMapper.selectByUserId(2L)).thenReturn(existing);

        Long returnedId = userVisibilityConfigService.saveOrUpdate(reqVO);

        assertEquals(existingId, returnedId);
        verify(userVisibilityConfigMapper).updateById(any(UserVisibilityConfigDO.class));
        verify(userVisibilityConfigMapper, never()).insert(any(UserVisibilityConfigDO.class));
    }

    /** type=all 但传了 visibleDirectionIds，应抛类型字段不匹配异常 */
    @Test
    void saveOrUpdate_typeAllWithDirectionIds_shouldThrowMismatch() {
        UserVisibilityConfigSaveReqVO reqVO = buildReqVO(3L, "all", List.of(101L), null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> userVisibilityConfigService.saveOrUpdate(reqVO));

        assertEquals(USER_VISIBILITY_CONFIG_TYPE_FIELD_MISMATCH.getCode(), ex.getCode());
        verify(userVisibilityConfigMapper, never()).selectByUserId(any(Long.class));
    }

    /** type=directions 但未传 visibleDirectionIds，应抛类型字段不匹配异常 */
    @Test
    void saveOrUpdate_typeDirectionsWithoutDirectionIds_shouldThrowMismatch() {
        UserVisibilityConfigSaveReqVO reqVO = buildReqVO(4L, "directions", null, null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> userVisibilityConfigService.saveOrUpdate(reqVO));

        assertEquals(USER_VISIBILITY_CONFIG_TYPE_FIELD_MISMATCH.getCode(), ex.getCode());
        verify(userVisibilityConfigMapper, never()).selectByUserId(any(Long.class));
    }

    // ===== 辅助方法 =====

    private static UserVisibilityConfigSaveReqVO buildReqVO(Long userId, String type,
                                                             List<Long> directionIds,
                                                             List<Long> projectIds) {
        UserVisibilityConfigSaveReqVO reqVO = new UserVisibilityConfigSaveReqVO();
        reqVO.setUserId(userId);
        reqVO.setVisibilityType(type);
        reqVO.setVisibleDirectionIds(directionIds);
        reqVO.setVisibleProjectIds(projectIds);
        return reqVO;
    }

    private static UserVisibilityConfigDO buildDO(Long id, Long userId, String type) {
        UserVisibilityConfigDO config = new UserVisibilityConfigDO();
        config.setId(id);
        config.setUserId(userId);
        config.setVisibilityType(type);
        return config;
    }

}
