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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yfd.platform.component.TaskStatusHolder;
import com.yfd.platform.component.WebSocketServer;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.exception.file.InvalidStorageSourceException;
import com.yfd.platform.modules.config.model.request.FileListRequest;
import com.yfd.platform.modules.experimentalData.domain.TsFiles;
import com.yfd.platform.modules.experimentalData.domain.TsNodes;
import com.yfd.platform.modules.experimentalData.domain.TsTask;
import com.yfd.platform.modules.experimentalData.mapper.TsFilesMapper;
import com.yfd.platform.modules.experimentalData.mapper.TsNodesMapper;
import com.yfd.platform.modules.experimentalData.mapper.TsTaskMapper;
import com.yfd.platform.modules.experimentalData.service.ITsFilesService;
import com.yfd.platform.modules.experimentalData.service.ITsNodesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.modules.specialDocument.domain.Files;
import com.yfd.platform.modules.specialDocument.domain.Nodes;
import com.yfd.platform.modules.specialDocument.domain.Project;
import com.yfd.platform.modules.storage.context.StorageSourceContext;
import com.yfd.platform.modules.storage.mapper.StorageSourceConfigMapper;
import com.yfd.platform.modules.storage.model.entity.StorageSourceConfig;
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.result.FileItemResult;
import com.yfd.platform.modules.storage.service.StorageSourceService;
import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService;
import com.yfd.platform.system.domain.LoginUser;
import com.yfd.platform.utils.StringUtils;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.val;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
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.io.File;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 试验任务节点表 服务实现类
 * </p>
 *
 * @author LiMengNan
 * @since 2025-01-21
 */
