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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.UnicodeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.CollectionUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
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.DictDatai18nDO;
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.enums.ErrorCodeConstants;
import com.cf.imes.module.system.enums.common.LanguageEnum;
import com.cf.imes.module.system.framework.trans.config.TransApi;
import com.cf.imes.module.system.util.locale.DictDataI18nCacheUtils;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.cf.imes.module.system.enums.ErrorCodeConstants.DICT_DATA_TRANS_ERROR;

/**
 * 字典数据 Service 实现类
 *
 * @author ruoyi
 */
@Service
@Slf4j
public class DictDataServiceImpl implements DictDataService {

    /**
     * 排序 dictType > sort
     */
    private static final Comparator<DictDataDO> COMPARATOR_TYPE_AND_SORT = Comparator
            .comparing(DictDataDO::getDictType)
            .thenComparingInt(DictDataDO::getSort);

    @Resource
    private DictTypeService dictTypeService;

    @Resource
    private DictDataMapper dictDataMapper;

    @Resource
    private DictDataI18nMapper dictDatai18nMapper;

    @Resource
    private TransApi transApi;

    @Resource
    private DictDataI18nVersionMapper dictDataI18nVersionMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public List<DictDataDO> getDictDataList(Integer status, String dictType) {
        List<DictDataDO> list = dictDataMapper.selectListByStatusAndDictType(status, dictType);
        list.sort(COMPARATOR_TYPE_AND_SORT);
        return list;
    }

    @Override
    public PageResult<DictDataDO> getDictDataPage(DictDataPageReqVO pageReqVO) {
        return dictDataMapper.selectPage(pageReqVO);
    }

    @Override
    public DictDataDO getDictData(Long id) {
        return dictDataMapper.selectById(id);
    }

    @Override
    public Long createDictData(DictDataSaveReqVO createReqVO) {
        // 校验字典类型有效
        validateDictTypeExists(createReqVO.getDictType());
        // 校验字典数据的值的唯一性
        validateDictDataValueUnique(null, createReqVO.getDictType(), createReqVO.getValue());

        // 插入字典类型
        DictDataDO dictData = BeanUtils.toBean(createReqVO, DictDataDO.class);
        dictDataMapper.insert(dictData);
        return dictData.getId();
    }

    @Override
    public void updateDictData(DictDataSaveReqVO updateReqVO) {
        // 校验自己存在
        validateDictDataExists(updateReqVO.getId());
        // 校验字典类型有效
        validateDictTypeExists(updateReqVO.getDictType());
        // 校验字典数据的值的唯一性
        validateDictDataValueUnique(updateReqVO.getId(), updateReqVO.getDictType(), updateReqVO.getValue());

        // 更新字典类型
        DictDataDO updateObj = BeanUtils.toBean(updateReqVO, DictDataDO.class);
        dictDataMapper.updateById(updateObj);
    }

    @Override
    public void deleteDictData(Long id) {
        // 校验是否存在
        validateDictDataExists(id);

        // 删除字典数据
        dictDataMapper.deleteById(id);
    }

    @Override
    public long getDictDataCountByDictType(String dictType) {
        return dictDataMapper.selectCountByDictType(dictType);
    }

