package com.zt.plat.module.qms.common.dic.service;

import cn.hutool.core.collection.CollUtil;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.qms.common.dic.controller.vo.*;
import com.zt.plat.module.qms.common.dic.controller.vo.DictionaryBusinessPageReqVO;
import com.zt.plat.module.qms.common.dic.controller.vo.DictionaryBusinessRespVO;
import com.zt.plat.module.qms.common.dic.controller.vo.DictionaryBusinessSaveReqVO;
import com.zt.plat.module.qms.common.dic.dal.dataobject.DictionaryBusinessDO;
import com.zt.plat.module.qms.common.dic.dal.mapper.DictionaryBusinessMapper;
import com.zt.plat.module.qms.core.constant.CacheConstant;
import com.zt.plat.module.qms.core.constant.CommonConstant;
import com.zt.plat.module.qms.core.constant.DataTypeConstant;
import com.zt.plat.module.qms.core.legend.SqlInjectionUtil;
import com.zt.plat.module.qms.core.legend.security.DictQueryBlackListHandler;
import com.zt.plat.module.qms.core.legend.vo.DictModel;
import com.zt.plat.module.qms.core.legend.vo.DictModelMany;
import com.zt.plat.module.qms.enums.ErrorCodeConstants;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import java.util.*;

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.qms.core.constant.DataTypeConstant.*;
import static com.zt.plat.module.qms.enums.ErrorCodeConstants.*;

/**
 * 业务参数字典 Service 实现类
 *
 * @author 后台管理
 */
@Service
@Validated
@Slf4j
public class DictionaryBusinessServiceImpl implements DictionaryBusinessService {
    private int id_path_update_level_limit = 7;
    @Resource private DictionaryBusinessMapper dictionaryBusinessMapper;
    @Resource private DictQueryBlackListHandler dictQueryBlackListHandler;


    @Override
    @Cacheable(value = CacheConstant.QMS_DICT_TABLE_CACHE, unless = "#result == null ")
    public String queryTableDictTextByKey(String table, String text, String code, String key) {
        log.debug("无缓存dictTable的时候调用这里！");

        // 1.表字典黑名单check
        String str = table+","+text+","+code;
        if(!dictQueryBlackListHandler.isPass(str)){
            log.error(dictQueryBlackListHandler.getError());
            return null;
        }
        // 2.sql注入check
        SqlInjectionUtil.filterContent(table, text, code, key);

        // 3.针对采用 ${}写法的表名和字段进行转义和check
        table = SqlInjectionUtil.getSqlInjectTableName(table);
        text = SqlInjectionUtil.getSqlInjectField(text);
        code = SqlInjectionUtil.getSqlInjectField(code);

        List<DictModel> dictModeList = dictionaryBusinessMapper.queryTableDictCustom(table, text, code, null, Arrays.asList(key));
        if(CollectionUtils.isEmpty(dictModeList)){
            return null;
        }
        return dictModeList.get(0).getText();
    }

    @Override
    @Cacheable(value = CacheConstant.QMS_DICT_BIZ_CACHE,key = "#code+':'+#key", unless = "#result == null ")
    public String queryDictTextByKey(String code, String key) {
        return dictionaryBusinessMapper.queryDictTextByKey(code, key);
    }

    @Override
    @Cacheable(value = CacheConstant.QMS_DICT_BIZ_CACHE,key = "#code+':'+#id", unless = "#result == null ")
    public String queryDictTextByID(String code, String id) {
        return dictionaryBusinessMapper.queryDictTextByID(code, id);
    }

