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

import cn.axzo.maokai.api.vo.response.tree.TreeBuilder;
import cn.axzo.maokai.api.vo.response.tree.ValueNode;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.dal.MessageGroupNodeDao;
import cn.axzo.msg.center.dal.MessageTemplateGroupDao;
import cn.axzo.msg.center.domain.entity.MessageGroupNode;
import cn.axzo.msg.center.domain.entity.MessageTemplateGroup;
import cn.axzo.msg.center.domain.persistence.BaseEntityExt;
import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;

/**
 * @author yanglin
 */
@Component
@RequiredArgsConstructor
public class GroupTemplateService {

    private final MessageGroupNodeDao messageGroupNodeDao;
    private final MessageTemplateGroupDao messageTemplateGroupDao;
    private final PendingMessageBizConfig cfg;

    // !! group nodes

    public RootNodeWrapper getGroupRoot() {
        TemplateCache cache = TemplateCache.get();
        if (cache != null && cache.getRoot() != null)
            return cache.getRoot();
        List<MessageGroupNode> groupNodes = messageGroupNodeDao.lambdaQuery()
                .eq(MessageGroupNode::getIsDelete, TableIsDeleteEnum.NORMAL.value)
                .list();
        Map<String, Long> nodeCode2Id = groupNodes.stream()
                .collect(toMap(MessageGroupNode::getCode, BaseEntityExt::getId));
        List<NodeWrapper> nodes = groupNodes.stream()
                .map(node -> new NodeWrapper(node, nodeCode2Id))
                .collect(toList());
        RootNodeWrapper root = new RootNodeWrapper(TreeBuilder.build(nodes, true));
        if (cache != null)
            cache.setRoot(root);
        return root;
    }

    public List<ValueNode<NodeWrapper>> getTodoGroups(AppTerminalTypeEnum terminals) {
        return getTodoGroups(Collections.singletonList(terminals));
    }

    public List<ValueNode<NodeWrapper>> getTodoGroups(List<AppTerminalTypeEnum> terminals) {
        Set<Long> configuredIds = new HashSet<>(cfg.fetchMessageGroupTreeNodeIds(terminals));
        if (configuredIds.isEmpty())
            return Collections.emptyList();
        RootNodeWrapper root = getGroupRoot();
        return configuredIds.stream()
                .map(root::findValueNode)
                .map(nodeOpt -> nodeOpt.orElse(null))
                .filter(Objects::nonNull)
                .collect(toList());
    }

    public List<ValueNode<NodeWrapper>> collectLeafNodes(List<ValueNode<NodeWrapper>> nodes) {
        List<ValueNode<NodeWrapper>> leafNodes = new ArrayList<>();
        for (ValueNode<NodeWrapper> configuredNode : nodes) {
            configuredNode.walkDown(node -> {
                //noinspection unchecked
                ValueNode<NodeWrapper> valueNode = node.tryCast(ValueNode.class);
                if (valueNode != null && valueNode.getValue().isLeaf())
                    leafNodes.add(valueNode);
                return true;
            });
        }
        leafNodes.sort(NodeComparator.INSTANCE);
        return leafNodes;
    }

    // !! templates

    public List<String> collectTemplateCodes(List<ValueNode<NodeWrapper>> nodes) {
        return collectTemplates(nodes).getTemplateCodes();
    }

    public List<String> collectTemplateCodes(String groupNodeCode) {
        return collectTemplates(groupNodeCode).getTemplateCodes();
    }

    public List<String> collectTemplateCodes(Collection<String> groupNodeCodes) {
        return collectTemplates(groupNodeCodes).getTemplateCodes();
    }

    public GroupTemplates collectTemplates(List<ValueNode<NodeWrapper>> nodes) {
        Set<String> groupNodeCodes = nodes.stream()
                .map(ValueNode::getValue)
                .map(NodeWrapper::getNodeCode)
                .collect(toSet());
        return collectTemplates(groupNodeCodes);
    }

    /**
     * 通过分组结点编码路径查询关联的模板编码列表, 包含子节点
     */
    public GroupTemplates collectTemplates(String groupNodeCode) {
        return collectTemplates(Collections.singletonList(groupNodeCode));
    }

    /**
     * 通过分组结点编码路径查询关联的模板编码列表, 包含子节点
     */
    public GroupTemplates collectTemplates(Collection<String> groupNodeCodes) {
        RootNodeWrapper root = getGroupRoot();
        if (CollectionUtils.isEmpty(groupNodeCodes))
            return new GroupTemplates(root);
        TemplateCache cache = TemplateCache.get();
        if (cache != null && cache.getGroupTemplates(groupNodeCodes) != null)
            return cache.getGroupTemplates(groupNodeCodes);
        List<ValueNode<NodeWrapper>> nodes = root.getNodes(groupNodeCodes);
        if (CollectionUtils.isEmpty(nodes))
            return new GroupTemplates(root);
        Set<String> paths = nodes.stream()
                .map(root::getPath)
                .collect(toSet());
        List<MessageTemplateGroup> groups = messageTemplateGroupDao.lambdaQuery()
                .eq(MessageTemplateGroup::getIsDelete, 0)
                .and(nested -> {
                    for (String path : paths)
                        nested.or().likeRight(MessageTemplateGroup::getPath, path);
                })
                .list();
        GroupTemplates groupTemplates = new GroupTemplates(root, groups);
        if (cache != null)
            cache.putGroupTemplates(groupNodeCodes, groupTemplates);
        return groupTemplates;
    }

    public TemplateGroups getTemplateGroups(Collection<String> templateCodes) {
        if (CollectionUtils.isEmpty(templateCodes))
            return new TemplateGroups();
        List<MessageTemplateGroup> groups = messageTemplateGroupDao.lambdaQuery()
                .eq(MessageTemplateGroup::getIsDelete, 0)
                .in(MessageTemplateGroup::getTemplateCode, templateCodes)
                .list();
        if (CollectionUtils.isEmpty(groups))
            return new TemplateGroups();
        ArrayList<String> nodeCodes = new ArrayList<>();
        for (MessageTemplateGroup group : groups)
            nodeCodes.addAll(group.getPathSegments());
        List<MessageGroupNode> nodes = messageGroupNodeDao.lambdaQuery()
                .eq(MessageGroupNode::getIsDelete, 0)
                .in(MessageGroupNode::getCode, nodeCodes)
                .list();
        return new TemplateGroups(groups, nodes);
    }

}