    @VisibleForTesting
    public void validateDictDataValueUnique(Long id, String dictType, String value) {
        DictDataDO dictData = dictDataMapper.selectByDictTypeAndValue(dictType, value);
        if (dictData == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的字典数据
        if (id == null) {
            throw new ServiceException(ErrorCodeConstants.DICT_DATA_VALUE_DUPLICATE);
        }
        if (!dictData.getId().equals(id)) {
            throw new ServiceException(ErrorCodeConstants.DICT_DATA_VALUE_DUPLICATE);
        }
    }

    @VisibleForTesting
    public void validateDictDataExists(Long id) {
        if (id == null) {
            return;
        }
        DictDataDO dictData = dictDataMapper.selectById(id);
        if (dictData == null) {
            throw new ServiceException(ErrorCodeConstants.DICT_DATA_NOT_EXISTS);
        }
    }

    @VisibleForTesting
    public void validateDictTypeExists(String type) {
        DictTypeDO dictType = dictTypeService.getDictType(type);
        if (dictType == null) {
            throw new ServiceException(ErrorCodeConstants.DICT_TYPE_NOT_EXISTS);
        }
        if (!CommonStatusEnum.ENABLE.getStatus().equals(dictType.getStatus())) {
            throw new ServiceException(ErrorCodeConstants.DICT_TYPE_NOT_ENABLE);
        }
    }

    @Override
    public void validateDictDataList(String dictType, Collection<String> values) {
        if (CollUtil.isEmpty(values)) {
            return;
        }
        Map<String, DictDataDO> dictDataMap = CollectionUtils.convertMap(
                dictDataMapper.selectByDictTypeAndValues(dictType, values), DictDataDO::getValue);
        // 校验
        values.forEach(value -> {
            DictDataDO dictData = dictDataMap.get(value);
            if (dictData == null) {
                throw new ServiceException(ErrorCodeConstants.DICT_DATA_NOT_EXISTS);
            }
            if (!CommonStatusEnum.ENABLE.getStatus().equals(dictData.getStatus())) {
                throw new ServiceException(ErrorCodeConstants.DICT_DATA_NOT_ENABLE, dictData.getLabel());
            }
        });
    }

    @Override
    public DictDataDO getDictData(String dictType, String value) {
        return dictDataMapper.selectByDictTypeAndValue(dictType, value);
    }

    @Override
    public DictDataDO getDictI18nData(String dictType, String value, String language) {
        return dictDatai18nMapper.selectI18nByDictTypeAndValue(dictType, value, language);
    }

    @Override
    public DictDataDO parseDictData(String dictType, String label) {
        return dictDataMapper.selectByDictTypeAndLabel(dictType, label);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void trans(DictDataTransReqVO reqVO) {
        String to = reqVO.getTargetLanguage();
        // 清理对应语种下的历史记录
        int delete = dictDatai18nMapper.delete(new LambdaQueryWrapper<DictDatai18nDO>()
                .eq(DictDatai18nDO::getLocale, to));
        log.info("========== 【字典数据标签国际化】{}清理历史记录，删除 {} 条 =====", to, delete);

        // 查询所有有效的菜单类型 菜单
        List<DictDataDO> dictDataDOS = dictDataMapper.selectList(new LambdaQueryWrapper<DictDataDO>()
                .eq(DictDataDO::getDeleted, false)
                .eq(DictDataDO::getStatus, CommonStatusEnum.ENABLE.getStatus()));

        // 转成有序 map（key = 序号, value = DictDataDO）
        Map<Integer, DictDataDO> dataMap = IntStream.range(0, dictDataDOS.size())
                .boxed()
                .collect(Collectors.toMap(
                        i -> i,
                        dictDataDOS::get,
                        (v1, v2) -> v1,
                        LinkedHashMap::new
                ));

        // 拼接 menu name，用换行分隔
        String namesForQuery = dataMap.values().stream()
                .map(DictDataDO::getLabel)
                .filter(Objects::nonNull)
                .collect(Collectors.joining("\n"));

        // 请求翻译api
        String transResult = transApi.getTransResult(namesForQuery, "zh", to);

        JSONObject obj = JSON.parseObject(transResult);
        String errorCode = obj.getString("error_code");
        if (StringUtils.isNotEmpty(errorCode)) {
            throw new ServiceException(DICT_DATA_TRANS_ERROR, errorCode);
        }
        JSONArray arr = obj.getJSONArray("trans_result");

        List<DictDatai18nDO> i18nList = new ArrayList<>();
        for (int i = 0; i < arr.size(); i++) {
            DictDataDO dictDataDO = dataMap.get(i);
            JSONObject item = arr.getJSONObject(i);
            String src = item.getString("src");
            String dst = item.getString("dst");

            if (dictDataDO == null || !dictDataDO.getLabel().equals(UnicodeUtil.toString(src))) {
                continue;
            }

            String name = UnicodeUtil.toString(dst);
            DictDatai18nDO i18n = new DictDatai18nDO();
            i18n.setDictDataId(dictDataDO.getId());
            i18n.setLocale(to);
            if (LanguageEnum.EN.getLanguage().equals(to)) {
                i18n.setLabel(Arrays.stream(name.split("\\s+"))
                        .filter(s -> !s.isEmpty())
                        .map(s -> Character.toUpperCase(s.charAt(0)) + s.substring(1))
                        .collect(Collectors.joining(" ")));
            } else {
                i18n.setLabel(name);
            }

            i18nList.add(i18n);
        }

        if (CollUtil.isNotEmpty(i18nList)) {
            dictDatai18nMapper.insertBatch(i18nList);
            log.info("========== 【字典数据标签国际化】{}成功，插入 {} 条 =====", to, i18nList.size());
        }
    }

    @Override
    public Integer getI18nVersion() {
        List<DictDatai18nVersionDO> dictDatai18nVersionDOS = dictDataI18nVersionMapper.selectList();
        if (CollUtil.isNotEmpty(dictDatai18nVersionDOS)) {
            return dictDatai18nVersionDOS.get(0).getVersion();
        }
        return 0;
    }

    @Override
    public void updateI18nVersion() {
        // 更新版本号
        dictDataI18nVersionMapper.updateVersion();

        // 通知清理cache
        stringRedisTemplate.convertAndSend(com.cf.imes.framework.redis.constants.RedisKeyConstants.DICT_DATA_I18N_CACHE_REFRESH, "");
    }

    @Override
    public Map<String, List<DictDataSimpleRespVO>> getAllLanguageDictData() {
        Map<String, List<DictDataSimpleRespVO>> cache = DictDataI18nCacheUtils.getCache();
        if (MapUtil.isNotEmpty(cache)) {
            return cache;
        }
        // dictData数据作为中文字典
        List<DictDataDO> list = dictDataMapper.selectListByStatusAndDictType(CommonStatusEnum.ENABLE.getStatus(), null);
        list.sort(COMPARATOR_TYPE_AND_SORT);

        List<Long> ids = list.stream().map(DictDataDO::getId).toList();

        Map<String, List<DictDataSimpleRespVO>> resultMap = new ConcurrentHashMap<>();
        // 获取每种语言的字典数据
        for (LanguageEnum languageEnum : LanguageEnum.values()) {
            if (LanguageEnum.ZH.equals(languageEnum)) {
                resultMap.put(languageEnum.getLanguage(), BeanUtils.toBean(list, DictDataSimpleRespVO.class));
            } else {
                resultMap.put(languageEnum.getLanguage(), BeanUtils.toBean(dictDatai18nMapper.getLocaleDictData(ids, languageEnum.getLanguage()), DictDataSimpleRespVO.class));
            }
        }

        // 缓存
        DictDataI18nCacheUtils.putCache(resultMap);
        return resultMap;
    }
}