    @Override
    public List<DictModel> queryTableDictText(String table, String text, String code, List<String> keys) {
        // 1.表字典黑名单check
        String str = table+","+text+","+code;
        if(!dictQueryBlackListHandler.isPass(str)){
            log.error(dictQueryBlackListHandler.getError());
            return null;
        }

        // 2.分割SQL获取表名和条件
        String filterSql = null;
        if(table.toLowerCase().indexOf(CommonConstant.SQL_WHERE)>0){
            String[] arr = table.split(" (?i)where ");
            table = arr[0];
            filterSql = arr[1];
        }

        // 3.SQL注入check
        SqlInjectionUtil.filterContent(table, text, code);
        SqlInjectionUtil.specialFilterContentForDictSql(filterSql);

        // 4.针对采用 ${}写法的表名和字段进行转义和check
        table = SqlInjectionUtil.getSqlInjectTableName(table);
        text = SqlInjectionUtil.getSqlInjectField(text);
        code = SqlInjectionUtil.getSqlInjectField(code);

        return dictionaryBusinessMapper.queryTableDictCustom(table, text, code, filterSql, keys);
    }

    @Override
    public Map<String, List<DictModel>> queryManyDict(List<String> dictCodeList, List<String> itemKeys) {
        //将dictCodeList分组
        List<String> listByKey = new ArrayList<>();
        List<String> listByID = new ArrayList<>();
        for(String dictCode: dictCodeList){
            if(dictCode.contains(DICT_ANNOTATION_SPLIT)){
                String[] dictCodeArr = dictCode.split(DICT_ANNOTATION_SPLIT);
                if(dictCodeArr[1].equals(DICT_ANNOTATION_KEY_TYPE_KEY)) {
                    listByKey.add(dictCodeArr[0]);
                }else
                    listByID.add(dictCodeArr[0]);
            }else{
                listByKey.add(dictCode);
            }
        }
        Map<String, List<DictModel>> dictMap = new HashMap<>(5);
        if(!listByKey.isEmpty())
            dictMap = queryManyDictBy(DICT_ANNOTATION_KEY_TYPE_KEY, dictMap, listByKey, itemKeys);
        if(!listByID.isEmpty())
            dictMap = queryManyDictBy(DICT_ANNOTATION_KEY_TYPE_ID, dictMap, listByID, itemKeys);
        return dictMap;
    }