@Service
public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> implements ITsNodesService {

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

    //试验任务节点表 Mapper
    @Resource
    private TsNodesMapper tsNodesMapper;

    //试验任务文档表 Mapper
    @Resource
    private TsFilesMapper tsFilesMapper;

    //实验任务文档表
    @Resource
    private ITsFilesService tsFilesService;

    @Resource
    private StorageSourceContext storageSourceContext;

    @Autowired
    private TaskStatusHolder taskStatusHolder;

    //试验任务Mapper
    @Resource
    private TsTaskMapper tsTaskMapper;

    @Resource
    private StorageSourceService storageSourceService;

    @Resource
    private StorageSourceConfigMapper storageSourceConfigMapper;


    //顶级父节点  Top level parent node
    public static final String TOP_LEVEL_PARENT_NODE = "00";

    @Override
    public List<Map<String, Object>> getTsNodesTree(String nodeName, String taskId) {
        TsTask tsTask = tsTaskMapper.selectOne(new QueryWrapper<TsTask>().eq("id", taskId));
        // 查询所有节点数据
        List<Map<String, Object>> allNodes = getAllNodes(taskId);

        // 查找所有根节点（parentId为"00"的节点）
        List<Map<String, Object>> rootNodes = findRootNodes(allNodes, taskId);

        // 如果未找到根节点，返回空列表
        if (rootNodes.isEmpty()) {
            return new ArrayList<>();
        }

        // 根节点的基本路径：/项目名称/
        String basePath = "/" + tsTask.getTaskName() + "/";

        // 存储最终结果
        List<Map<String, Object>> result = new ArrayList<>();

        // 如果 nodeName 为空，返回所有根节点的完整树形结构
        if (StringUtils.isEmpty(nodeName)) {
            for (Map<String, Object> rootNode : rootNodes) {
                rootNode.put("path", basePath);
                result.addAll(buildFullTree(rootNode, allNodes));
            }
            return result;
        }

        // 否则，返回从根节点到目标节点的树形结构
        for (Map<String, Object> rootNode : rootNodes) {
            rootNode.put("path", basePath);
            List<Map<String, Object>> tree = buildTreeToTargetNode(rootNode, allNodes, nodeName);
            if (!tree.isEmpty()) {
                result.addAll(tree);
            }
        }

        // 返回结果
        return result;
    }

    /**
     * 查询所有节点数据
     *
     * @param taskId 任务ID（用于精确查询）
     * @return 返回所有节点列表
     */
    private List<Map<String, Object>> getAllNodes(String taskId) {
        // 创建查询条件
        QueryWrapper<TsNodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(
                "node_id as nodeId",              // 节点ID
                "node_name as nodeName",         // 节点名称
                "task_id as taskId",             // 所属任务ID
                "parent_id as parentId",         // 父节点ID
                "node_order as nodeOrder",       // 节点顺序
                "create_time as createTime",     // 创建时间
                "creator"                        // 创建人
        );

        // 如果任务ID不为空，添加精确查询条件
        if (StringUtils.isNotEmpty(taskId)) {
            queryWrapper.eq("task_id", taskId);
        }
        // 按节点顺序升序排序
        queryWrapper.orderByAsc("node_order");

        // 查询所有符合条件的节点
        return tsNodesMapper.selectMaps(queryWrapper);
    }

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

    /**
     * 构建完整的树形结构
     *
     * @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("nodeId").toString());

        // 为每个子节点设置路径：父路径 + 父节点名称 + "/"
        for (Map<String, Object> child : children) {
            String parentPath = (String) currentNode.get("path");
            String parentName = (String) currentNode.get("nodeName");
            child.put("path", parentPath + parentName + "/");
        }

        // 递归构建子树
        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 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;
    }

    /**
     * 构建从根节点到目标节点的树形结构
     *
     * @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("nodeId").toString());

        for (Map<String, Object> child : children) {
            String parentPath = (String) currentNode.get("path");
            String parentName = (String) currentNode.get("nodeName");
            child.put("path", parentPath + parentName + "/");
        }

        // 递归查找目标节点
        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;
    }


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

        //查询文件表 如果有一样的名称不能新增
        List<TsFiles> tsFiles = tsFilesMapper.selectList(new QueryWrapper<TsFiles>()
                .eq("task_id", tsnodes.getTaskId())
                .eq("parent_id", "00")
                .eq("file_name", tsnodes.getNodeName())
                .eq("is_file", "FOLDER"));
        if (tsFiles.size() > 0) {
            return ResponseResult.error("数据管理中存在该文件夹！");
        }
        // 校验文件名是否包含非法字符
        String nodeName = tsnodes.getNodeName();
        if (containsInvalidCharacters(nodeName)) {
            return ResponseResult.error("文件名包含非法字符（<>:\"/\\|?*）！");
        }
        //  差不多的流程  就是提出来  然后判断  如果两个 中有一个是true
        //获取当前登录用户
        UsernamePasswordAuthenticationToken authentication =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginuser = (LoginUser) authentication.getPrincipal();
        //创建人是当前登录人
        tsnodes.setCreator(loginuser.getUsername());

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

        //通过获取上级节点的条数 设置节点顺序
        QueryWrapper<TsNodes> queryWrapperNodeOrder = new QueryWrapper<>();
        int orderno = this.count(queryWrapperNodeOrder.eq("parent_id", tsnodes.getParentId())) + 1;
        //判断节点名称是否存在
        QueryWrapper<TsNodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("node_name", tsnodes.getNodeName());//名称
        queryWrapper.eq("task_id", tsnodes.getTaskId());//所属任务ID
        queryWrapper.eq("parent_id", tsnodes.getParentId());//所属任务ID
        int count = tsNodesMapper.selectCount(queryWrapper);
        // 大于0说明 区域名称重复
        if (count > 0) {
            return ResponseResult.error("节点名称已存在！");
        }
        //序号
        tsnodes.setNodeOrder(orderno);


        int valueAdded = tsNodesMapper.insert(tsnodes);
        if (valueAdded == 1) {
            LOGGER.info("tsnodes表结构增加成功");


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


//            //判断文件夹是否创建
//            AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey("local");
//            boolean flag = fileService.isFolderCreated(File.separator + tsnodes.getNodeId());
//            //如果是false 说明没有创建  那就新建一个文件夹
//            if (!flag) {
//                //本地创建文件夹
//                AbstractBaseFileService<?> fileServiceData = storageSourceContext.getByStorageKey("local");
//                boolean flagData = fileServiceData.newFolder(File.separator, tsnodes.getNodeId());
//                if (!flagData) {
//                    LOGGER.error("创建节点文件夹失败！");
//                    return ResponseResult.error("新增节点失败!");
//                }
//            }
//            return ResponseResult.success();
        } else {
            LOGGER.error("tsnodes表结构增加失败");
            return ResponseResult.error();
        }

    }


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


    /**********************************
     * 用途说明: 修改试验任务节点
     * 参数说明  tsnodes  试验任务节点信息
     * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败
     ***********************************/
    @Override
    public ResponseResult updateTsNodes(TsNodes tsnodes) {

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

        //查询没改之前的节点名称
        TsNodes nodesold = tsNodesMapper.selectById(tsnodes.getNodeId());

        //老的节点名称
        String nodeNameOld = null;
        if (ObjUtil.isNotEmpty(nodesold)) {
            nodeNameOld = nodesold.getNodeName();
        }

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

        //判断节点名称是否存在
        QueryWrapper<TsNodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("node_name", tsnodes.getNodeName());//名称
        queryWrapper.eq("task_id", tsnodes.getTaskId());//所属任务ID
        queryWrapper.eq("parent_id", tsnodes.getParentId());//所属任务ID
        int count = tsNodesMapper.selectCount(queryWrapper);
        // 大于0说明 区域名称重复
        if (count > 0) {
            return ResponseResult.error("节点名称已存在！");
        }
        int valueAdded = tsNodesMapper.updateById(tsnodes);
        if (valueAdded == 1) {
            LOGGER.info("tsnodes表结构修改成功");
            return ResponseResult.success();
        } else {
            LOGGER.error("tsnodes表结构修改失败");
            return ResponseResult.error();
        }
    }

    /**********************************
     * 用途说明: 根据ID删除试验任务节点
     * 参数说明  id 试验任务节点ID
     * 参数说明  path 路径
     * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
     ***********************************/
    @Override
    public boolean deleteTsNodesById(String id, String path) {
        //根据ID 查询当前数据
        TsNodes tsNodes = tsNodesMapper.selectById(id);
        //删除之前  先拼路径  然后删除本地和minio的文件夹 最后删除表结构
        //查询所有的子节点
        List<TsNodes> tsNodesList = selectChildrentsNodes(tsNodes.getNodeId(), tsNodes.getTaskId());
        // 提取所有节点的 ID
        List<String> nodeIds = tsNodesList.stream()
                .map(TsNodes::getNodeId)  // 获取每个节点的 ID
                .collect(Collectors.toList());


        //查询所有的文件
        QueryWrapper<TsFiles> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.in("node_id", nodeIds);
        queryWrapper1.eq("task_id", tsNodes.getTaskId());
        List<TsFiles> tsFiles = tsFilesMapper.selectList(queryWrapper1);
        //获取所有文件的ID集合
        List<String> dataset = new ArrayList<>();
        if (tsFiles != null && !tsFiles.isEmpty()) {
            dataset.addAll(tsFiles.stream()
                    .map(TsFiles::getId)
                    .collect(Collectors.toList()));
        }
        if (dataset.size() <= 0) {
            return true;
        }

        //批量修改
        LambdaUpdateWrapper<TsFiles> updateWrapper1 = new LambdaUpdateWrapper<>();
        updateWrapper1.in(TsFiles::getId, dataset).isNotNull(TsFiles::getBackupPath).ne(TsFiles::getBackupPath, "").set(TsFiles::getWorkPath, "");
        tsFilesMapper.update(null, updateWrapper1);
        //批量删除
        LambdaQueryWrapper<TsFiles> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.in(TsFiles::getId, dataset).isNull(TsFiles::getBackupPath).eq(TsFiles::getBackupPath, "").isNull(TsFiles::getWorkPath).eq(TsFiles::getWorkPath, "");
        tsFilesMapper.delete(deleteWrapper);

//        //批量修改TsFiles表结构
//        if (dataset.size() > 0) {
//            QueryWrapper<TsFiles> updateWrapper = new QueryWrapper<>();
//            updateWrapper.in("id", dataset);
//            // 创建 TsFiles 实体对象，设置要更新的字段
//            TsFiles tsFiles1 = new TsFiles();
//            tsFiles1.setWorkPath(""); // 将 work_path 更新为 ""
//            tsFilesMapper.update(tsFiles1, updateWrapper);
//            LOGGER.info("tsFiles一共删除 {}跳", dataset.size());
//        }
//
////        // 递归删除子节点
//        deleteChildren(tsNodes.getNodeId(), tsNodes.getTaskId());


        //删除当前节点的文件夹  todo 这个地方改动
        // 删除 sdlocal 中的文件夹
        List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
        BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
        deleteItemData.setName(tsNodes.getNodeName());
        deleteItemData.setPassword("");
        deleteItemData.setPath(path);
        deleteItemData.setType(FileTypeEnum.FOLDER);
        deleteItemList.add(deleteItemData);

        BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
        batchDeleteRequest.setDeleteItems(deleteItemList);
        batchDeleteRequest.setStorageKey("local");
        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) {
            // 删除当前节点
            int deleteCount = tsNodesMapper.deleteById(id);
            if (deleteCount == 1) {
                LOGGER.info("tsnodes表结删除改成功");
                return true;
            } else {
                LOGGER.error("tsnodes表结构删除失败");
                return false;
            }
        } else {
            return false;
        }
    }

    private List<TsNodes> selectChildrentsNodes(String parentId, String taskId) {
        List<TsNodes> nodesList = new ArrayList<>();
        TsNodes tsNodes = tsNodesMapper.selectById(parentId);
        // 查询当前节点的所有子节点

        QueryWrapper<TsNodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id", parentId); // parent_id = #{parentId}
        queryWrapper.eq("task_id", taskId); // parent_id = #{parentId}
        List<TsNodes> children = tsNodesMapper.selectList(queryWrapper);
        // 将子节点添加到 nodesList 中
        nodesList.addAll(children);
        nodesList.add(tsNodes);

        // 递归删除每个子节点
        for (TsNodes child : children) {
            // 递归查询子节点的子节点
            List<TsNodes> childNodes = selectChildrentsNodes(child.getNodeId(), taskId);
            // 将子节点的所有子节点添加到 nodesList 中
            nodesList.addAll(childNodes);
        }
        return nodesList;
    }

    /**
     * 递归删除子节点
     *
     * @param parentId 父节点ID
     */
    private void deleteChildren(String parentId, String taskId) {
        // 使用 QueryWrapper 查询当前节点的所有子节点
        QueryWrapper<TsNodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id", parentId); // parent_id = #{parentId}
        queryWrapper.eq("task_id", taskId); // parent_id = #{parentId}
        List<TsNodes> children = tsNodesMapper.selectList(queryWrapper);

        // 递归删除每个子节点
        for (TsNodes child : children) {
            deleteChildren(child.getNodeId(), child.getTaskId()); // 递归删除子节点的子节点
            tsNodesMapper.deleteById(child.getNodeId()); // 删除当前子节点
            //批量文件的数据
            QueryWrapper<TsFiles> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.eq("node_id", child.getNodeId());
            queryWrapper1.eq("task_id", taskId);
            List<TsFiles> tsFiles = tsFilesMapper.selectList(queryWrapper1);

            List<String> dataset = new ArrayList<>();
            if (tsFiles != null && !tsFiles.isEmpty()) {
                dataset = tsFiles.stream()
                        .map(TsFiles::getId)  // 假设 TsFiles 类中有 getId() 方法
                        .collect(Collectors.toList());
            }
            //批量删除TsFiles表数据
            if (dataset.size() > 0) {
                tsFilesService.deleteTsFilesByIds(dataset, "local");
            }
        }
    }


    /**********************************
     * 用途说明: 根据任务ID删除下面所有的节点以及节点下面所有的文件
     * 参数说明  taskId 试验任务ID
     * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
     ***********************************/
    @Override
    public boolean deleteTsNodesByTaskId(String taskId, String path) {
        Boolean value = false;
        //根据任务ID 查询所有的集合  不管层级结构全部都删除
        QueryWrapper<TsNodes> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("task_id", taskId);
        List<TsNodes> tsNodesList = tsNodesMapper.selectList(queryWrapper);
        if (tsNodesList.size() == 0) {
            return true;
        }

        for (TsNodes tsNodes : tsNodesList) {
            //删除文件表
            Boolean deleteTsFiles = tsFilesService.deleteTsFilesByNodeId(tsNodes.getNodeId(), tsNodes.getTaskId());


            //删除当前节点的文件夹  todo 这个地方改动
            // 删除 sdlocal 中的文件夹
            List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
            BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
            deleteItemData.setName(tsNodes.getNodeName());
            deleteItemData.setPassword("");
            deleteItemData.setPath(path);
            deleteItemData.setType(FileTypeEnum.FOLDER);
            deleteItemList.add(deleteItemData);

            BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
            batchDeleteRequest.setDeleteItems(deleteItemList);
            batchDeleteRequest.setStorageKey("local");
            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) {
                // 删除当前节点
                int deleteCount = tsNodesMapper.deleteById(tsNodes.getNodeId());
                if (deleteCount == 1) {
                    LOGGER.info("tsnodes表结删除改成功");
                    value = true;
                } else {
                    LOGGER.error("tsnodes表结构删除失败");
                    value = false;
                }
            } else {
                value = false;
            }
        }
        return value;
    }

    /**********************************
     * 用途说明: 试验数据扫描接口
     * 参数说明  id 试验任务ID
     * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
     ***********************************/
    @Override
    @Async("asyncExecutor")
    public void testDataScanByIdAsync(String id) throws Exception {
        try {
            // 执行扫描并且插入数据库
            this.testDataScanById(id);
        } finally {
            // 生成唯一Key
            String asyncKey = taskStatusHolder.testDatascanKey(id);
            // 无论成功失败都标记完成
            taskStatusHolder.finishTask(asyncKey);
            WebSocketServer.sendMessageTo("试验数据扫描接口完成", "taskId_" + id);
        }
    }

    private String testDataScanById(String id) throws Exception {

        //查询试验任务信息
        TsTask tsTask = tsTaskMapper.selectById(id);
        //文件的第一层是节点      下面的层级是文件夹
        //获取文件列表
        String absolutePath = "/" + tsTask.getTaskName() + "/";
        FileListRequest fileListRequest = buildFileRequest(absolutePath);
        String storageKey = fileListRequest.getStorageKey();
        Integer storageId = storageSourceService.findIdByKey(storageKey);
        if (storageId == null) {
            throw new InvalidStorageSourceException("通过存储源 key 未找到存储源, key: " + storageKey);
        }
        // 处理请求参数默认值
        fileListRequest.handleDefaultValue();
        AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageId(storageId);
        //todo 首先获取两个集合 对比出数据库中没有的文件夹以及文件，递归增加
        List<FileItemResult> fileItemList = fileService.fileList(fileListRequest.getPath());

        if (fileItemList.size() == 0) {
            throw new Exception("该试验任务管理项目目录不存在或没有项目文档，请先建立项目目录和文档。");
        }
        firstLayerData(fileItemList, id);

        return "扫描完成";
    }

    /**
     * 第一层下面应该只有文件夹
     *
     * @param fileItemList 项目下面第一层级的所有数据
     * @param taskId       所属任务ID
     * @throws Exception
     */
    public void firstLayerData(List<FileItemResult> fileItemList, String taskId) throws Exception {

        for (FileItemResult item : fileItemList) {
            //思路就是 如果是文件夹  就查询一下 没有就新增， 新的的时候递归往下走
            if (item.getType() == FileTypeEnum.FOLDER) {
                //先查询有没有 如果没有就新增
                LambdaQueryWrapper<TsNodes> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(TsNodes::getTaskId, taskId);
                queryWrapper.eq(TsNodes::getParentId, TOP_LEVEL_PARENT_NODE);
                queryWrapper.eq(TsNodes::getNodeName, item.getName());
                TsNodes nodeData = tsNodesMapper.selectOne(queryWrapper);
                //如果没有 新增 并且递归
                if (nodeData == null) {
                    TsNodes node = savetsNodes(taskId, TOP_LEVEL_PARENT_NODE, item.getName());
                    otherLevelsData(taskId, node.getNodeId(), item.getName(), item.getPath(), TOP_LEVEL_PARENT_NODE);
                } else {
                    otherLevelsData(taskId, nodeData.getNodeId(), item.getName(), item.getPath(), TOP_LEVEL_PARENT_NODE);
                }

            }
        }
    }

    /**
     * @param taskId   所属项目ID
     * @param nodeId   节点ID
     * @param nodeName 节点名称
     * @param path     路径
     * @param parentId 父级ID
     * @return
     */
    private void otherLevelsData(String taskId, String nodeId, String nodeName, String path, String parentId) throws Exception {


        // 存储所有目录和文件的列表
        List<TsFiles> tsFilesToCreate = new ArrayList<>();

        // 设置当前时间
        LocalDateTime now = LocalDateTime.now();
        // 转换为 Timestamp
        Timestamp currentTime = Timestamp.valueOf(now);
        // 查询本地文件路径根目录（如 E:\yun）
        QueryWrapper<StorageSourceConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", "filePath");
        queryWrapper.eq("type", "local");
        StorageSourceConfig storageSourceConfig = storageSourceConfigMapper.selectOne(queryWrapper);
        //获取文件列表  这个地方path+name
        File projectDir = new File(storageSourceConfig.getValue() + path + nodeName);
        // 获取所有子目录
        List<File> allSubDirs = new ArrayList<>();
        getAllSubDirectories(projectDir, allSubDirs);
        String storageSourceConfigPath = storageSourceConfig.getValue().replace("\\", "/");
        // 批量处理子目录
        long startBuildNodes = System.currentTimeMillis();
        for (File subDir : allSubDirs) {
            String normalizedSubDirPath = subDir.getAbsolutePath().replace("\\", "/");
            // 获取上一级目录
            String parentPath = normalizedSubDirPath.substring(0, normalizedSubDirPath.lastIndexOf("/"));
            // 去掉 D:/yun 部分
            String finalPath = parentPath.replace(storageSourceConfigPath, "");
            //保存文件信息
            TsFiles tsFiles1 = new TsFiles();
            //任务
            tsFiles1.setTaskId(taskId);
            //节点
            tsFiles1.setNodeId(nodeId);
            //文件 文件夹 区分
            tsFiles1.setIsFile("FOLDER");
            //上级ID
            tsFiles1.setParentId("00");
            //文件名称
            tsFiles1.setFileName(subDir.getName());
            //工作空间路径
            tsFiles1.setWorkPath(ensurePathFormat(finalPath));
            tsFiles1.setUploadTime(currentTime);


            if ("null".equals(String.valueOf(subDir.length()))) {
                tsFiles1.setFileSize("0.001");
            } else {
                //文件大小
                if ("0.000".equals(String.valueOf(subDir.length()))) {
                    tsFiles1.setFileSize("0.001");
                } else {
                    tsFiles1.setFileSize(String.valueOf(subDir.length()));
                }
            }
            tsFilesToCreate.add(tsFiles1);
        }
        LOGGER.info("[构建文件表中的文件夹列表] 耗时 {} ms | 待处理数量: {} 条",
                System.currentTimeMillis() - startBuildNodes,
                tsFilesToCreate.size());


        // 获取所有文件
        List<File> allFiles = new ArrayList<>();
        getAllFiles(projectDir, allFiles);
        System.out.println("allFiles=" + allFiles.size());
        long startBuildFiles = System.currentTimeMillis();
        // 批量处理文件
        for (File file : allFiles) {
            String normalizedFilesPath = file.getAbsolutePath().replace("\\", "/");
            // 获取上一级目录
            String parentPath = normalizedFilesPath.substring(0, normalizedFilesPath.lastIndexOf("/"));
            // 去掉 D:/yun 部分
            String finalPath = parentPath.replace(storageSourceConfigPath, "");
            //保存文件信息
            TsFiles tsFiles1 = new TsFiles();
            tsFiles1.setId(IdUtil.fastSimpleUUID());
            //任务
            tsFiles1.setTaskId(taskId);
            //节点
            tsFiles1.setNodeId(nodeId);
            //文件 文件夹 区分
            tsFiles1.setIsFile("FILE");
            //上级ID
            tsFiles1.setParentId("00");
            //文件名称
            tsFiles1.setFileName(file.getName());
            //工作空间路径
            tsFiles1.setWorkPath(ensurePathFormat(finalPath));
            tsFiles1.setUploadTime(currentTime);


            if ("null".equals(String.valueOf(file.length()))) {
                tsFiles1.setFileSize("0.001");
            } else {
                //文件大小
                if ("0.000".equals(String.valueOf(file.length()))) {
                    tsFiles1.setFileSize("0.001");
                } else {
                    tsFiles1.setFileSize(String.valueOf(file.length()));
                }
            }
            tsFilesToCreate.add(tsFiles1);
        }
        LOGGER.info("[构建文件表中的文件列表] 耗时 {} ms | 待处理数量: {} 条",
                System.currentTimeMillis() - startBuildFiles,
                tsFilesToCreate.size());

        // 批量插入文件表（忽略重复）
        if (!tsFilesToCreate.isEmpty()) {
            long startBatchInsertFiles = System.currentTimeMillis();
            int affectedRowsFiles = tsFilesMapper.batchInsertTsFiles(tsFilesToCreate);
            LOGGER.info("[批量插入试验任务文件表] 耗时 {} ms | 实际新增数量: {} 条",
                    System.currentTimeMillis() - startBatchInsertFiles,
                    affectedRowsFiles);
        }

        // 记录开始时间
        long startTimeFiles = System.currentTimeMillis();
        // 执行更新操作  taskId, String nodeId
        int affectedLevelFilesRows = tsFilesMapper.updateParentIdByPathHierarchy(taskId, nodeId);
        // 记录结束时间
        long endTimeFiles = System.currentTimeMillis();
        // 计算耗时
        long costTimeFiles = endTimeFiles - startTimeFiles;
        // 打印日志
        LOGGER.info("文件表中的节点ID更新完成，影响 {} 行，总耗时 {} 毫秒", affectedLevelFilesRows, costTimeFiles);
    }

    /**
     * 确保路径格式为以 "/" 开头和结尾（例如 "/data/test/"）
     * 若路径为空或非法，返回根路径 "/"
     */
    private String ensurePathFormat(String path) {
        if (StringUtils.isBlank(path)) {
            return "/";
        }
        // 统一替换反斜杠为斜杠（兼容 Windows 路径）
        String normalized = path.replaceAll("\\\\", "/");
        // 去掉多余的斜杠
        normalized = normalized.replaceAll("/{2,}", "/");
        // 确保以 "/" 开头
        if (!normalized.startsWith("/")) {
            normalized = "/" + normalized;
        }
        // 确保以 "/" 结尾
        if (!normalized.endsWith("/")) {
            normalized += "/";
        }
        return normalized;
    }

    private void getAllSubDirectories(File dir, List<File> subDirs) {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    subDirs.add(file);
                    getAllSubDirectories(file, subDirs);
                }
            }
        }
    }


    private void getAllFiles(File dir, List<File> files) {
        File[] fileList = dir.listFiles();
        if (fileList != null) {
            for (File file : fileList) {
                if (file.isFile()) {
                    files.add(file);
                } else if (file.isDirectory()) {
                    getAllFiles(file, files);
                }
            }
        }
    }

    /**
     * @param taskId   所属项目ID
     * @param nodeId   节点ID
     * @param fileName 名称
     * @param path     路径
     * @param parentId 父级ID
     * @param isFile   文件还是文件夹
     * @param fileSize 文件大小
     * @return
     */
    private TsFiles savetsFiles(String taskId, String nodeId, String fileName, String path, String parentId, String isFile, String fileSize) {
        // 设置当前时间
        LocalDateTime now = LocalDateTime.now();
        // 转换为 Timestamp
        Timestamp currentTime = Timestamp.valueOf(now);

        //保存文件信息
        TsFiles tsFiles1 = new TsFiles();
        //任务
        tsFiles1.setTaskId(taskId);
        //节点
        tsFiles1.setNodeId(nodeId);
        //文件 文件夹 区分
        tsFiles1.setIsFile(isFile);
        //上级ID
        tsFiles1.setParentId(parentId);
        //文件名称
        tsFiles1.setFileName(fileName);
        //工作空间路径
        tsFiles1.setWorkPath(path);
        tsFiles1.setUploadTime(currentTime);

        if ("null".equals(fileSize)) {
            tsFiles1.setFileSize("0.001");
        } else {
            //文件大小
            if ("0.000".equals(fileSize)) {
                tsFiles1.setFileSize("0.001");

            } else {
                tsFiles1.setFileSize(fileSize);
            }
        }

        tsFilesMapper.insert(tsFiles1);
        return tsFiles1;
    }

    /**
     * @param taskId   所属任务ID
     * @param parentId 父节点ID
     * @param nodeName 节点名称
     * @return
     */
    private TsNodes savetsNodes(String taskId, String parentId, String nodeName) {
        // 设置当前时间
        LocalDateTime now = LocalDateTime.now();
        // 转换为 Timestamp
        Timestamp currentTime = Timestamp.valueOf(now);
        //获取当前登录用户
        QueryWrapper<TsNodes> queryWrapperNodeOrder = new QueryWrapper<>();
        int orderno = this.count(queryWrapperNodeOrder.eq("parent_id", TOP_LEVEL_PARENT_NODE)) + 1;

        TsNodes node = new TsNodes();
        node.setTaskId(taskId);
        node.setParentId(parentId);
        node.setNodeName(nodeName);
        node.setCreateTime(currentTime);
        node.setNodeOrder(orderno);
        tsNodesMapper.insert(node);
        return node;
    }

    private FileListRequest buildFileRequest(String path) {
        FileListRequest fileListRequest = new FileListRequest();
        fileListRequest.setOrderBy("time");
        fileListRequest.setOrderDirection("desc");
        fileListRequest.setPassword("");
        fileListRequest.setPath(path);
        fileListRequest.setStorageKey("local");

        return fileListRequest;
    }

}
