package com.keyware.shandan.bianmu.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.keyware.shandan.bianmu.entity.DirectoryResource;
import com.keyware.shandan.bianmu.entity.DirectoryVo;
import com.keyware.shandan.bianmu.enums.DirectoryType;
import com.keyware.shandan.bianmu.enums.ReviewStatus;
import com.keyware.shandan.bianmu.mapper.DirectoryResourceMapper;
import com.keyware.shandan.bianmu.utils.DirectoryUtil;
import com.keyware.shandan.common.constants.DirConstant;
import com.keyware.shandan.common.entity.TreeVo;
import com.keyware.shandan.common.util.StreamUtil;
import com.keyware.shandan.common.util.StringUtils;
import com.keyware.shandan.common.util.TreeUtil;
import com.keyware.shandan.frame.config.security.SecurityUtil;
import com.keyware.shandan.system.entity.SysUser;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Service
public class DirectoryTreeService {
    private final DirectoryService directoryService;
    private final DirPermissionService dirPermissionService;
    private final DirectoryResourceMapper directoryResourceMapper;


    public DirectoryTreeService(DirectoryService directoryService, DirPermissionService dirPermissionService, DirectoryResourceMapper directoryResourceMapper) {
        this.directoryService = directoryService;
        this.dirPermissionService = dirPermissionService;
        this.directoryResourceMapper = directoryResourceMapper;
    }

    /**
     * 获取目录树
     *
     * @param parentId     父级目录ID
     * @param reviewStatus 审核状态
     * @param hasMetadata  是否包含数据资源
     * @param permis       权限类型
     * @return 目录树
     */
    public List<TreeVo> tree(String parentId, String reviewStatus, boolean hasMetadata, String permis, boolean allChild, int busType) {
        final String pid = StringUtils.hasText(parentId) ? parentId : "";
        DirectoryVo parentDir = null;
        DirectoryVo linkSourceDir = null;
        if (StringUtils.hasText(pid)) {
            parentDir = directoryService.getById(pid);
            if (parentDir != null && parentDir.getDirectoryType() == DirectoryType.LINK_DIR) {
                linkSourceDir = directoryService.getById(parentDir.getResourceId());
            }
        }
        // 获取子级节点
        List<DirectoryVo> directoryList = getDirectoryList(linkSourceDir == null ? parentDir : linkSourceDir, allChild, hasMetadata, busType);
        // 审核状态
        ReviewStatus status = StringUtils.hasText(reviewStatus) ? ReviewStatus.valueOf(reviewStatus) : null;
        // 当前用户
        SysUser user = SecurityUtil.getLoginSysUser();
        // 根据权限类型获取权限范围内的数据的ID集合
        Set<String> permisDis = "write".equals(permis) ? dirPermissionService.getByWritePermis(user) : dirPermissionService.getByReadPermis(user);
        // 判断权限范围
        List<DirectoryVo> permisDirectoryList = StreamUtil.as(directoryList).filter(dir -> {
            if (dir.getDirectoryType() == DirectoryType.FILE
                    || dir.getDirectoryType() == DirectoryType.LINK_FILE
                    || dir.getDirectoryType() == DirectoryType.METADATA
                    || dir.getDirectoryType() == DirectoryType.LINK_METADATA) {
                return true;
            }

            if (dir.getReviewStatus() == status || status == null) {
                boolean permisAllow = permisDis.contains(dir.getId());
                if (permisAllow) {
                    return true;
                }
                // 如果权限不允许，则需要判断当前节点的所有子级节点是否有允许的目录，如果有则需要返回到前端的树组件中
                Set<String> childrenIds = getDirectoryList(dir, true, false, busType)
                        .stream()
                        .map(DirectoryVo::getId)
                        .collect(Collectors.toSet());
                for (String id : childrenIds) {
                    if (permisDis.contains(id)) {
                        return true;
                    }
                }
                return false;
            }
            return false;
        }).toList();
        DirectoryParentBuilder dpb = new DirectoryParentBuilder(directoryList, permisDirectoryList);

        // 转换为Dtree对象
        List<TreeVo> treeVoList = StreamUtil.as(dpb.build().get()).map(DirectoryUtil::dir2Tree).toList();

        boolean isLink = linkSourceDir != null;
        String nodeParentId = isLink ? linkSourceDir.getId() : pid,
                replacePath = isLink ? linkSourceDir.getDirectoryPath() : null,
                sourcePath = isLink ? parentDir.getDirectoryPath() : null;
        // 构建树形结构
        treeVoList = StreamUtil.as(TreeUtil.buildDirTree(treeVoList, nodeParentId))
                // 过滤根节点的垃圾数据
                .filter(dir -> (dir.getParentId().equals(nodeParentId)))
                .peek(dir -> {
                    if (isLink) {
                        String path = dir.getPath();
                        dir.setPath(path.replace(replacePath, sourcePath));
                    }
                })
                .toList();

        return treeVoList.stream().filter(tree -> !tree.getId().equals("ROOT_THEME")).collect(Collectors.toList());
    }