    private Map<String, List<DictModel>> queryManyDictBy(String dictValType, Map<String, List<DictModel>> dictMap, List<String> dictCodeList, List<String> itemKeys){
        LambdaQueryWrapper<DictionaryBusinessDO> query = new LambdaQueryWrapper<>();
        query.in(DictionaryBusinessDO::getKey, dictCodeList);
        query.eq(DictionaryBusinessDO::getNodeType,  DataTypeConstant.DATA_TYPE_CATEGORY);
//        query.eq(DictionaryBusinessDO::getDeleted, 0);
        List<DictionaryBusinessDO> parList = dictionaryBusinessMapper.selectListWithoutPermissionCheck(query);
        if(parList.isEmpty())
            return dictMap;
        List<Long> parIds = parList.stream().map(DictionaryBusinessDO::getId).toList();
        query.clear();

        //通过key查询
        query.in(DictionaryBusinessDO::getParentId, parIds);

        query.eq(DictionaryBusinessDO::getNodeType,  DataTypeConstant.DATA_TYPE_DATA);
        if(dictValType.equals(DICT_ANNOTATION_KEY_TYPE_KEY))
            query.in(DictionaryBusinessDO::getKey, itemKeys);
        else
            query.in(DictionaryBusinessDO::getId, itemKeys);
        List<DictionaryBusinessDO> itemList = dictionaryBusinessMapper.selectListWithoutPermissionCheck(query);
        List<DictModelMany> modelItemList = new ArrayList<>();
        for(DictionaryBusinessDO item: itemList){
            Long parId = item.getParentId();
            DictionaryBusinessDO parNode = parList.stream().filter(e->e.getId().equals(parId)).findFirst().orElse(null);
            if(parNode == null)
                continue;
            if(dictValType.equals(DICT_ANNOTATION_KEY_TYPE_KEY))
                modelItemList.add(new DictModelMany(parNode.getKey(), item.getKey(), item.getName()));
            else
                modelItemList.add(new DictModelMany(parNode.getKey(), item.getId().toString(), item.getName()));
        }
        for(DictModelMany dict: modelItemList){
            List<DictModel> list = dictMap.computeIfAbsent(dict.getDictCode(), i -> new ArrayList<>());
            list.add(new DictModel(dict.getValue(), dict.getText()));
        }
        return dictMap;
    }

//    @Override
//    public Map<String, List<DictModel>> queryManyDictByIDs(List<String> dictCodeList, List<String> ids) {
//        Map<String, List<DictModel>> dictMap = new HashMap<>(5);
//        LambdaQueryWrapper<DictionaryBusinessDO> query = new LambdaQueryWrapper<>();
//        query.in(DictionaryBusinessDO::getKey, dictCodeList);
//        query.eq(DictionaryBusinessDO::getNodeType,  DataTypeConstant.DATA_TYPE_CATEGORY);
////        query.eq(DictionaryBusinessDO::getDeleted, 0);
//        List<DictionaryBusinessDO> parList = dictionaryBusinessMapper.selectListWithoutPermissionCheck(query);
//        if(parList.isEmpty())
//            return dictMap;
//        List<Long> parIds = parList.stream().map(DictionaryBusinessDO::getId).toList();
//        query.clear();
//        query.in(DictionaryBusinessDO::getParentId, parIds);
//        query.eq(DictionaryBusinessDO::getNodeType,  DataTypeConstant.DATA_TYPE_DATA);
//        query.in(DictionaryBusinessDO::getId, ids);
//        List<DictionaryBusinessDO> itemList = dictionaryBusinessMapper.selectListWithoutPermissionCheck(query);
//        List<DictModelMany> modelItemList = new ArrayList<>();
//        for(DictionaryBusinessDO item: itemList){
//            Long parId = item.getParentId();
//            DictionaryBusinessDO parNode = parList.stream().filter(e->e.getId().equals(parId)).findFirst().orElse(null);
//            if(parNode == null)
//                continue;
//            modelItemList.add(new DictModelMany(parNode.getKey(), item.getId().toString(), item.getName()));
//        }
//        for(DictModelMany dict: modelItemList){
//            List<DictModel> list = dictMap.computeIfAbsent(dict.getDictCode(), i -> new ArrayList<>());
//            list.add(new DictModel(dict.getValue(), dict.getText()));
//        }
//        return dictMap;
//    }

    /*
    * 通过分类key获取数据列表*/
    @Override
    public List<DictionaryBusinessDO> queryDictItemsByKey(String key) {
        LambdaQueryWrapper<DictionaryBusinessDO> query = new LambdaQueryWrapper<>();
        query.eq(DictionaryBusinessDO::getKey, key);
        query.eq(DictionaryBusinessDO::getNodeType, DataTypeConstant.DATA_TYPE_CATEGORY);
        List<DictionaryBusinessDO> list = dictionaryBusinessMapper.selectListWithoutPermissionCheck(query);
        if(list.isEmpty())
            throw exception(DICTIONARY_BUSINESS_NOT_EXISTS);
        if(list.size() > 1)
            throw exception(DICTIONARY_BUSINESS_CATEGORY_MORE_THAN_ONE);
        DictionaryBusinessDO entity = list.get(0);
        List<DictionaryBusinessDO> dataList = this.listByParId(entity.getId(), DataTypeConstant.DATA_TYPE_DATA);

        return dataList;
    }

    /*
    * 通过数据key获取数据*/
    @Override
    public CommonResult<DictionaryBusinessRespVO> getDataByDataKey(String key) {
        LambdaQueryWrapper<DictionaryBusinessDO> query = new LambdaQueryWrapper<>();
        query.eq(DictionaryBusinessDO::getKey, key);
        query.eq(DictionaryBusinessDO::getNodeType, DataTypeConstant.DATA_TYPE_DATA);
        List<DictionaryBusinessDO> list = dictionaryBusinessMapper.selectListWithoutPermissionCheck(query);
        if(list.isEmpty())
            return CommonResult.error(DICTIONARY_BUSINESS_NOT_EXISTS);
        if(list.size() > 1)
            throw exception(DICTIONARY_BUSINESS_DATA_MORE_THAN_ONE);
        DictionaryBusinessRespVO vo = BeanUtils.toBean(list.get(0), DictionaryBusinessRespVO.class);
        return CommonResult.success(vo);
    }

