package com.ningdatech.pmapi.meta.manage;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.common.enumeration.BoolDisplayEnum;
import com.ningdatech.pmapi.common.util.BizUtils;
import com.ningdatech.pmapi.meta.assembler.MetaDictionaryAssembler;
import com.ningdatech.pmapi.meta.assembler.MetaTagAssembler;
import com.ningdatech.pmapi.meta.constant.DictExpertInfoTypeEnum;
import com.ningdatech.pmapi.meta.constant.ExpertTagEnum;
import com.ningdatech.pmapi.meta.constant.TagConst;
import com.ningdatech.pmapi.meta.helper.DictionaryCache;
import com.ningdatech.pmapi.meta.helper.TagCache;
import com.ningdatech.pmapi.meta.model.dto.DictionaryDTO;
import com.ningdatech.pmapi.meta.model.dto.TagTreeDTO;
import com.ningdatech.pmapi.meta.model.entity.ExpertDictionary;
import com.ningdatech.pmapi.meta.model.entity.ExpertTag;
import com.ningdatech.pmapi.meta.model.entity.MetaDictionary;
import com.ningdatech.pmapi.meta.model.entity.MetaTag;
import com.ningdatech.pmapi.meta.model.po.*;
import com.ningdatech.pmapi.meta.model.vo.*;
import com.ningdatech.pmapi.meta.service.IExpertDictionaryService;
import com.ningdatech.pmapi.meta.service.IExpertTagService;
import com.ningdatech.pmapi.meta.service.IMetaDictionaryService;
import com.ningdatech.pmapi.meta.service.IMetaTagService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author liuxinxin
 * @date 2023/2/22 下午2:34
 */
@Component
@RequiredArgsConstructor
public class MetaManage {

    private final DictionaryCache dictionaryCache;
    private final TagCache tagCache;
    private final IMetaDictionaryService metaDictionaryManageService;
    private final IExpertDictionaryService iExpertDictionaryService;
    private final IMetaTagService iMetaTagManageService;
    private final IExpertTagService iExpertTagService;

    public List<ResDictionaryListVO> getDictionaryList(ReqDictionaryListPO request) {
        List<String> dictionaryTypeList = request.getDictionaryTypeList();
        if (CollectionUtils.isEmpty(dictionaryTypeList)) {
            dictionaryTypeList = DictExpertInfoTypeEnum.getAll();
        }

        List<ResDictionaryListVO> resDictionaryListVos = new ArrayList<>();
        for (String dictionaryType : dictionaryTypeList) {
            ResDictionaryListVO resDictionaryListVO = new ResDictionaryListVO();
            resDictionaryListVO.setDictionaryName(dictionaryType);
            List<DictionaryDTO> dictionaryList = dictionaryCache.getDictionaryListByDictionaryType(dictionaryType);
            List<ResDictionaryVO> resDictionaryVOList = dictionaryList.stream()
                    .map(MetaDictionaryAssembler::toDictionaryVO).collect(Collectors.toList());
            resDictionaryListVO.setDictionaryList(resDictionaryVOList);
            resDictionaryListVos.add(resDictionaryListVO);
        }
        return resDictionaryListVos;
    }

    public List<ResDictionaryListVO> commonDictList(ReqDictionaryListPO request) {
        List<ResDictionaryListVO> result = new ArrayList<>();
        if (request.getAllDict()) {
            Map<String, List<MetaDictionary>> groupByType = CollUtils.group(metaDictionaryManageService.list(), MetaDictionary::getDictionaryType);
            groupByType.forEach((k, v) -> {
                ResDictionaryListVO resDictionaryListVO = new ResDictionaryListVO();
                resDictionaryListVO.setDictionaryName(k);
                List<ResDictionaryVO> resDictionaryVOList = v.stream()
                        .map(w -> {
                            ResDictionaryVO vo = new ResDictionaryVO();
                            vo.setDescribe(w.getDictionaryDescribe());
                            vo.setDictionaryCode(w.getDictionaryCode());
                            vo.setDictionaryType(w.getDictionaryType());
                            vo.setName(w.getDictionaryName());
                            vo.setReadonly(w.getReadonly());
                            vo.setSortValue(w.getSortValue());
                            return vo;
                        }).collect(Collectors.toList());
                resDictionaryListVO.setDictionaryList(resDictionaryVOList);
                result.add(resDictionaryListVO);
            });
        } else {
            List<String> dictionaryTypeList = request.getDictionaryTypeList();
            for (String dictionaryType : dictionaryTypeList) {
                ResDictionaryListVO resDictionaryListVO = new ResDictionaryListVO();
                resDictionaryListVO.setDictionaryName(dictionaryType);
                List<DictionaryDTO> dictionaryList = dictionaryCache.getDictionaryListByDictionaryType(dictionaryType);
                List<ResDictionaryVO> resDictionaryVOList = dictionaryList.stream()
                        .map(MetaDictionaryAssembler::toDictionaryVO).collect(Collectors.toList());
                resDictionaryListVO.setDictionaryList(resDictionaryVOList);
                result.add(resDictionaryListVO);
            }
        }
        return result;
    }

