package com.yfd.platform.modules.specialDocument.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.modules.experimentalData.domain.TsFiles;
import com.yfd.platform.modules.experimentalData.domain.TsNodes;
import com.yfd.platform.modules.specialDocument.domain.Files;
import com.yfd.platform.modules.specialDocument.domain.Nodes;
import com.yfd.platform.modules.specialDocument.mapper.FilesMapper;
import com.yfd.platform.modules.specialDocument.mapper.NodesMapper;
import com.yfd.platform.modules.specialDocument.mapper.ProjectMapper;
import com.yfd.platform.modules.specialDocument.service.INodesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.modules.storage.context.StorageSourceContext;
import com.yfd.platform.modules.storage.controller.file.FileOperatorController;
import com.yfd.platform.modules.storage.model.enums.FileTypeEnum;
import com.yfd.platform.modules.storage.model.request.BatchDeleteRequest;
import com.yfd.platform.modules.storage.model.request.NewFolderRequest;
import com.yfd.platform.modules.storage.model.request.RenameFolderRequest;
import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService;
import com.yfd.platform.system.domain.LoginUser;
import com.yfd.platform.system.domain.SysDictionaryItems;
import com.yfd.platform.utils.StringUtils;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>
 * 专项文档节点表 服务实现类
 * </p>
 *
 * @author LiMengNan
 * @since 2025-01-20
 */
