package cn.axzo.msg.center.message.service.impl;

import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.framework.core.util.MapUtil;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.dal.MessageTemplateGroupDao;
import cn.axzo.msg.center.domain.entity.MessageTemplateGroup;
import cn.axzo.msg.center.message.service.MessageGroupNodeService;
import cn.axzo.msg.center.message.service.MessageTemplateGroupService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author cold_blade
 * @date 2023/10/5
 * @version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MessageTemplateGroupServiceImpl implements MessageTemplateGroupService {

    private final MessageGroupNodeService messageGroupNodeService;
    private final MessageTemplateGroupDao messageTemplateGroupDao;

    @Override
    public List<String> listMessageTemplateCodes(Collection<String> leafNodePathCodes) {
        if (CollectionUtils.isEmpty(leafNodePathCodes)) {
            log.info("leafNodePathCodes is empty.");
            return Collections.emptyList();
        }
        return messageTemplateGroupDao.lambdaQuery()
                .in(MessageTemplateGroup::getPath, leafNodePathCodes)
                .eq(MessageTemplateGroup::getIsDelete, 0)
                .list().stream()
                .map(MessageTemplateGroup::getTemplateCode)
                .collect(Collectors.toList());
    }

    @Override
    public Map<String, List<String>> groupingByMessageTemplateCodes(Collection<String> leafNodePathCodes) {
        if (CollectionUtils.isEmpty(leafNodePathCodes)) {
            log.info("leafNodePathCodes is empty.");
            return Collections.emptyMap();
        }
        Map<String, List<MessageTemplateGroup>> groupingBy = messageTemplateGroupDao.lambdaQuery()
                .in(MessageTemplateGroup::getPath, leafNodePathCodes)
                .select(MessageTemplateGroup::getPath, MessageTemplateGroup::getTemplateCode)
                .list().stream()
                .collect(Collectors.groupingBy(MessageTemplateGroup::getTemplateCode));
        final Map<String, List<String>> result = Maps.newHashMap();
        groupingBy.forEach((key, value) -> {
            List<String> paths = value.stream().map(MessageTemplateGroup::getPath).collect(Collectors.toList());
            result.put(key, paths);
        });
        return result;
    }

    @Override
    public void templateGroup(String templateNode, Collection<String> groupNodeCodes) {
        if (StringUtils.isBlank(templateNode)
                || CollectionUtils.isEmpty(groupNodeCodes)) {
            log.info("the param is invalid. templateNode:[{}], groupNodeCodes:{}", templateNode, groupNodeCodes);
            return;
        }
        Map<String, String> pathMap = messageGroupNodeService.leafGroupNodeCodePaths(groupNodeCodes);
        List<MessageTemplateGroup> rows = groupNodeCodes.stream()
                .filter(pathMap::containsKey)
                .map(e -> {
                    MessageTemplateGroup group = new MessageTemplateGroup();
                    group.setTemplateCode(templateNode);
                    group.setPath(pathMap.get(e));
                    return group;
                }).collect(Collectors.toList());
        messageTemplateGroupDao.saveBatch(rows);
    }

    @Override
    public void templateGroup(Map<String, List<String>> templateGroupMap) {
        if (MapUtil.isEmpty(templateGroupMap)) {
            log.info("the templateGroupMap is empty.");
            return;
        }
        Set<String> groupNodes = templateGroupMap.values().stream()
                .flatMap(Collection::stream)
                .collect(Collectors.toSet());
        Map<String, String> pathMap = messageGroupNodeService.leafGroupNodeCodePaths(groupNodes);
        List<MessageTemplateGroup> rows = templateGroupMap.entrySet().stream()
                .flatMap(e -> e.getValue().stream()
                        .filter(pathMap::containsKey)
                        .map(nodeCode -> {
                            MessageTemplateGroup group = new MessageTemplateGroup();
                            group.setTemplateCode(e.getKey());
                            group.setPath(pathMap.get(nodeCode));
                            return group;
                        })
                ).collect(Collectors.toList());
        messageTemplateGroupDao.saveBatch(rows);
    }

    @Override
    public void updateTemplateGroup(String templateNode, Collection<String> groupNodeCodes) {
        // 先解除之前的关联关系
        deleteTemplateGroup(templateNode);
        // 构建新的关联关系
        templateGroup(templateNode, groupNodeCodes);
    }

    @Override
    public void deleteTemplateGroup(String templateNode) {
        if (StringUtils.isBlank(templateNode)) {
            log.info("the templateNode is blank.");
            return;
        }
        messageTemplateGroupDao.lambdaUpdate()
                .eq(MessageTemplateGroup::getTemplateCode, templateNode)
                .remove();
    }

    @Override
    public Map<String, List<String>> listMessageTemplateGroupPaths(Collection<String> templateCodes) {
        if (CollectionUtils.isEmpty(templateCodes)) {
            return Collections.emptyMap();
        }
        return messageTemplateGroupDao.lambdaQuery()
                .in(MessageTemplateGroup::getTemplateCode, templateCodes)
                .eq(MessageTemplateGroup::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .list().stream()
                .collect(Collectors.groupingBy(MessageTemplateGroup::getTemplateCode,
                        Collectors.mapping(MessageTemplateGroup::getPath, Collectors.toList())));
    }

    @Override
    public void remove(String curGroupNodeCode, Collection<String> templateCodes) {
        // 参数校验
        AssertUtil.isTrue(StringUtils.isNotBlank(curGroupNodeCode), "curGroupNodeCode can not be blank");
        AssertUtil.notEmpty(templateCodes, "templateCodes can not be empty");
        // 通过分类结点编码获取其对应的结点编码的路径
        Map<String, String> nodeCodePathMap = messageGroupNodeService
                .leafGroupNodeCodePaths(Lists.newArrayList(curGroupNodeCode));
        // 通过能否成功获取到其树路径来校验其有效性
        AssertUtil.isFalse(nodeCodePathMap.isEmpty(), "curGroupNodeCode is invalid");
        // 统计指定的模板编码关联的分类数量
        Map<String, Long> templateCodeList = messageTemplateGroupDao.lambdaQuery()
                .in(MessageTemplateGroup::getTemplateCode, templateCodes)
                .eq(MessageTemplateGroup::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                // 这里仅查询模板编码字段即可
                .select(MessageTemplateGroup::getTemplateCode)
                .list().stream()
                .map(MessageTemplateGroup::getTemplateCode)
                // 内存中来做groupingBy操作
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        // 筛选出仅关联了一个分类的模板编码
        List<String> onlyContainsOneTemplateCodes = templateCodeList.entrySet().stream()
                .filter(e -> Objects.equals(e.getValue(), 1L))
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        AssertUtil.isEmpty(onlyContainsOneTemplateCodes, removeErrorStr(onlyContainsOneTemplateCodes));
        // 解除关联关系
        messageTemplateGroupDao.lambdaUpdate()
                .in(MessageTemplateGroup::getTemplateCode, templateCodes)
                .eq(MessageTemplateGroup::getPath, nodeCodePathMap.get(curGroupNodeCode))
                .remove();
    }

    @Override
    public void move(String srcGroupNodeCode, String tgtGroupNodeCode, Collection<String> templateCodes) {
        // 参数校验
        AssertUtil.isTrue(StringUtils.isNotBlank(srcGroupNodeCode), "srcGroupNodeCode can not be blank");
        AssertUtil.isTrue(StringUtils.isNotBlank(tgtGroupNodeCode), "tgtGroupNodeCode can not be blank");
        AssertUtil.notEmpty(templateCodes, "templateCodes can not be empty");
        // 通过分类结点编码获取其对应的结点编码的路径
        Map<String, String> nodeCodePathMap = messageGroupNodeService
                .leafGroupNodeCodePaths(Lists.newArrayList(srcGroupNodeCode, tgtGroupNodeCode));
        // 通过能否成功获取到其树路径来校验其有效性
        AssertUtil.isTrue(StringUtils.isNotBlank(nodeCodePathMap.get(srcGroupNodeCode)), "srcGroupNodeCode is invalid");
        AssertUtil.isTrue(StringUtils.isNotBlank(nodeCodePathMap.get(tgtGroupNodeCode)), "tgtGroupNodeCode is invalid");
        // 获取已经存在目标分类与模板关联关系的模板编码列表
        List<String> existTgtRelationTemplates = messageTemplateGroupDao.lambdaQuery()
                .eq(MessageTemplateGroup::getPath, nodeCodePathMap.get(tgtGroupNodeCode))
                .in(MessageTemplateGroup::getTemplateCode, templateCodes)
                .select(MessageTemplateGroup::getTemplateCode)
                .list().stream().map(MessageTemplateGroup::getTemplateCode).collect(Collectors.toList());
        templateCodes = templateCodes.stream()
                // 过滤掉已经存在目标分类与模板关联关系存在的模板编码
                .filter(e -> !existTgtRelationTemplates.contains(e))
                .collect(Collectors.toList());
        if (templateCodes.isEmpty()) {
            log.info("目标分类与模板的关联关系已存在. existTgtRelationTemplates:{}", existTgtRelationTemplates);
            return;
        }
        // 更新分类路径与模板编码的映射关系
        messageTemplateGroupDao.lambdaUpdate()
                .eq(MessageTemplateGroup::getPath, nodeCodePathMap.get(srcGroupNodeCode))
                .in(MessageTemplateGroup::getTemplateCode, templateCodes)
                .set(MessageTemplateGroup::getPath, nodeCodePathMap.get(tgtGroupNodeCode))
                .update();
    }

    private String removeErrorStr(List<String> onlyContainsOneTemplateCodes) {
        return String.format("模板：%s。解除关联后，将无关联分类！本次操作无效！",
                String.join("、", onlyContainsOneTemplateCodes));
    }
}
