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

import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.ArrayUtils;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO;
import com.cf.imes.module.system.controller.admin.dict.vo.data.DictDataTransReqVO;
import com.cf.imes.module.system.dal.dataobject.dict.DictDataDO;
import com.cf.imes.module.system.dal.dataobject.dict.DictDatai18nVersionDO;
import com.cf.imes.module.system.dal.dataobject.dict.DictTypeDO;
import com.cf.imes.module.system.dal.mysql.dict.DictDataI18nMapper;
import com.cf.imes.module.system.dal.mysql.dict.DictDataI18nVersionMapper;
import com.cf.imes.module.system.dal.mysql.dict.DictDataMapper;
import com.cf.imes.module.system.framework.trans.config.TransApi;
import com.cf.imes.module.system.util.locale.DictDataI18nCacheUtils;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.*;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class DictDataServiceImplTest extends BaseMockitoUnitTest {

    @InjectMocks
    private DictDataServiceImpl dictDataService;

    @Mock
    private DictDataMapper dictDataMapper;

    @Mock
    private DictDataI18nMapper dictDatai18nMapper;

    @Mock
    private DictDataI18nVersionMapper dictDataI18nVersionMapper;

    @Mock
    private DictTypeService dictTypeService;

    @Mock
    private StringRedisTemplate stringRedisTemplate;

    @Mock
    private TransApi transApi;

    @Test
    void testGetDictDataList() {
        // mock 数据
        DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2)
                .setStatus(CommonStatusEnum.ENABLE.getStatus());
        DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1)
                .setStatus(CommonStatusEnum.ENABLE.getStatus());

        // 准备参数
        Integer status = CommonStatusEnum.ENABLE.getStatus();
        String dictType = "yunai";

        when(dictDataMapper.selectListByStatusAndDictType(status, dictType)).thenReturn(Arrays.asList(dictDataDO01, dictDataDO02));

        // 调用
        List<DictDataDO> dictDataDOList = dictDataService.getDictDataList(status, dictType);
        // 断言
        assertEquals(2, dictDataDOList.size());
        assertPojoEquals(dictDataDO02, dictDataDOList.get(0));
        assertPojoEquals(dictDataDO01, dictDataDOList.get(1));
    }

    @Test
    void testGetDictDataPage() {
        // 准备模拟数据
        DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> {
            o.setLabel("晨丰");
            o.setDictType("yunai");
            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
        });

        List<DictDataDO> mockList = List.of(dbDictData);

        DictDataPageReqVO reqVO = new DictDataPageReqVO();
        reqVO.setLabel("晨");
        reqVO.setDictType("yunai");
        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());

        // mock Mapper 返回值
        PageResult<DictDataDO> dictDataDOPageResult = new PageResult<>();
        dictDataDOPageResult.setList(mockList);
        dictDataDOPageResult.setTotal(1L);
        when(dictDataMapper.selectPage(reqVO)).thenReturn(dictDataDOPageResult);

        // 调用 Service
        PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(reqVO);

        // 断言
        assertEquals(1, pageResult.getTotal());
        assertEquals(1, pageResult.getList().size());
        assertPojoEquals(dbDictData, pageResult.getList().get(0));

        // 验证 Mapper 方法被调用
        verify(dictDataMapper, times(1)).selectPage(reqVO);
    }

    @Test
    void testGetDictData() {
        Long id = randomLongId();
        // mock 数据
        DictDataDO dbDictData = randomDictDataDO();
        when(dictDataMapper.selectById(id)).thenReturn(dbDictData);

        // 调用
        DictDataDO queryData = dictDataService.getDictData(id);
        // 断言
        assertPojoEquals(dbDictData, queryData);
    }

    @Test
    void testCreateDictData_success() {
        // 准备请求对象
        DictDataSaveReqVO reqVO = new DictDataSaveReqVO();
        reqVO.setDictType("gender");
        reqVO.setValue("1");
        reqVO.setLabel("男");

        // mock 方法
        when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));

        // 模拟 Mapper 插入时生成 ID
        doAnswer(invocation -> {
            DictDataDO arg = invocation.getArgument(0);
            arg.setId(100L); // 模拟自增主键
            return null;
        }).when(dictDataMapper).insert(any(DictDataDO.class));

        // 调用 Service
        Long id = dictDataService.createDictData(reqVO);

        // 断言返回的 ID 是模拟的
        assertEquals(100L, id);

        // 验证 Mapper 方法被调用一次，且参数是根据请求对象生成的 DictDataDO
        verify(dictDataMapper, times(1)).insert(argThat((DictDataDO dict) ->
                "gender".equals(dict.getDictType()) &&
                        "1".equals(dict.getValue()) &&
                        "男".equals(dict.getLabel())
        ));
    }

    @Test
    void testUpdateDictData_success() {
        // 准备请求对象
        DictDataSaveReqVO reqVO = new DictDataSaveReqVO();
        reqVO.setId(100L);
        reqVO.setDictType("gender");
        reqVO.setValue("1");
        reqVO.setLabel("男");

        // mock 方法，字典类型
        when(dictDataMapper.selectById(anyLong())).thenReturn(randomDictDataDO());
        when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));

        // Mock Mapper 的 updateById 方法
        when(dictDataMapper.updateById(any(DictDataDO.class))).thenReturn(1);

        // 调用 Service
        dictDataService.updateDictData(reqVO);

        // 验证 Mapper 被调用
        verify(dictDataMapper, times(1)).updateById(argThat((DictDataDO dict) ->
                dict.getId().equals(100L) &&
                        "gender".equals(dict.getDictType()) &&
                        "1".equals(dict.getValue()) &&
                        "男".equals(dict.getLabel())
        ));
    }

    @Test
    void testDeleteDictData_success() {
        Long id = 100L;

        // mock 方法，字典类型
        when(dictDataMapper.selectById(anyLong())).thenReturn(randomDictDataDO());

        // mock mapper 删除行为（deleteById 返回 int）
        when(dictDataMapper.deleteById(anyLong())).thenReturn(1);

        // 调用
        dictDataService.deleteDictData(id);

        // 验证 mapper 被正确调用
        verify(dictDataMapper, times(1)).deleteById(id);
    }

    @Test
    void testValidateDictDataExists_success() {
        Long id = 1L;
        DictDataDO dictData = new DictDataDO();
        dictData.setId(id);

        when(dictDataMapper.selectById(id)).thenReturn(dictData);

        dictDataService.validateDictDataExists(id);

        verify(dictDataMapper).selectById(id);
    }

    @Test
    void testValidateDictDataExists_notExists() {
        Long id = 1L;
        when(dictDataMapper.selectById(id)).thenReturn(null);

        // 异常
        assertServiceException(() -> dictDataService.validateDictDataExists(id), DICT_DATA_NOT_EXISTS);
    }

    @Test
    void testValidateDictDataExists_null_notExists() {
        // 不抛异常即可
        dictDataService.validateDictDataExists(null);

        // mapper 不应被调用
        verify(dictDataMapper, never()).selectById(any());
    }

    @Test
    void testValidateDictTypeExists_success() {
        String type = "gender";
        DictTypeDO dictType = new DictTypeDO();
        dictType.setType(type);
        dictType.setStatus(CommonStatusEnum.ENABLE.getStatus());

        when(dictTypeService.getDictType(type)).thenReturn(dictType);

        // 不抛异常即成功
        dictDataService.validateDictTypeExists(type);

        verify(dictTypeService).getDictType(type);
    }

    @Test
    void testValidateDictTypeExists_notExists() {
        String type = "gender";
        when(dictTypeService.getDictType(type)).thenReturn(null);

        assertServiceException(() -> dictDataService.validateDictTypeExists(type), DICT_TYPE_NOT_EXISTS);
    }

    @Test
    void testValidateDictTypeExists_notEnable() {
        String type = "gender";
        DictTypeDO dictType = new DictTypeDO();
        dictType.setType(type);
        dictType.setStatus(CommonStatusEnum.DISABLE.getStatus());

        when(dictTypeService.getDictType(type)).thenReturn(dictType);

        // 调用, 并断言异常
        assertServiceException(() -> dictDataService.validateDictTypeExists(type), DICT_TYPE_NOT_ENABLE);
    }

    @Test
    void testValidateDictDataValueUnique_success() {
        DictDataDO exist = new DictDataDO();
        exist.setId(10L);

        when(dictDataMapper.selectByDictTypeAndValue("gender", "1"))
                .thenReturn(exist);

        dictDataService.validateDictDataValueUnique(10L, "gender", "1");

        verify(dictDataMapper).selectByDictTypeAndValue("gender", "1");
    }

    @Test
    void testValidateDictDataValueUnique_exist_success() {
        when(dictDataMapper.selectByDictTypeAndValue(anyString(), anyString()))
                .thenReturn(null);

        dictDataService.validateDictDataValueUnique(1L, "gender", "1");

        verify(dictDataMapper).selectByDictTypeAndValue("gender", "1");
    }

    @Test
    void testValidateDictDataValueUnique_valueDuplicateForCreate() {
        DictDataDO exist = new DictDataDO();
        exist.setId(10L);

        when(dictDataMapper.selectByDictTypeAndValue("gender", "1"))
                .thenReturn(exist);

        // 调用，校验异常
        assertServiceException(() -> dictDataService.validateDictDataValueUnique(null, "gender", "1"),
                DICT_DATA_VALUE_DUPLICATE);
    }

    @Test
    void testValidateDictDataValueUnique_valueDuplicateForUpdate() {
        DictDataDO exist = new DictDataDO();
        exist.setId(10L);

        when(dictDataMapper.selectByDictTypeAndValue("gender", "1"))
                .thenReturn(exist);

        // 调用，校验异常
        assertServiceException(() -> dictDataService.validateDictDataValueUnique(20L, "gender", "1"),
                DICT_DATA_VALUE_DUPLICATE);
    }

    @Test
    void testGetDictDataCountByDictType() {
        when(dictDataMapper.selectCountByDictType("gender")).thenReturn(5L);

        long count = dictDataService.getDictDataCountByDictType("gender");

        assertEquals(5L, count);
        verify(dictDataMapper).selectCountByDictType("gender");
    }

    @Test
    void testValidateDictDataList_success() {
        String dictType = "gender";
        List<String> values = List.of("1", "2");

        DictDataDO male = new DictDataDO();
        male.setValue("1");
        male.setStatus(CommonStatusEnum.ENABLE.getStatus());

        DictDataDO female = new DictDataDO();
        female.setValue("2");
        female.setStatus(CommonStatusEnum.ENABLE.getStatus());

        when(dictDataMapper.selectByDictTypeAndValues(dictType, values))
                .thenReturn(List.of(male, female));

        dictDataService.validateDictDataList(dictType, values);

        verify(dictDataMapper).selectByDictTypeAndValues(dictType, values);
    }

    @Test
    void testValidateDictDataList_notFound() {
        String dictType = "gender";
        List<String> values = List.of("1", "2");

        DictDataDO onlyOne = new DictDataDO();
        onlyOne.setValue("1");
        onlyOne.setStatus(CommonStatusEnum.ENABLE.getStatus());

        when(dictDataMapper.selectByDictTypeAndValues(dictType, values))
                .thenReturn(List.of(onlyOne));

        // 调用, 并断言异常
        assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), DICT_DATA_NOT_EXISTS);
    }

    @Test
    void testValidateDictDataList_notEnable() {
        String dictType = "gender";
        List<String> values = List.of("1");

        DictDataDO disabled = new DictDataDO();
        disabled.setValue("1");
        disabled.setStatus(CommonStatusEnum.DISABLE.getStatus());
        disabled.setLabel("男");

        when(dictDataMapper.selectByDictTypeAndValues(dictType, values))
                .thenReturn(List.of(disabled));

        // 调用, 并断言异常
        assertServiceException(() -> dictDataService.validateDictDataList(dictType, values),
                DICT_DATA_NOT_ENABLE, "男");
    }

    @Test
    void testValidateDictDataList_empty() {
        dictDataService.validateDictDataList("gender", Collections.emptyList());

        verify(dictDataMapper, never())
                .selectByDictTypeAndValues(anyString(), anyCollection());
    }

    @Test
    void testGetDictData_dictType() {
        String dictType = "gender";
        String value = "1";

        DictDataDO dictData = new DictDataDO();
        dictData.setDictType(dictType);
        dictData.setValue(value);

        when(dictDataMapper.selectByDictTypeAndValue(dictType, value))
                .thenReturn(dictData);

        DictDataDO result = dictDataService.getDictData(dictType, value);

        assertSame(dictData, result);
        verify(dictDataMapper).selectByDictTypeAndValue(dictType, value);
    }

    @Test
    void testGetDictI18nData_success() {
        String dictType = "gender";
        String value = "1";
        String language = "en_US";

        DictDataDO dictData = new DictDataDO();
        dictData.setDictType(dictType);
        dictData.setValue(value);
        dictData.setLabel("Male");

        when(dictDatai18nMapper
                .selectI18nByDictTypeAndValue(dictType, value, language))
                .thenReturn(dictData);

        DictDataDO result =
                dictDataService.getDictI18nData(dictType, value, language);

        assertSame(dictData, result);
        verify(dictDatai18nMapper)
                .selectI18nByDictTypeAndValue(dictType, value, language);
    }

    @Test
    void testParseDictData() {
        String dictType = "gender";
        String label = "男";

        DictDataDO dictData = new DictDataDO();
        dictData.setDictType(dictType);
        dictData.setLabel(label);

        when(dictDataMapper.selectByDictTypeAndLabel(dictType, label))
                .thenReturn(dictData);

        DictDataDO result = dictDataService.parseDictData(dictType, label);

        assertSame(dictData, result);
        verify(dictDataMapper).selectByDictTypeAndLabel(dictType, label);
    }

    @Test
    void testGetI18nVersion_nonEmpty() {
        DictDatai18nVersionDO versionDO = new DictDatai18nVersionDO();
        versionDO.setVersion(42);

        when(dictDataI18nVersionMapper.selectList()).thenReturn(List.of(versionDO));

        Integer version = dictDataService.getI18nVersion();

        assertEquals(42, version);
        verify(dictDataI18nVersionMapper).selectList();
    }

    @Test
    void testGetI18nVersion_emptyList() {
        when(dictDataI18nVersionMapper.selectList()).thenReturn(Collections.emptyList());

        Integer version = dictDataService.getI18nVersion();

        assertEquals(0, version);
        verify(dictDataI18nVersionMapper).selectList();
    }

    @Test
    void testUpdateI18nVersion_success() {
        // 调用方法
        dictDataService.updateI18nVersion();

        // 验证 mapper 的 updateVersion 被调用
        verify(dictDataI18nVersionMapper).updateVersion();

        // 验证 Redis 通知被发送
        verify(stringRedisTemplate).convertAndSend(
                com.cf.imes.framework.redis.constants.RedisKeyConstants.DICT_DATA_I18N_CACHE_REFRESH, "");
    }