    @Override
    public String getValueByDataKey(String key) {
        CommonResult<DictionaryBusinessRespVO> result = getDataByDataKey(key);
        if(!result.isSuccess())
            return null;
        return result.getData().getValue();
    }

    @Override
    public CommonResult<DictionaryBusinessRespVO> saveCategory(DictionaryBusinessSaveReqVO entity) {
        if(entity.getParentId() == null)
            entity.setParentId(0L);
        Long parentId = entity.getParentId();
        List<DictionaryBusinessDO> checkList = listByParId(parentId, DataTypeConstant.DATA_TYPE_CATEGORY);
        boolean duplicate = false;
        DictionaryBusinessDO duplicateEntity = checkList.stream().filter(e->e.getName().equals(entity.getName())).findFirst().orElse(null);
        if(duplicateEntity != null && !duplicateEntity.getId().equals(entity.getId()))
            duplicate = true;
        if(duplicate)
            throw exception(ErrorCodeConstants.DICTIONARY_BUSINESS_CLASSIFY_DUPLICATE);
        entity.setNodeType(DataTypeConstant.DATA_TYPE_CATEGORY);
        Long id = entity.getId();
        DictionaryBusinessDO dic = BeanUtils.toBean(entity, DictionaryBusinessDO.class);
        if(ObjectUtils.isEmpty(id)){
            dictionaryBusinessMapper.insert(dic);
            id = dic.getId();
        }
        DictionaryBusinessDO backData = this.getDictionaryBusiness(id);
        Long parentId_ = backData.getParentId();
        //检查parId，避免父节点设置为本节点的后代节点
        if(parentId_.equals(id))
            throw new ServiceException(ErrorCodeConstants.DATA_COLLECTION_CLASSIFY_PARENT_ERROR);
        String idPath = getIdPath(dic);
        int curIdIndex = idPath.indexOf("/" + id.toString() + "/");
        int parIdIndex = idPath.indexOf("/" + parentId.toString() + "/");
        if(curIdIndex <= parIdIndex)
            throw new ServiceException(ErrorCodeConstants.DATA_COLLECTION_CLASSIFY_PARENT_ERROR);
        dic.setIdPath(idPath);

        //更新后代节点路径
        updateAllIdPath(id, 1);
        dictionaryBusinessMapper.updateById(dic);
        return CommonResult.success(BeanUtils.toBean(dic, DictionaryBusinessRespVO.class));
    }

    @Override
    public CommonResult<DictionaryBusinessRespVO> saveData(DictionaryBusinessSaveReqVO createReqVO) {
        Long id = createReqVO.getId();
        DictionaryBusinessDO entity = BeanUtils.toBean(createReqVO, DictionaryBusinessDO.class);
        if(id == null){
            dictionaryBusinessMapper.insert(entity);
        }else{
            dictionaryBusinessMapper.updateById(entity);
        }
        DictionaryBusinessRespVO vo = BeanUtils.toBean(entity, DictionaryBusinessRespVO.class);
        return CommonResult.success( vo);
    }

    @Override
    public CommonResult<String> updateAllIdPath(Long parentId, Integer level) {
        if(level > id_path_update_level_limit)
            return CommonResult.success("更新完成（超出层数限制："+id_path_update_level_limit+"）");
        List<DictionaryBusinessDO> list = listByParId(parentId, DataTypeConstant.DATA_TYPE_CATEGORY);
        DictionaryBusinessDO parEntity = this.getDictionaryBusiness(parentId);
        if(list.isEmpty())
            return CommonResult.success("");
        String parIdPath = "/0/";
        if(parEntity != null)
            parIdPath = parEntity.getIdPath();
        for(DictionaryBusinessDO entity : list){
            entity.setIdPath(parIdPath + "/" + entity.getId() + "/");
            dictionaryBusinessMapper.updateById( entity);
            updateAllIdPath(entity.getId(),level+1);
        }
        return CommonResult.success("");
    }