    public List<ResTagTreeVO> getTagTree(ReqTagListPO request) {
        List<String> rootTagCodeList = request.getRootTagCodeList();
        if (CollectionUtils.isEmpty(rootTagCodeList)) {
            rootTagCodeList = ExpertTagEnum.getAll();
        }
        List<TagTreeDTO> tagTreeDTOList = new ArrayList<>();
        for (String rootTagCode : rootTagCodeList) {
            TagTreeDTO tagTreeDTO = tagCache.getTagTreeDTO(4, rootTagCode);
            if (Objects.nonNull(tagTreeDTO)) {
                tagTreeDTOList.add(tagTreeDTO);
            }
        }
        return MetaTagAssembler.toTagTreeVOList(tagTreeDTOList);
    }


    /**
     * 专家管理员才能调用
     *
     * @param reqAddDictionaryPO
     */
    @Transactional(rollbackFor = Exception.class)
    public void addDictionary(ReqAddDictionaryPO reqAddDictionaryPO) {
        String dictionaryType = reqAddDictionaryPO.getDictionaryType();
        boolean contains = DictExpertInfoTypeEnum.contains(dictionaryType);
        if (!contains) {
            throw BizException.wrap("无效的专家字典类型：%s", dictionaryType);
        }
        String name = reqAddDictionaryPO.getName();
        Integer sortValue = reqAddDictionaryPO.getSortValue();
        LambdaQueryWrapper<MetaDictionary> eq = Wrappers.lambdaQuery(MetaDictionary.class).eq(MetaDictionary::getDictionaryType, dictionaryType);
        List<MetaDictionary> dictionaryManageList = metaDictionaryManageService.list(eq);

        MetaDictionary saveRecord = new MetaDictionary();
        saveRecord.setCreateOn(LocalDateTime.now());
        saveRecord.setDictionaryName(name);
        saveRecord.setUpdateOn(LocalDateTime.now());
        saveRecord.setDictionaryCode(BizUtils.uuid32());
        saveRecord.setDictionaryType(dictionaryType);
        saveRecord.setReadonly(BoolDisplayEnum.N.name());
        saveRecord.setSortValue(sortValue);

        List<MetaDictionary> saveRecordList = new ArrayList<>();
        saveRecordList.add(saveRecord);
        for (MetaDictionary metaDictionary : dictionaryManageList) {
            if (metaDictionary.getSortValue() >= sortValue) {
                metaDictionary.setSortValue(metaDictionary.getSortValue() + 1);
            }
            saveRecordList.add(metaDictionary);
        }
        boolean remove = metaDictionaryManageService
                .remove(Wrappers.lambdaQuery(MetaDictionary.class).eq(MetaDictionary::getDictionaryType, dictionaryType));
        if (remove) {
            metaDictionaryManageService.saveBatch(saveRecordList);
        }
    }

    public void removeDictionary(String dictionaryCode) {
        LambdaUpdateWrapper<MetaDictionary> eq = Wrappers.lambdaUpdate(MetaDictionary.class)
                .eq(MetaDictionary::getDictionaryCode, dictionaryCode);
        MetaDictionary one = metaDictionaryManageService.getOne(eq);
        if (Objects.isNull(one)) {
            throw BizException.wrap("字典值不存在");
        }
        if (BoolDisplayEnum.judgeBoolean(one.getReadonly())) {
            throw BizException.wrap("内置字典值无法删除");
        }
        List<ExpertDictionary> expertDictionaryList = iExpertDictionaryService.list(Wrappers.lambdaQuery(ExpertDictionary.class).eq(ExpertDictionary::getDictionaryCode, dictionaryCode));
        if (CollUtil.isNotEmpty(expertDictionaryList)) {
            throw BizException.wrap("该字典值已被使用无法删除");
        }
        metaDictionaryManageService.removeById(one);
    }

    public void addTagType(ReqAddTagTypePO request) {
        String tagName = request.getTagName();
        LambdaQueryWrapper<MetaTag> in = Wrappers.lambdaQuery(MetaTag.class)
                .in(MetaTag::getTagName, tagName)
                .in(MetaTag::getParentCode, TagConst.TAG_ROOT_PARENT_CODE);
        List<MetaTag> list = iMetaTagManageService.list(in);
        if (CollectionUtils.isNotEmpty(list)) {
            throw BizException.wrap("该标签类型已存在");
        }
        MetaTag saveRecord = new MetaTag();
        saveRecord.setCreateOn(LocalDateTime.now());
        saveRecord.setUpdateOn(LocalDateTime.now());
        saveRecord.setParentCode(TagConst.TAG_ROOT_PARENT_CODE);
        saveRecord.setTagCode(BizUtils.uuid32());
        saveRecord.setTagName(tagName);
        saveRecord.setTagLevel(TagConst.TAG_ROOT_LEVEL);
        iMetaTagManageService.save(saveRecord);
    }