//    // ========== 随机对象 ==========
//
    @SafeVarargs
    private static DictDataDO randomDictDataDO(Consumer<DictDataDO>... consumers) {
        Consumer<DictDataDO> consumer = (o) -> {
            o.setStatus(randomCommonStatus()); // 保证 status 的范围
        };
        return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers));
    }

    @Test
    void testGetAllLanguageDictData_withCache() {
        Map<String, List<DictDataSimpleRespVO>> cache = new HashMap<>();
        cache.put("ZH", List.of(new DictDataSimpleRespVO()));

        try (MockedStatic<DictDataI18nCacheUtils> mockedCache = Mockito.mockStatic(DictDataI18nCacheUtils.class)) {
            mockedCache.when(DictDataI18nCacheUtils::getCache).thenReturn(cache);

            Map<String, List<DictDataSimpleRespVO>> result = dictDataService.getAllLanguageDictData();

            assertSame(cache, result);
            mockedCache.verify(DictDataI18nCacheUtils::getCache, times(1));
        }
    }

    @Test
    void testGetAllLanguageDictData_noCache() {
        List<DictDataDO> dictDataList = Arrays.asList(new DictDataDO().setId(1L).setDictType("type"));
        List<DictDataDO> enList = List.of(new DictDataDO().setId(1L));

        try (MockedStatic<DictDataI18nCacheUtils> mockedCache = Mockito.mockStatic(DictDataI18nCacheUtils.class)) {
            mockedCache.when(DictDataI18nCacheUtils::getCache).thenReturn(Collections.emptyMap());

            when(dictDataMapper.selectListByStatusAndDictType(CommonStatusEnum.ENABLE.getStatus(), null))
                    .thenReturn(dictDataList);
            when(dictDatai18nMapper.getLocaleDictData(List.of(1L), "en")).thenReturn(enList);

            Map<String, List<DictDataSimpleRespVO>> result = dictDataService.getAllLanguageDictData();

            // 验证中文和英文都存在
            assertTrue(result.containsKey("zh"));
            assertTrue(result.containsKey("en"));

            mockedCache.verify(() -> DictDataI18nCacheUtils.putCache(result), times(1));
        }
    }

    @Test
    void testTrans_success() {
        DictDataDO dictDataDO1 = new DictDataDO().setId(1L).setLabel("苹果").setStatus(CommonStatusEnum.ENABLE.getStatus());
        List<DictDataDO> dictDataList = List.of(dictDataDO1);

        // mock 数据库
        when(dictDatai18nMapper.delete(any())).thenReturn(1);
        when(dictDataMapper.selectList(any())).thenReturn(dictDataList);

        // mock TransApi
        doReturn("{\"trans_result\":[{\"src\":\"苹果\",\"dst\":\"Apple\"}]}")
                .when(transApi).getTransResult(any(), any(), any());

        DictDataTransReqVO reqVO = new DictDataTransReqVO();
        reqVO.setTargetLanguage("en");
        dictDataService.trans(reqVO);

        // 验证删除和批量插入被调用
        verify(dictDatai18nMapper).delete(any());
        verify(dictDatai18nMapper).insertBatch(anyList());
    }

    @Test
    void testTrans_request_error() {
        DictDataDO dictDataDO1 = new DictDataDO().setId(1L).setLabel("苹果").setStatus(CommonStatusEnum.ENABLE.getStatus());
        List<DictDataDO> dictDataList = List.of(dictDataDO1);

        // mock 数据库
        when(dictDatai18nMapper.delete(any())).thenReturn(1);
        when(dictDataMapper.selectList(any())).thenReturn(dictDataList);

        // mock TransApi
        doReturn("{\"error_code\":\"52002\"}")
                .when(transApi).getTransResult(any(), any(), any());

        DictDataTransReqVO reqVO = new DictDataTransReqVO();
        reqVO.setTargetLanguage("en");

        assertServiceException(() -> dictDataService.trans(reqVO),
                DICT_DATA_TRANS_ERROR, "{\"error_code\":\"52002\"}");
    }



    /**
     * 生成一个有效的字典类型
     *
     * @param type 字典类型
     * @return DictTypeDO 对象
     */
    private static DictTypeDO randomDictTypeDO(String type) {
        return randomPojo(DictTypeDO.class, o -> {
            o.setType(type);
            o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启
        });
    }

}