    @Override
    public List<DictionaryBusinessDO> getTreeData() {
        LambdaQueryWrapper<DictionaryBusinessDO> query = new LambdaQueryWrapper<>();
        query.eq(DictionaryBusinessDO::getNodeType, DataTypeConstant.DATA_TYPE_CATEGORY);
        query.orderByAsc(DictionaryBusinessDO::getSortNo);
        return dictionaryBusinessMapper.selectListWithoutPermissionCheck(query);
    }

    @Override
    public List<DictionaryBusinessDO> listByParId(Long parId, String nodeType) {
        LambdaQueryWrapper<DictionaryBusinessDO> query = new LambdaQueryWrapper<>();
        query.eq(DictionaryBusinessDO::getParentId, parId);
        if(!ObjectUtils.isEmpty(nodeType))
            query.eq(DictionaryBusinessDO::getNodeType, nodeType);
        query.orderByAsc(DictionaryBusinessDO::getSortNo);
        return dictionaryBusinessMapper.selectListWithoutPermissionCheck(query);
    }

    @Override
    public DictionaryBusinessRespVO createDictionaryBusiness(DictionaryBusinessSaveReqVO createReqVO) {
        // 插入
        DictionaryBusinessDO dictionaryBusiness = BeanUtils.toBean(createReqVO, DictionaryBusinessDO.class);
        dictionaryBusinessMapper.insert(dictionaryBusiness);
        // 返回
        return BeanUtils.toBean(dictionaryBusiness, DictionaryBusinessRespVO.class);
    }

    @Override
    public void updateDictionaryBusiness(DictionaryBusinessSaveReqVO updateReqVO) {
        // 校验存在
        validateDictionaryBusinessExists(updateReqVO.getId());
        // 更新
        DictionaryBusinessDO updateObj = BeanUtils.toBean(updateReqVO, DictionaryBusinessDO.class);
        dictionaryBusinessMapper.updateById(updateObj);
    }

    @Override
    public void deleteDictionaryBusiness(Long id) {
        // 校验存在
        validateDictionaryBusinessExists(id);
        // 删除
        dictionaryBusinessMapper.deleteById(id);
    }

    @Override
        public void deleteDictionaryBusinessListByIds(List<Long> ids) {
        // 校验存在
        validateDictionaryBusinessExists(ids);
        // 删除
        dictionaryBusinessMapper.deleteByIds(ids);
        }

    private void validateDictionaryBusinessExists(List<Long> ids) {
        List<DictionaryBusinessDO> list = dictionaryBusinessMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(DICTIONARY_BUSINESS_NOT_EXISTS);
        }
    }

    private void validateDictionaryBusinessExists(Long id) {
        if (dictionaryBusinessMapper.selectById(id) == null) {
            throw exception(DICTIONARY_BUSINESS_NOT_EXISTS);
        }
    }

    @Override
    public DictionaryBusinessDO getDictionaryBusiness(Long id) {
        return dictionaryBusinessMapper.selectById(id);
    }

    @Override
    public PageResult<DictionaryBusinessDO> getDictionaryBusinessPage(DictionaryBusinessPageReqVO pageReqVO) {
        return dictionaryBusinessMapper.selectPage(pageReqVO);
    }

    private String getIdPath(DictionaryBusinessDO entity){
        String parIdPath = "";
        if(ObjectUtils.isEmpty(entity.getParentId()) || 0L == entity.getParentId())
            parIdPath = "/0/";
        DictionaryBusinessDO parEntity = this.getDictionaryBusiness(entity.getParentId());
        if(parEntity != null){
            parIdPath = parEntity.getIdPath();
        }
        return parIdPath + entity.getId() + "/";
    }
}