    /**
     * 获取目录的列表
     *
     * @param parentDir   父级目录
     * @param allChild    是否查询所有下级
     * @param hasResource 是否包含数据资源
     * @return 目录集合
     */
    private List<DirectoryVo> getDirectoryList(DirectoryVo parentDir, boolean allChild, boolean hasResource, int busType) {
        QueryWrapper<DirectoryVo> wrapper = new QueryWrapper<>();
        if (parentDir == null) {
            switch (busType) {
                case 2:
                    wrapper.in("ID", "ROOT", "ROOT_BASIC_DATA");
                    break;
                case 1:
                    wrapper.eq("ID", "ROOT_BASIC_DATA");
                    break;
                case 0:
                default:
                    wrapper.eq("ID", "ROOT");
            }
        } else {
            wrapper.in(!hasResource, "DIRECTORY_TYPE", DirectoryType.DIRECTORY, DirectoryType.LINK_DIR).eq("IS_BASIC_DIR", (busType == 1 || busType == 2));

            if (allChild) {
                wrapper.likeRight("DIRECTORY_PATH", parentDir.getDirectoryPath() + "/");
            } else {
                wrapper.eq("PARENT_ID", parentDir.getId());
            }
        }

        return directoryService.list(wrapper);
    }

    public Page<DirectoryResource> resourcePage(Page<DirectoryResource> page, String parentId, String text, boolean all,String reviewStatus) {
        DirectoryVo parent = directoryService.getById(parentId);
        HashSet<DirectoryVo> allDir = new HashSet<>();
        if (all) {
            allDirectoryByParent(parent, allDir);
        } else {
            allDir.addAll(directoryService.childrenListByParent(parentId));
        }
        Set<String> allowDirIds = dirPermissionService.getByReadPermis(SecurityUtil.getLoginSysUser());
        Set<String> dirIds = allDir.stream().map(DirectoryVo::getId).filter(allowDirIds::contains).collect(Collectors.toSet());

        QueryWrapper<DirectoryResource> query = new QueryWrapper<>();
        query.in(dirIds.size()>0,"PARENT_ID", dirIds);
        query.eq(StringUtils.hasText(reviewStatus),"REVIEW_STATUS", reviewStatus);
        if (StringUtils.hasText(text)) {
            query.and(queryWrapper -> {
                queryWrapper.
                        like("RESOURCE_NAME", text)
                        .or()
                        .like("RESOURCE_COMMENT", text);
            });
        }
        return directoryResourceMapper.selectPage(page, query);
    }

    private void allDirectoryByParent(DirectoryVo parent, HashSet<DirectoryVo> result) {
        if (DirectoryType.LINK_DIR == parent.getDirectoryType()) {
            DirectoryVo linkDir = directoryService.getById(parent.getResourceId());
            if (linkDir != null) {
                result.add(linkDir);
            }
        } else {
            result.add(parent);
        }
        // 查询普通目录数据
        result.addAll(dirListByParent(parent));

        // 查询软连接目录数据
        LambdaQueryWrapper<DirectoryVo> query = new LambdaQueryWrapper<>();
        query.eq(DirectoryVo::getDirectoryType, DirectoryType.LINK_DIR)
                .likeRight(DirectoryVo::getDirectoryPath, parent.getDirectoryPath() + "/");
        directoryService.list(query).forEach(dir -> result.addAll(dirListByParent(dir)));

    }

    private List<DirectoryVo> dirListByParent(DirectoryVo parent) {
        LambdaQueryWrapper<DirectoryVo> query = new LambdaQueryWrapper<>();
        query.eq(DirectoryVo::getDirectoryType, DirectoryType.DIRECTORY)
                .likeRight(DirectoryVo::getDirectoryPath, parent.getDirectoryPath() + "/");
        return directoryService.list(query);
    }

    private static class DirectoryParentBuilder {
        private final Map<String, DirectoryVo> allDirMap = new HashMap<>();
        private final Map<String, DirectoryVo> targetMap = new HashMap<>();

        private final ArrayBlockingQueue<DirectoryVo> queue;

        public DirectoryParentBuilder(List<DirectoryVo> allDirList, @NonNull List<DirectoryVo> list) {
            allDirList.forEach(dir -> allDirMap.put(dir.getId(), dir));
            list.forEach(item -> targetMap.put(item.getId(), item));
            queue = new ArrayBlockingQueue<>(Math.max(allDirList.size(), 1));
            queue.addAll(this.targetMap.values());
        }

        public DirectoryParentBuilder build() {
            while (queue.size() > 0) {
                try {
                    DirectoryVo dir = queue.take();
                    DirectoryVo parent = targetMap.get(dir.getParentId());
                    if (parent == null) {
                        parent = allDirMap.get(dir.getParentId());
                        if (parent != null) {
                            queue.put(parent);
                            targetMap.put(parent.getId(), parent);
                        }
                    }
                } catch (InterruptedException ignored) {
                }
            }
            // 主题目录
            DirectoryVo themeRoot = allDirMap.get(DirConstant.DIR_THEME_ROOT_ID);
            if (targetMap.get(DirConstant.DIR_THEME_ROOT_ID) == null && themeRoot != null) {
                targetMap.put(themeRoot.getId(), themeRoot);
            }
            return this;
        }

        public List<DirectoryVo> get() {
            return StreamUtil.as(targetMap.values()).sorted(Comparator.comparing(DirectoryVo::getCreateTime)).toList();
        }
    }
}
