package com.cf.imes.module.system.service.dict;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.system.controller.admin.dict.vo.type.DictTypePageReqVO;
import com.cf.imes.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.dict.DictDataDO;
import com.cf.imes.module.system.dal.dataobject.dict.DictTypeDO;
import com.cf.imes.module.system.dal.mysql.dict.DictDataMapper;
import com.cf.imes.module.system.dal.mysql.dict.DictTypeMapper;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;

import java.util.List;

import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.DICT_TYPE_HAS_CHILDREN;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.DICT_TYPE_NAME_DUPLICATE;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.DICT_TYPE_NOT_EXISTS;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.DICT_TYPE_TYPE_DUPLICATE;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class DictTypeServiceImplTest extends BaseMockitoUnitTest {

    @InjectMocks
    private DictTypeServiceImpl dictTypeService;

    @Mock
    private DictTypeMapper dictTypeMapper;

    @Mock
    private DictDataMapper dictDataMapper;

    @Mock
    private DictDataService dictDataService;

    @BeforeAll
    static void init(){
        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), DictDataDO.class);
    }

    @Test
    void testGetDictTypePage_success() {
        // 准备参数
        DictTypePageReqVO reqVO = new DictTypePageReqVO();

        PageResult<DictTypeDO> pageResult = new PageResult<>();
        pageResult.setTotal(1L);

        // mock mapper
        when(dictTypeMapper.selectPage(reqVO)).thenReturn(pageResult);

        // 调用
        PageResult<DictTypeDO> result = dictTypeService.getDictTypePage(reqVO);

        // 断言
        assertSame(pageResult, result);

        // 校验调用
        verify(dictTypeMapper).selectPage(reqVO);
    }

    @Test
    void testGetDictTypeById_success() {
        DictTypeDO dictType = new DictTypeDO();
        dictType.setId(1L);

        when(dictTypeMapper.selectById(1L)).thenReturn(dictType);

        DictTypeDO result = dictTypeService.getDictType(1L);

        assertSame(dictType, result);
        verify(dictTypeMapper).selectById(1L);
    }

    @Test
    void testGetDictTypeByType_success() {
        DictTypeDO dictType = new DictTypeDO();
        dictType.setType("system");

        when(dictTypeMapper.selectByType("system")).thenReturn(dictType);

        DictTypeDO result = dictTypeService.getDictType("system");

        assertSame(dictType, result);
        verify(dictTypeMapper).selectByType("system");
    }

    @Test
    void testCreateDictType_success() {
        DictTypeSaveReqVO reqVO = new DictTypeSaveReqVO();
        reqVO.setName("系统配置");
        reqVO.setType("system");

        // mock 唯一性校验
        when(dictTypeMapper.selectByName("系统配置")).thenReturn(null);
        when(dictTypeMapper.selectByType("system")).thenReturn(null);

        // mock insert：模拟 MyBatis 回写 id
        doAnswer(invocation -> {
            DictTypeDO arg = invocation.getArgument(0);
            arg.setId(1L);
            return 1;
        }).when(dictTypeMapper).insert(any(DictTypeDO.class));

        Long id = dictTypeService.createDictType(reqVO);

        assertEquals(1L, id);
        verify(dictTypeMapper).insert(any(DictTypeDO.class));
    }

    @Test
    void testUpdateDictType_success_noTypeChange() {
        DictTypeSaveReqVO reqVO = new DictTypeSaveReqVO();
        reqVO.setId(1L);
        reqVO.setName("系统");
        reqVO.setType("system");

        DictTypeDO old = new DictTypeDO();
        old.setId(1L);
        old.setType("system");

        when(dictTypeMapper.selectById(1L)).thenReturn(old);
        when(dictTypeMapper.selectByName("系统")).thenReturn(null);
        when(dictTypeMapper.selectByType("system")).thenReturn(old);
        when(dictTypeMapper.updateById(any(DictTypeDO.class))).thenReturn(1);

        dictTypeService.updateDictType(reqVO);

        verify(dictTypeMapper).updateById(any(DictTypeDO.class));
        verify(dictDataMapper, Mockito.never()).update(any());
    }

    @Test
    void testUpdateDictType_success_typeChanged() {
        DictTypeSaveReqVO reqVO = new DictTypeSaveReqVO();
        reqVO.setId(1L);
        reqVO.setType("new_type");

        DictTypeDO old = new DictTypeDO();
        old.setId(1L);
        old.setType("old_type");

        when(dictTypeMapper.selectById(1L)).thenReturn(old);
        when(dictTypeMapper.selectByType("new_type")).thenReturn(null);
        when(dictTypeMapper.updateById(any(DictTypeDO.class))).thenReturn(1);
        when(dictDataMapper.update(any())).thenReturn(1);

        dictTypeService.updateDictType(reqVO);

        // 校验 dict_data 被批量更新
        verify(dictDataMapper).update(
                argThat((LambdaUpdateWrapper wrapper) ->
                        wrapper.getSqlSet().contains("dict_type")
                )
        );


    }

    @Test
    void testDeleteDictType_success() {
        DictTypeDO dictType = new DictTypeDO();
        dictType.setId(1L);
        dictType.setType("system");

        when(dictTypeMapper.selectById(1L)).thenReturn(dictType);
        when(dictDataService.getDictDataCountByDictType("system")).thenReturn(0L);

        dictTypeService.deleteDictType(1L);

        verify(dictTypeMapper).updateToDelete(eq(1L), any());
    }

    @Test
    void testDeleteDictType_hasChildren() {
        DictTypeDO dictType = new DictTypeDO();
        dictType.setId(1L);
        dictType.setType("system");

        when(dictTypeMapper.selectById(1L)).thenReturn(dictType);
        when(dictDataService.getDictDataCountByDictType("system")).thenReturn(1L);

        assertServiceException(() -> dictTypeService.deleteDictType(1L), DICT_TYPE_HAS_CHILDREN);
    }

    @Test
    void testGetDictTypeList_success() {
        List<DictTypeDO> list = List.of(new DictTypeDO(), new DictTypeDO());
        when(dictTypeMapper.selectList()).thenReturn(list);

        List<DictTypeDO> result = dictTypeService.getDictTypeList();

        assertSame(list, result);
    }

    @Test
    void validateDictTypeNameUnique_notExists_shouldPass() {
        // 模拟数据库返回 null，表示该名字不存在
        when(dictTypeMapper.selectByName("newTypeName")).thenReturn(null);

        // 调用方法，不应该抛异常
        dictTypeService.validateDictTypeNameUnique(null, "newTypeName");
        dictTypeService.validateDictTypeNameUnique(1L, "newTypeName");
    }

    @Test
    void validateDictTypeNameUnique_existsWithNullId_shouldThrow() {
        // 模拟数据库返回已有记录
        DictTypeDO existing = new DictTypeDO();
        existing.setId(100L);
        when(dictTypeMapper.selectByName("existName")).thenReturn(existing);

        assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(null, "existName"), DICT_TYPE_NAME_DUPLICATE);
    }

    @Test
    void validateDictTypeNameUnique_existsWithDifferentId_shouldThrow() {
        // 模拟数据库返回已有记录
        DictTypeDO existing = new DictTypeDO();
        existing.setId(100L);
        when(dictTypeMapper.selectByName("existName")).thenReturn(existing);


        assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(200L, "existName"), DICT_TYPE_NAME_DUPLICATE);
    }

    @Test
    void validateDictTypeNameUnique_existsWithSameId_shouldPass() {
        // 模拟数据库返回已有记录
        DictTypeDO existing = new DictTypeDO();
        existing.setId(100L);
        when(dictTypeMapper.selectByName("existName")).thenReturn(existing);

        // 当 id 与已有记录一致，不抛异常
        dictTypeService.validateDictTypeNameUnique(100L, "existName");
    }

    @Test
    void validateDictTypeUnique_typeNotExists_shouldPass() {
        when(dictTypeMapper.selectByType("newType")).thenReturn(null);
        dictTypeService.validateDictTypeUnique(1L, "newType");
    }

    @Test
    void validateDictTypeUnique_typeExistsWithNullId_shouldThrow() {
        DictTypeDO existing = new DictTypeDO();
        existing.setId(100L);
        when(dictTypeMapper.selectByType("existType")).thenReturn(existing);

        assertServiceException(() -> dictTypeService.validateDictTypeUnique(null, "existType"), DICT_TYPE_TYPE_DUPLICATE);
    }

    @Test
    void validateDictTypeUnique_typeExistsWithDifferentId_shouldThrow() {
        DictTypeDO existing = new DictTypeDO();
        existing.setId(100L);
        when(dictTypeMapper.selectByType("existType")).thenReturn(existing);

        assertServiceException(() -> dictTypeService.validateDictTypeUnique(200L, "existType"), DICT_TYPE_TYPE_DUPLICATE);
    }

    @Test
    void validateDictTypeUnique_typeExistsWithSameId_shouldPass() {
        DictTypeDO existing = new DictTypeDO();
        existing.setId(100L);
        when(dictTypeMapper.selectByType("existType")).thenReturn(existing);

        dictTypeService.validateDictTypeUnique(100L, "existType");
    }

    // ================= validateDictTypeExists =================
    @Test
    void validateDictTypeExists_idNull_shouldReturnNull() {
        assertNull(dictTypeService.validateDictTypeExists(null));
    }

    @Test
    void validateDictTypeExists_idNotExists_shouldThrow() {
        when(dictTypeMapper.selectById(123L)).thenReturn(null);

        assertServiceException(() -> dictTypeService.validateDictTypeExists(123L), DICT_TYPE_NOT_EXISTS);
    }

    @Test
    void validateDictTypeExists_idExists_shouldReturnDictType() {
        DictTypeDO dictTypeDO = new DictTypeDO();
        dictTypeDO.setId(100L);
        when(dictTypeMapper.selectById(100L)).thenReturn(dictTypeDO);

        DictTypeDO result = dictTypeService.validateDictTypeExists(100L);
        assertSame(dictTypeDO, result);
    }
}