    public void removeTagName(String tagCode) {
        LambdaQueryWrapper<ExpertTag> eq = Wrappers.lambdaQuery(ExpertTag.class)
                .eq(ExpertTag::getTagCode, tagCode);
        List<ExpertTag> expertTagList = iExpertTagService.list(eq);
        if (CollectionUtils.isNotEmpty(expertTagList)) {
            throw BizException.wrap("该标签正在使用无法删除");
        }
        LambdaQueryWrapper<MetaTag> queryWrapper = Wrappers.lambdaQuery(MetaTag.class).eq(MetaTag::getParentCode, tagCode);
        List<MetaTag> list = iMetaTagManageService.list(queryWrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            throw BizException.wrap("该标签存在子标签无法删除");
        }
        LambdaQueryWrapper<MetaTag> metaTagManageLambdaQueryWrapper = Wrappers.lambdaQuery(MetaTag.class).eq(MetaTag::getTagCode, tagCode);
        MetaTag one = iMetaTagManageService.getOne(metaTagManageLambdaQueryWrapper);
        if (Objects.nonNull(one)) {
            iMetaTagManageService.removeById(one);
        }
    }

    public List<ResTagTypeVO> tagTypeList() {
        LambdaQueryWrapper<MetaTag> eq = Wrappers.lambdaQuery(MetaTag.class).eq(MetaTag::getParentCode, TagConst.TAG_ROOT_PARENT_CODE);
        List<MetaTag> list = iMetaTagManageService.list(eq);
        return list.stream().map(r -> {
            ResTagTypeVO resTagTypeVO = new ResTagTypeVO();
            resTagTypeVO.setTagCode(r.getTagCode());
            resTagTypeVO.setTagLevel(r.getTagLevel());
            resTagTypeVO.setTagName(r.getTagName());
            resTagTypeVO.setTagName(r.getTagName());
            return resTagTypeVO;
        }).collect(Collectors.toList());
    }

    public List<ResTagListVO> tagQuery(ReqTagQueryPO reqTagQueryPO) {
        String parentCode = reqTagQueryPO.getParentCode();
        if (StringUtils.isBlank(parentCode)) {
            parentCode = TagConst.TAG_ROOT_PARENT_CODE;
        }
        LambdaQueryWrapper<MetaTag> eq = Wrappers.lambdaQuery(MetaTag.class)
                .eq(MetaTag::getParentCode, parentCode);
        List<MetaTag> list = iMetaTagManageService.list(eq);

        List<String> tagCodeList = CollUtils.fieldList(list, MetaTag::getTagCode);
        Map<String, List<MetaTag>> hasChildrenMap = new HashMap<>(16);
        Map<String, List<ExpertTag>> useFrequencyMap = new HashMap<>(16);
        if (CollectionUtils.isNotEmpty(tagCodeList)) {
            List<MetaTag> childrenMetaTagList = iMetaTagManageService.list(Wrappers.lambdaQuery(MetaTag.class)
                    .in(MetaTag::getParentCode, tagCodeList));
            hasChildrenMap = CollUtils.group(childrenMetaTagList, MetaTag::getParentCode);

            List<ExpertTag> expertTagList = iExpertTagService.list(Wrappers.lambdaQuery(ExpertTag.class)
                    .in(ExpertTag::getTagCode, tagCodeList));
            useFrequencyMap = CollUtils.group(expertTagList, ExpertTag::getTagCode);
        }

        Map<String, List<MetaTag>> finalHasChildrenMap = hasChildrenMap;
        Map<String, List<ExpertTag>> finalUseFrequencyMap = useFrequencyMap;
        return list.stream().map(r -> {
            ResTagListVO resTagListVO = new ResTagListVO();
            resTagListVO.setCreateTime(r.getCreateOn());
            resTagListVO.setCreator(TagConst.TAG_CREATOR);
            resTagListVO.setTagLevel(r.getTagLevel());
            resTagListVO.setTagCode(r.getTagCode());
            resTagListVO.setTagName(r.getTagName());
            List<MetaTag> metaTagManages = finalHasChildrenMap.get(r.getTagCode());
            if (CollUtil.isNotEmpty(metaTagManages)) {
                resTagListVO.setIsLeaf(false);
                resTagListVO.setHasChildren(true);
            } else {
                resTagListVO.setIsLeaf(true);
                resTagListVO.setHasChildren(false);
            }
            long useFrequency = 0;
            List<ExpertTag> expertTagList = finalUseFrequencyMap.get(r.getTagCode());
            if (CollUtil.isNotEmpty(expertTagList)) {
                useFrequency = expertTagList.size();
            }
            resTagListVO.setUseFrequency(useFrequency);
            return resTagListVO;
        }).collect(Collectors.toList());
    }
}