@Service
public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements INodesService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);


    //专项文档节点表 Mapper
    @Resource
    private NodesMapper nodesMapper;

    //专项文档表 Mapper
    @Resource
    private FilesMapper filesMapper;

    @Resource
    private FileOperatorController fileOperatorController;

    @Resource
    private StorageSourceContext storageSourceContext;

    /***********************************
     * 用途说明：获取专项文档节点 树形结构
     * 参数说明
     *  nodeName        节点名称
     *  projectId        所属项目ID
     * 返回值说明:  专项文档节点树形结构
     ***********************************/
    @Override
    public List<Map<String, Object>> getNodesTree(String nodeName, String projectId) {
        // 查询所有节点数据
        List<Map<String, Object>> allNodes = getAllNodes(projectId);
        // 查找所有根节点（parentId为"00"的节点）
        List<Map<String, Object>> rootNodes = findRootNodes(allNodes, projectId);
        // 如果未找到根节点，返回空列表
        if (rootNodes.isEmpty()) {
            return new ArrayList<>();
        }

        // 存储最终结果
        List<Map<String, Object>> result = new ArrayList<>();
        // 如果 nodeName 为空，返回所有根节点的完整树形结构
        if (StringUtils.isEmpty(nodeName)) {
            for (Map<String, Object> rootNode : rootNodes) {
                result.addAll(buildFullTree(rootNode, allNodes));
            }
            return result;
        }
        // 否则，返回从根节点到目标节点的树形结构
        for (Map<String, Object> rootNode : rootNodes) {
            List<Map<String, Object>> tree = buildTreeToTargetNode(rootNode, allNodes, nodeName);
            if (!tree.isEmpty()) {
                result.addAll(tree);
            }
        }
        // 返回结果
        return result;
    }

    /**
     * 构建完整的树形结构
     *
     * @param currentNode 当前节点
     * @param allNodes    所有节点数据
     * @return 返回当前节点的子树
     */
    private List<Map<String, Object>> buildFullTree(Map<String, Object> currentNode, List<Map<String, Object>> allNodes) {
        // 查找当前节点的所有子节点
        List<Map<String, Object>> children = findChildren(allNodes, currentNode.get("id").toString());
        // 递归构建子树
        List<Map<String, Object>> tree = new ArrayList<>();
        for (Map<String, Object> child : children) {
            List<Map<String, Object>> childTree = buildFullTree(child, allNodes);
            if (!childTree.isEmpty()) {
                tree.addAll(childTree);
            }
        }
        // 将当前节点加入树中
        Map<String, Object> nodeWithChildren = new HashMap<>(currentNode);
        if (!tree.isEmpty()) {
            nodeWithChildren.put("children", tree);
        }
        return Collections.singletonList(nodeWithChildren);
    }

    /**
     * 构建从根节点到目标节点的树形结构
     *
     * @param currentNode 当前节点
     * @param allNodes    所有节点数据
     * @param nodeName    目标节点名称
     * @return 返回从根节点到目标节点的树形结构
     */
    private List<Map<String, Object>> buildTreeToTargetNode(Map<String, Object> currentNode, List<Map<String, Object>> allNodes, String nodeName) {

        List<Map<String, Object>> result = new ArrayList<>();

        // 查找当前节点的所有子节点
        List<Map<String, Object>> children = findChildren(allNodes, currentNode.get("id").toString());

        // 递归查找目标节点
        for (Map<String, Object> child : children) {
            List<Map<String, Object>> childTree = buildTreeToTargetNode(child, allNodes, nodeName);
            if (!childTree.isEmpty()) {
                // 如果找到目标节点，将当前节点加入树中，并将其作为子节点
                Map<String, Object> nodeWithChildren = new HashMap<>(currentNode);
                nodeWithChildren.put("children", childTree);
                result.add(nodeWithChildren);  // 将当前节点加入结果列表
            }
        }

        // 如果当前节点符合条件且没有被添加到result，则将其添加
        if (currentNode.get("nodeName") instanceof String && ((String) currentNode.get("nodeName")).contains(nodeName) && result.isEmpty()) {
            result.add(currentNode);  // 将当前节点添加到结果列表
        }

        // 返回包含所有符合条件的树结构的列表
        return result;

//        // 如果当前节点是目标节点，返回当前节点
//        if (currentNode.get("nodeName") instanceof String && ((String) currentNode.get("nodeName")).contains(nodeName)) {
//            return Collections.singletonList(currentNode);
//        }
////        if (nodeName.equals(currentNode.get("nodeName"))) {
////            return Collections.singletonList(currentNode);
////        }
//        // 查找当前节点的所有子节点
//        List<Map<String, Object>> children = findChildren(allNodes, currentNode.get("id").toString());
//        // 递归查找目标节点
//        for (Map<String, Object> child : children) {
//            List<Map<String, Object>> childTree = buildTreeToTargetNode(child, allNodes, nodeName);
//            if (!childTree.isEmpty()) {
//                // 如果找到目标节点，将当前节点加入树中
//                Map<String, Object> nodeWithChildren = new HashMap<>(currentNode);
//                nodeWithChildren.put("children", childTree);
//                return Collections.singletonList(nodeWithChildren);
//            }
//        }
//        // 如果未找到目标节点，返回空列表
//        return new ArrayList<>();
    }

    /**
     * 查找当前节点的所有子节点
     *
     * @param allNodes 所有节点数据
     * @param parentId 父节点ID
     * @return 返回所有子节点列表
     */
    private List<Map<String, Object>> findChildren(List<Map<String, Object>> allNodes, String parentId) {
        List<Map<String, Object>> children = new ArrayList<>();
        for (Map<String, Object> node : allNodes) {
            if (parentId.equals(node.get("parentId").toString())) {
                children.add(node);
            }
        }
        return children;
    }

    /**
     * 查找所有根节点（parentId为"00"的节点）
     *
     * @param allNodes  所有节点数据
     * @param projectId 项目ID
     * @return 返回所有根节点列表
     */
    private List<Map<String, Object>> findRootNodes(List<Map<String, Object>> allNodes, String projectId) {
        List<Map<String, Object>> rootNodes = new ArrayList<>();
        for (Map<String, Object> node : allNodes) {
            if ("00".equals(node.get("parentId").toString()) && projectId.equals(node.get("projectId").toString())) {
                rootNodes.add(node);
            }
        }
        return rootNodes;
    }

    /**
     * 查询所有节点数据
     *
     * @param projectId 项目ID（用于精确查询）
     * @return 返回所有节点列表
     */
    private List<Map<String, Object>> getAllNodes(String projectId) {
        // 创建查询条件
        QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(
                "id as id",                      // 节点ID
                "node_name as nodeName",         // 节点名称
                "project_id as projectId",       // 项目ID
                "parent_id as parentId",         // 父节点ID
                "node_order as nodeOrder",       // 节点顺序
                "node_type as nodeType",         // 节点类型
                "create_time as createTime",     // 创建时间
                "creator"                        // 创建人
        );
        // 如果项目ID不为空，添加精确查询条件
        if (StringUtils.isNotEmpty(projectId)) {
            queryWrapper.eq("project_id", projectId);
        }
        // 按节点顺序升序排序
        queryWrapper.orderByAsc("node_order");
        // 查询所有符合条件的节点
        return nodesMapper.selectMaps(queryWrapper);
    }


    /**********************************
     * 用途说明: 增加专项文档节点
     * 参数说明  nodes 专项文档节点信息
     * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败
     ***********************************/
    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务注解，遇到异常时回滚
    public ResponseResult addNodes(Nodes nodes) {

        // 校验文件名是否包含非法字符
        String nodeName = nodes.getNodeName();
        if (containsInvalidCharacters(nodeName)) {
            return ResponseResult.error("文件名包含非法字符（<>:\"/\\|?*）！");
        }

        //获取当前登录用户
        UsernamePasswordAuthenticationToken authentication =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginuser = (LoginUser) authentication.getPrincipal();
        //创建人是当前登录人
        nodes.setCreator(loginuser.getUsername());

        //当前操作时间
        LocalDateTime now = LocalDateTime.now();
        // 转换为 Timestamp
        Timestamp currentTime = Timestamp.valueOf(now);
        nodes.setCreateTime(currentTime);

        //通过获取上级节点的条数 设置节点顺序
        QueryWrapper<Nodes> queryWrapperNodeOrder = new QueryWrapper<>();
        int orderno = this.count(queryWrapperNodeOrder.eq("parent_id", nodes.getParentId())) + 1;
        //判断节点名称是否存在
        QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("node_name", nodes.getNodeName());//名称
        queryWrapper.eq("parent_id", nodes.getParentId());//父节点
        int count = nodesMapper.selectCount(queryWrapper);
        // 大于0说明 区域名称重复
        if (count > 0) {
            return ResponseResult.error("节点名称已存在！");
        }
        //序号
        nodes.setNodeOrder(orderno);
        int valueAdded = nodesMapper.insert(nodes);
        if (valueAdded == 1) {
            List<String> pathNodes = new ArrayList<>();
            Nodes nodesData = nodesMapper.selectById(nodes.getParentId());
            // 从当前节点向上遍历，直到根节点
            while (nodesData != null) {
                pathNodes.add(nodesData.getNodeName());
                // 如果父节点是 "00"，说明已经到了根节点，停止遍历
                if ("00".equals(nodesData.getParentId())) {
                    break;
                }
                // 获取父节点
                nodesData = nodesMapper.selectById(nodesData.getParentId()); // 修正：从 nodesData 中获取 parentId
            }
            // 反转路径，使其从根节点到当前节点
            Collections.reverse(pathNodes);
            String path = "/" + String.join("/", pathNodes);

            //新增节点的时候 创建文件夹
            NewFolderRequest newFolderRequest = new NewFolderRequest();
            newFolderRequest.setName(nodes.getNodeName());//新建的文件夹名称,示例值(/a/b/c)
            newFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问，则支持请求密码,示例值(123456)
            newFolderRequest.setPath(path);//请求路径,示例值(/)
            newFolderRequest.setStorageKey("minio");//存储源 key,示例值(local minio)
            AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(newFolderRequest.getStorageKey());
            boolean flag = fileService.newFolder(newFolderRequest.getPath(), newFolderRequest.getName());
            if (flag) {
                return ResponseResult.success();
            } else {
                LOGGER.error("节点新增成功，但是minio创建文件失败");
                return ResponseResult.error();
            }

        } else {
            LOGGER.error("节点新增失败");
            return ResponseResult.error();
        }
    }

    /**********************************
     * 用途说明: 修改专项文档节点
     * 参数说明  nodes 专项文档节点信息
     * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败
     ***********************************/
    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务注解，遇到异常时回滚
    public ResponseResult updateNodes(Nodes nodes) {

        //新的节点名称
        String nodeName = nodes.getNodeName();
        if (containsInvalidCharacters(nodeName)) {
            return ResponseResult.error("文件名包含非法字符（<>:\"/\\|?*）！");
        }

        //查询没改之前的节点名称
        Nodes nodesold = nodesMapper.selectById(nodes.getId());
        //老的节点名称
        String nodeNameOld = null;
        if (ObjUtil.isNotEmpty(nodesold)) {
            nodeNameOld = nodesold.getNodeName();
        }

        //获取当前登录用户
        UsernamePasswordAuthenticationToken authentication =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginuser = (LoginUser) authentication.getPrincipal();
        //创建人是当前登录人
        nodes.setCreator(loginuser.getUsername());

        //判断节点名称是否存在
        QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("node_name", nodes.getNodeName());
        queryWrapper.eq("parent_id", nodes.getParentId());//父节点
        int count = nodesMapper.selectCount(queryWrapper);
        // 大于0说明 区域名称重复
        if (count > 0) {
            return ResponseResult.error("节点名称已存在！");
        }
        int valueUpdate = nodesMapper.updateById(nodes);

        // 4. 递归获取所有子节点ID（包含当前节点）
        List<String> affectedNodeIds = getAllChildNodeIds(nodes.getId());

        // 5. 更新相关文件路径
        updateFilePaths(affectedNodeIds, nodes.getProjectId(), nodeNameOld, nodeName);

        if (valueUpdate == 1) {
            List<String> pathNodes = new ArrayList<>();
            Nodes nodesData = nodesMapper.selectById(nodes.getParentId());
            // 从当前节点向上遍历，直到根节点
            while (nodesData != null) {
                pathNodes.add(nodesData.getNodeName());
                // 如果父节点是 "00"，说明已经到了根节点，停止遍历
                if ("00".equals(nodesData.getParentId())) {
                    break;
                }
                // 获取父节点
                nodesData = nodesMapper.selectById(nodesData.getParentId()); // 修正：从 nodesData 中获取 parentId
            }
            // 反转路径，使其从根节点到当前节点
            Collections.reverse(pathNodes);
            String path = String.join("/", pathNodes);

            //修改文件名称
            RenameFolderRequest renameFolderRequest = new RenameFolderRequest();
            renameFolderRequest.setName(nodeNameOld);//重命名的原文件夹名称,示例值(movie)
            renameFolderRequest.setNewName(nodeName);//	重命名后的文件名称,示例值(music)
            renameFolderRequest.setPassword("");//文件夹密码, 如果文件夹需要密码才能访问，则支持请求密码,示例值(123456)
            renameFolderRequest.setPath(path);//请求路径,示例值(/)
            renameFolderRequest.setStorageKey("minio");//存储源 key,示例值(local minio)
            AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(renameFolderRequest.getStorageKey());
            boolean flag = fileService.renameFolder(renameFolderRequest.getPath(), renameFolderRequest.getName(), renameFolderRequest.getNewName());
            if (flag) {
                return ResponseResult.success("重命名成功");
            } else {
                LOGGER.error("节点修改成功，但是minio修改文件名失败");
                return ResponseResult.error("重命名失败");
            }
        } else {
            LOGGER.error("节点修改失败");
            return ResponseResult.error();
        }
    }

    // 校验文件名是否包含非法字符
    private boolean containsInvalidCharacters(String nodeName) {
        // 定义非法字符的正则表达式
        String regex = "[<>:\"/\\\\|?*]";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(nodeName);
        return matcher.find();
    }

    /**
     * 更新文件路径中的节点名称
     */
    private void updateFilePaths(List<String> nodeIds, String projectId, String oldName, String newName) {
        // 查询需要更新的文件记录
        List<Files> filesToUpdate = filesMapper.selectList(
                new QueryWrapper<Files>()
                        .in("node_id", nodeIds)
                        .eq("project_id", projectId)
        );

        // 构建路径替换规则
        String pathPattern = "/" + oldName;  // 正确转义斜杠
        String replacement = "/" + newName;  // 保留斜杠或结尾不变

        // 批量更新文件路径
        filesToUpdate.forEach(file -> {
            String newPath = file.getFilePath().replace(pathPattern, replacement);
            file.setFilePath(newPath);
        });
        // 批量更新数据库
        if (!filesToUpdate.isEmpty()) {
            for (Files files : filesToUpdate) {
                filesMapper.updateById(files);
            }
        }
    }


    /**
     * 递归获取所有子节点ID（包含当前节点）
     */
    private List<String> getAllChildNodeIds(String nodeId) {
        List<String> nodeIds = new ArrayList<>();
        nodeIds.add(nodeId);

        // 查询直接子节点
        List<Nodes> directChildren = nodesMapper.selectList(
                new QueryWrapper<Nodes>().eq("parent_id", nodeId));

        // 递归获取子节点的子节点
        for (Nodes child : directChildren) {
            nodeIds.addAll(getAllChildNodeIds(child.getId()));
        }

        return nodeIds;
    }


    /**********************************
     * 用途说明: 根据ID删除专项文档节点
     * 参数说明  id 专项文档节点ID
     * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
     ***********************************/
    @Override
    @Transactional(rollbackFor = Exception.class) // 启用事务
    public boolean deleteNodesById(String id) {
        Boolean value = false;

        // 根据ID 查询当前数据
        Nodes nodes = nodesMapper.selectById(id);
        if (nodes == null) {
            return false; // 节点不存在
        }

        // 构建节点路径
        List<String> pathNodes = new ArrayList<>();
        Nodes nodesData = nodesMapper.selectById(nodes.getParentId());
        while (nodesData != null) {
            pathNodes.add(nodesData.getNodeName());
            if ("00".equals(nodesData.getParentId())) {
                break;
            }
            nodesData = nodesMapper.selectById(nodesData.getParentId());
        }
        Collections.reverse(pathNodes);
        String path = String.join("/", pathNodes);

        // 删除 MinIO 中的文件夹
        List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
        BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
        deleteItemData.setName(nodes.getNodeName());
        deleteItemData.setPassword("");
        deleteItemData.setPath(path);
        deleteItemData.setType(FileTypeEnum.FOLDER);
        deleteItemList.add(deleteItemData);

        BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
        batchDeleteRequest.setDeleteItems(deleteItemList);
        batchDeleteRequest.setStorageKey("minio");
        AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());

        int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItemList);
        for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) {
            boolean flag = false;
            try {
                if (deleteItem.getType() == FileTypeEnum.FILE) {
                    flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
                } else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
                    flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
                }

                if (flag) {
                    deleteSuccessCount++;
                } else {
                    deleteFailCount++;
                }
            } catch (Exception e) {
                LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
                deleteFailCount++;
            }
        }

        if (deleteSuccessCount >= 1) {
            // 递归删除子节点
            deleteChildren(nodes.getId(), nodes.getProjectId());

            // 删除当前节点的文件
            filesMapper.delete(
                    new QueryWrapper<Files>()
                            .eq("node_id", nodes.getId())
                            .eq("project_id", nodes.getProjectId())
            );

            // 删除当前节点
            int deleteCount = nodesMapper.deleteById(id);
            value = deleteCount > 0;
        } else {
            value = false;
        }

        return value;
    }

    /**
     * 递归删除子节点
     */
    private void deleteChildren(String parentId, String projectId) {
        // 查询当前节点的所有子节点
        QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id", parentId);
        queryWrapper.eq("project_id", projectId);
        List<Nodes> children = nodesMapper.selectList(queryWrapper);

        // 递归删除每个子节点
        for (Nodes child : children) {
            // 删除子节点下的文件
            QueryWrapper<Files> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.eq("node_id", child.getId()); // 使用子节点的 ID
            queryWrapper1.eq("project_id", projectId);
            filesMapper.delete(queryWrapper1);

            // 递归删除子节点的子节点
            deleteChildren(child.getId(), projectId);

            // 删除当前子节点
            nodesMapper.deleteById(child.getId());
        }
    }
//
//
//
//        // 使用 QueryWrapper 查询当前节点的所有子节点
//        QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("parent_id", parentId); // parent_id = #{parentId}
//        List<Nodes> children = nodesMapper.selectList(queryWrapper);
//
//        // 递归删除每个子节点
//        for (Nodes child : children) {
//            deleteChildren(child.getId()); // 递归删除子节点的子节点
//            nodesMapper.deleteById(child.getId()); // 删除当前子节点
//        }


}