package com.dji.sample.wayline.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.oss.service.impl.OssServiceContext;
import com.dji.sample.wayline.dao.IWaylineFileMapper;
import com.dji.sample.wayline.model.dto.KmzFileProperties;
import com.dji.sample.wayline.model.dto.WaylineFileDTO;
import com.dji.sample.wayline.model.dto.WaylineFileTreeVO;
import com.dji.sample.wayline.model.entity.WaylineFileEntity;
import com.dji.sample.wayline.model.enums.WaylineFileTypeEnum;
import com.dji.sample.wayline.model.param.WaylineSearchParam;
import com.dji.sample.wayline.service.IWaylineFileService;
import com.dji.sdk.cloudapi.device.DeviceDomainEnum;
import com.dji.sdk.cloudapi.device.DeviceEnum;
import com.dji.sdk.cloudapi.device.DeviceSubTypeEnum;
import com.dji.sdk.cloudapi.device.DeviceTypeEnum;
import com.dji.sdk.cloudapi.wayline.GetWaylineListRequest;
import com.dji.sdk.cloudapi.wayline.GetWaylineListResponse;
import com.dji.sdk.cloudapi.wayline.WaylineTypeEnum;
import com.dji.sdk.common.Pagination;
import com.dji.sdk.common.PaginationData;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import static com.dji.sample.common.util.SecurityUtils.aboveSysAdminRole;
import static com.dji.sample.common.util.SecurityUtils.getOrgId;
import static com.dji.sample.wayline.model.dto.KmzFileProperties.WAYLINE_FILE_SUFFIX;

/**
 * @author sean
 * @version 0.3
 * @date 2021/12/22
 */
@Service
@Transactional
public class WaylineFileServiceImpl extends ServiceImpl<IWaylineFileMapper, WaylineFileEntity> implements IWaylineFileService {

    @Autowired
    private IWaylineFileMapper mapper;

    @Autowired
    private OssServiceContext ossService;

    @Override
    public PaginationData<GetWaylineListResponse> getWaylinesByParam(String workspaceId, GetWaylineListRequest param) {

        LambdaQueryWrapper<WaylineFileEntity> waylineFileaQueryWrapper = new LambdaQueryWrapper<WaylineFileEntity>();

        if (aboveSysAdminRole()) {

        } else {
            waylineFileaQueryWrapper.eq(WaylineFileEntity::getOrgId, getOrgId());
        }

        waylineFileaQueryWrapper.eq(WaylineFileEntity::getIsDir, WaylineFileTypeEnum.FILE.getVal())
                .eq(WaylineFileEntity::getWorkspaceId, workspaceId)
                .eq(Objects.nonNull(param.getFavorited()), WaylineFileEntity::getFavorited, param.getFavorited())
                .and(param.getTemplateType() != null, wrapper -> {
                    for (WaylineTypeEnum type : param.getTemplateType()) {
                        wrapper.like(WaylineFileEntity::getTemplateTypes, type.getValue()).or();
                    }
                })
                .and(param.getPayloadModelKey() != null, wrapper -> {
                    for (DeviceEnum type : param.getPayloadModelKey()) {
                        wrapper.like(WaylineFileEntity::getPayloadModelKeys, type.getType()).or();
                    }
                })
                .and(param.getDroneModelKeys() != null, wrapper -> {
                    for (DeviceEnum type : param.getDroneModelKeys()) {
                        wrapper.eq(WaylineFileEntity::getDroneModelKey, type.getType()).or();
                    }
                })
                .like(Objects.nonNull(param.getKey()), WaylineFileEntity::getName, param.getKey())
                // There is a risk of SQL injection
                .last(Objects.nonNull(param.getOrderBy()), " order by " + param.getOrderBy().toString());
        // Paging Query
        Page<WaylineFileEntity> page = mapper.selectPage(
                new Page<>(param.getPage(), param.getPageSize()), waylineFileaQueryWrapper
);

        // Wrap the results of a paging query into a custom paging object.
        List<GetWaylineListResponse> records = page.getRecords()
                .stream()
                .map(this::entityConvertToDTO)
                .collect(Collectors.toList());

        return new PaginationData<>(records, new Pagination(page.getCurrent(), page.getSize(), page.getTotal()));
    }

    @Override
    public Optional<GetWaylineListResponse> getWaylineByWaylineId(String workspaceId, String waylineId) {
        return Optional.ofNullable(
                this.entityConvertToDTO(
                        mapper.selectOne(
                                new LambdaQueryWrapper<WaylineFileEntity>()
                                    .eq(WaylineFileEntity::getWorkspaceId, workspaceId)
                                    .eq(WaylineFileEntity::getWaylineId, waylineId))));
    }

    public WaylineFileEntity getWaylineFileEntityByWaylineId(String workspaceId, String waylineId) {

        LambdaQueryWrapper<WaylineFileEntity> fileQueryWrapper = new LambdaQueryWrapper<WaylineFileEntity>()
                .eq(WaylineFileEntity::getWorkspaceId, workspaceId)
                .eq(WaylineFileEntity::getWaylineId, waylineId);

        WaylineFileEntity waylineFileEntity = mapper.selectOne(fileQueryWrapper);

        return waylineFileEntity;
    }

    @Override
    public URL getObjectUrl(String workspaceId, String waylineId) throws SQLException {
        Optional<GetWaylineListResponse> waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId);
        if (waylineOpt.isEmpty()) {
            throw new SQLException(waylineId + " does not exist.");
        }
        return ossService.getObjectUrl(OssConfiguration.bucket, waylineOpt.get().getObjectKey());
    }

    @Override
    public Integer saveWaylineFile(String workspaceId, WaylineFileDTO metadata) {
        // 航线文件 一定是文件
        metadata.setIsDir(WaylineFileTypeEnum.FILE.getVal());
        if (!StringUtils.hasText(metadata.getParentId())) {
            metadata.setParentId("0");
            metadata.setAncestors("0");
        }

        WaylineFileEntity file = this.dtoConvertToEntity(metadata);
        file.setWaylineId(UUID.randomUUID().toString());
        file.setWorkspaceId(workspaceId);
        file.setOrgId(getOrgId());

        if (!StringUtils.hasText(file.getSign())) {
            try (InputStream object = ossService.getObject(OssConfiguration.bucket, metadata.getObjectKey())) {
                if (object.available() == 0) {
                    throw new RuntimeException("The file " + metadata.getObjectKey() +
                            " does not exist in the bucket[" + OssConfiguration.bucket + "].");
                }
                file.setSign(DigestUtils.md5DigestAsHex(object));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        int insertId = mapper.insert(file);
        return insertId > 0 ? file.getId() : insertId;
    }

    @Override
    public Boolean markFavorite(String workspaceId, List<String> waylineIds, Boolean isFavorite) {
        if (waylineIds.isEmpty()) {
            return false;
        }
        if (isFavorite == null) {
            return true;
        }
        return mapper.update(null, new LambdaUpdateWrapper<WaylineFileEntity>()
                .set(WaylineFileEntity::getFavorited, isFavorite)
                .eq(WaylineFileEntity::getWorkspaceId, workspaceId)
                .in(WaylineFileEntity::getWaylineId, waylineIds)) > 0;
    }

    @Override
    public List<String> getDuplicateNames(String workspaceId, List<String> names) {
        return mapper.selectList(new LambdaQueryWrapper<WaylineFileEntity>()
                .eq(WaylineFileEntity::getWorkspaceId, workspaceId)
                .in(WaylineFileEntity::getName, names))
                .stream()
                .map(WaylineFileEntity::getName)
                .collect(Collectors.toList());
    }

    @Override
    public Boolean deleteByWaylineId(String workspaceId, String waylineId) {
        Optional<GetWaylineListResponse> waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId);
        if (waylineOpt.isEmpty()) {
            return true;
        }
        GetWaylineListResponse wayline = waylineOpt.get();
        boolean isDel = mapper.delete(new LambdaUpdateWrapper<WaylineFileEntity>()
                    .eq(WaylineFileEntity::getWorkspaceId, workspaceId)
                    .eq(WaylineFileEntity::getWaylineId, waylineId))
                > 0;
        if (!isDel) {
            return false;
        }
        return ossService.deleteObject(OssConfiguration.bucket, wayline.getObjectKey());
    }

    /**
     * Delete the wayline file based on the wayline id.
     * @param workspaceId
     * @param ids
     * @param waylineIds
     */
    @Override
    public Boolean deleteBatchByIdOrWaylineId(String workspaceId, List<String> ids, List<String> waylineIds) {

        LambdaQueryWrapper<WaylineFileEntity> wrapper = new LambdaQueryWrapper<>();

        wrapper.eq(WaylineFileEntity::getWorkspaceId, workspaceId);

        if (!CollectionUtils.isEmpty(ids)) {
            wrapper.in(WaylineFileEntity::getId, ids);
        } else if (!CollectionUtils.isEmpty(waylineIds)) {
            wrapper.in(WaylineFileEntity::getWaylineId, waylineIds);
        } else {
            return false;
        }
        List<WaylineFileEntity> list = this.list(wrapper);

        if (CollectionUtils.isEmpty(list)) {
            return false;
        }

        List<Integer> dbIds = list.stream().map(WaylineFileEntity::getId).collect(Collectors.toList());

        boolean remove = this.removeByIds(dbIds);

        return remove;
    }


    @Override
    public void importKmzFile(MultipartFile file, String workspaceId, String creator) {
        importKmzFile(file, workspaceId, creator, null);
    }

    @Override
    public void importKmzFile(MultipartFile file, String workspaceId, String creator, String fileParentId) {
        Optional<WaylineFileDTO> waylineFileOpt = validKmzFile(file, workspaceId);
        if (waylineFileOpt.isEmpty()) {
            throw new RuntimeException("The file format is incorrect.");
        }

        try {
            WaylineFileDTO waylineFile = waylineFileOpt.get();
            waylineFile.setUsername(creator);
            // 查询 fileParentId
            if (StringUtils.hasText(fileParentId) && !"0".equals(fileParentId)) {
                LambdaQueryWrapper<WaylineFileEntity> fileQueryWrapper = new LambdaQueryWrapper<>();
                fileQueryWrapper.eq(WaylineFileEntity::getWaylineId, fileParentId);

                WaylineFileEntity parentWaylineFile = mapper.selectOne(fileQueryWrapper);

                if (parentWaylineFile.getIsDir() != WaylineFileTypeEnum.DIRECTORY.getVal()) {
                    throw new RuntimeException("parent structure is not a directory");
                }

                waylineFile.setParentId(parentWaylineFile.getWaylineId());
                waylineFile.setAncestors(parentWaylineFile.getAncestors() + StringPool.COMMA + parentWaylineFile.getWaylineId());
            } else {
                waylineFile.setParentId("0");
                waylineFile.setAncestors("0");
            }

            ossService.putObject(OssConfiguration.bucket, waylineFile.getObjectKey(), file.getInputStream());
            this.saveWaylineFile(workspaceId, waylineFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Integer addDirectory(String workspaceId, WaylineFileDTO waylineDirDTO) {

        WaylineFileEntity dir = dirDtoConvertToEntity(waylineDirDTO);

        // 获取parent
        if (StringUtils.hasText(waylineDirDTO.getParentId()) && !"0".equals(waylineDirDTO.getParentId())) {
            String parentId = waylineDirDTO.getParentId();
            WaylineFileEntity parent = this.getWaylineFileEntityByWaylineId(workspaceId, parentId);
            if (parent != null) {
                if (parent.getIsDir() != WaylineFileTypeEnum.DIRECTORY.getVal()) {
                    throw new RuntimeException("parent structure is not a directory");
                }
               String ancestors = StringUtils.hasText(parent.getAncestors()) ? parent.getAncestors() : "0";
                dir.setAncestors(ancestors + StringPool.COMMA + parentId);
            } else {
                throw new RuntimeException("parent directory is not found");
            }
        } else {
            dir.setParentId("0");
            dir.setAncestors("0");
        }

        dir.setIsDir(WaylineFileTypeEnum.DIRECTORY.getVal());
        dir.setWaylineId(UUID.randomUUID().toString());
        dir.setWorkspaceId(workspaceId);
        dir.setOrgId(getOrgId());

        int insertId = mapper.insert(dir);

        return insertId > 0 ? dir.getId() : insertId;
    }

    @Override
    public List<WaylineFileTreeVO> getDirectoryTree(String workspaceId) {

        return getDirectoryTree(workspaceId, "0");
    }

    /**
     * get directory tree
     * @param workspaceId
     */
    @Override
    public List<WaylineFileTreeVO> getWaylineFileTree(String workspaceId, String rootId) {
        LambdaQueryWrapper<WaylineFileEntity> fileQueryWrapper = new LambdaQueryWrapper<>();
        fileQueryWrapper.eq(WaylineFileEntity::getWorkspaceId, workspaceId);
        fileQueryWrapper.like(WaylineFileEntity::getAncestors, rootId);
        // fileQueryWrapper.eq(WaylineFileEntity::getIsDir, WaylineFileTypeEnum.DIRECTORY.getVal());

        List<WaylineFileEntity> waylineFiles = mapper.selectList(fileQueryWrapper);
        return getWaylineFileTree(waylineFiles, rootId);
    }
    public List<WaylineFileTreeVO> getDirectoryTree(String workspaceId, String rootId) {

        LambdaQueryWrapper<WaylineFileEntity> fileQueryWrapper = new LambdaQueryWrapper<>();

        fileQueryWrapper.eq(WaylineFileEntity::getWorkspaceId, workspaceId);
        fileQueryWrapper.like(WaylineFileEntity::getAncestors, rootId);
        fileQueryWrapper.eq(WaylineFileEntity::getIsDir, WaylineFileTypeEnum.DIRECTORY.getVal());

        if (aboveSysAdminRole()) {

        } else {
            // 只能 查看自己团队的 航线
            fileQueryWrapper.eq(WaylineFileEntity::getOrgId, getOrgId());
        }
        List<WaylineFileEntity> waylineFiles = mapper.selectList(fileQueryWrapper);

        return getWaylineFileTree(waylineFiles, rootId);

    }

    public List<WaylineFileTreeVO> getWaylineFileTree(List<WaylineFileEntity> waylineFiles, String rootId) {
        // 转换一下
        List<WaylineFileTreeVO> waylineFileTreeVOS = waylineFiles.stream().map(x -> {
            WaylineFileTreeVO vo = new WaylineFileTreeVO();
            BeanUtils.copyProperties(x, vo);  // 复制属性
            return vo;
        }).collect(Collectors.toList());

        Map<String, List<WaylineFileTreeVO>> waylineFileMapByParentId = waylineFileTreeVOS.stream().collect(Collectors.groupingBy(WaylineFileTreeVO::getParentId));
        // 返回 一个树结构
        if (!StringUtils.hasText(rootId)) {
            rootId = "0";
        }

        Map<String, WaylineFileTreeVO> nodeMap = new HashMap<>();

        List<WaylineFileTreeVO> roots = new ArrayList<>();

        // 1. 先存入 Map
        for (WaylineFileTreeVO item : waylineFileTreeVOS) {
            nodeMap.put(item.getWaylineId(), item);
        }

        // 2. 建立父子关系
        for (WaylineFileTreeVO item : waylineFileTreeVOS) {

            String parentId = item.getParentId();

            WaylineFileTreeVO node = nodeMap.get(item.getWaylineId());
            if (rootId.equals(parentId)) {
                // 多个根节点
                roots.add(node);
            } else {
                if (Objects.isNull(nodeMap.get(parentId))) {
                    continue;
                }
                // 归属于父节点
                if (CollectionUtils.isEmpty(nodeMap.get(parentId).getChildren())) {
                    nodeMap.get(parentId).setChildren(new ArrayList<>());
                }
                nodeMap.get(parentId).getChildren().add(node);
            }
        }

        return roots;
    }

    private Optional<WaylineFileDTO> validKmzFile(MultipartFile file, String workspaceId) {
        String filename = file.getOriginalFilename();
        if (Objects.nonNull(filename) && !filename.endsWith(WAYLINE_FILE_SUFFIX)) {
            throw new RuntimeException("The file format is incorrect.");
        }
        try (ZipInputStream unzipFile = new ZipInputStream(file.getInputStream(), StandardCharsets.UTF_8)) {

            ZipEntry nextEntry = unzipFile.getNextEntry();
            while (Objects.nonNull(nextEntry)) {
                boolean isWaylines = (KmzFileProperties.FILE_DIR_FIRST + "/" + KmzFileProperties.FILE_DIR_SECOND_TEMPLATE).equals(nextEntry.getName());
                if (!isWaylines) {
                    nextEntry = unzipFile.getNextEntry();
                    continue;
                }
                SAXReader reader = new SAXReader();
                Document document = reader.read(unzipFile);
                if (!StandardCharsets.UTF_8.name().equals(document.getXMLEncoding())) {
                    throw new RuntimeException("The file encoding format is incorrect.");
                }

                Node droneNode = document.selectSingleNode("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_INFO);
                Node payloadNode = document.selectSingleNode("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_INFO);
                if (Objects.isNull(droneNode) || Objects.isNull(payloadNode)) {
                    throw new RuntimeException("The file format is incorrect.");
                }

                DeviceTypeEnum type = DeviceTypeEnum.find(Integer.parseInt(droneNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_ENUM_VALUE)));
                DeviceSubTypeEnum subType = DeviceSubTypeEnum.find(Integer.parseInt(droneNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_SUB_ENUM_VALUE)));
                DeviceTypeEnum payloadType = DeviceTypeEnum.find(Integer.parseInt(payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_ENUM_VALUE)));
                DeviceSubTypeEnum payloadSubType = DeviceSubTypeEnum.find(Integer.parseInt(payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_SUB_ENUM_VALUE)));
                String templateType = document.valueOf("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_TEMPLATE_TYPE);

                return Optional.of(WaylineFileDTO.builder()
                        .droneModelKey(DeviceEnum.find(DeviceDomainEnum.DRONE, type, subType).getDevice())
                        .payloadModelKeys(List.of(DeviceEnum.find(DeviceDomainEnum.PAYLOAD, payloadType, payloadSubType).getDevice()))
                        // .objectKey(OssConfiguration.objectDirPrefix + File.separator + filename)
                        .objectKey(OssConfiguration.objectDirPrefix + "/" + workspaceId + "/" + filename)
                        .name(filename.substring(0, filename.lastIndexOf(WAYLINE_FILE_SUFFIX)))
                        .sign(DigestUtils.md5DigestAsHex(file.getInputStream()))
                        .templateTypes(List.of(WaylineTypeEnum.find(templateType).getValue()))
                        .build());
            }

        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }
    /**
     * Convert database entity objects into wayline data transfer object.
     * @param entity
     * @return
     */
    private GetWaylineListResponse entityConvertToDTO(WaylineFileEntity entity) {
        if (entity == null) {
            return null;
        }
        return new GetWaylineListResponse()
                .setDroneModelKey(DeviceEnum.find(entity.getDroneModelKey()))
                .setFavorited(entity.getFavorited())
                .setName(entity.getName())
                .setPayloadModelKeys(entity.getPayloadModelKeys() != null ?
                        Arrays.stream(entity.getPayloadModelKeys().split(",")).map(DeviceEnum::find).collect(Collectors.toList()) : null)
                .setTemplateTypes(Arrays.stream(entity.getTemplateTypes().split(","))
                        .map(Integer::parseInt).map(WaylineTypeEnum::find)
                        .collect(Collectors.toList()))
                .setUsername(entity.getUsername())
                .setObjectKey(entity.getObjectKey())
                .setSign(entity.getSign())
                .setUpdateTime(entity.getUpdateTime())
                .setCreateTime(entity.getCreateTime())
                .setId(entity.getWaylineId());

    }

    /**
     * Convert the received wayline object into a database entity object.
     * @param file
     * @return
     */
    private WaylineFileEntity dtoConvertToEntity(WaylineFileDTO file) {

        if (file.getIsDir() != null && file.getIsDir() == WaylineFileTypeEnum.DIRECTORY.getVal()) {
            return dirDtoConvertToEntity(file);
        }
        WaylineFileEntity.WaylineFileEntityBuilder builder = WaylineFileEntity.builder();

        if (file != null) {
            builder.droneModelKey(file.getDroneModelKey())
                    .name(file.getName())
                    .username(file.getUsername())
                    .objectKey(file.getObjectKey())
                    .orgId(file.getOrgId())
                    // Separate multiple payload data with ",".
                    .payloadModelKeys(String.join(",", file.getPayloadModelKeys()))
                    .templateTypes(file.getTemplateTypes().stream()
                            .map(String::valueOf)
                            .collect(Collectors.joining(",")))
                    .favorited(file.getFavorited())
                    .sign(file.getSign())
                    .isDir(file.getIsDir())
                    .parentId(file.getParentId())
                    .ancestors(file.getAncestors())
                    .build();
        }

        return builder.build();
    }

    /**
     * Convert the received directory object into a database entity object.
     * @param file
     * @return
     */
    private WaylineFileEntity dirDtoConvertToEntity(WaylineFileDTO file) {
        WaylineFileEntity.WaylineFileEntityBuilder builder = WaylineFileEntity.builder();

        if (file != null) {
            builder.droneModelKey(file.getDroneModelKey())
                    .name(file.getName())
                    .username(file.getUsername())
                    .objectKey(file.getObjectKey())
                    .orgId(file.getOrgId())
                    // Separate multiple payload data with ",".
                    // .payloadModelKeys(String.join(",", file.getPayloadModelKeys()))
//                    .templateTypes(file.getTemplateTypes().stream()
//                            .map(String::valueOf)
//                            .collect(Collectors.joining(",")))
                    .favorited(file.getFavorited())
                    .sign(file.getSign())
                    .isDir(file.getIsDir())
                    .parentId(file.getParentId())
                    .ancestors(file.getAncestors())
                    .build();
        }
        return builder.build();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateParentId(String waylineFileId, String parentId) {


        // 查询数据库
        LambdaQueryWrapper<WaylineFileEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(WaylineFileEntity::getWaylineId, waylineFileId, parentId);

        List<WaylineFileEntity> waylineFileEntity = this.mapper.selectList(lambdaQueryWrapper);

        WaylineFileEntity updateEntity = new WaylineFileEntity();
        WaylineFileEntity parentEntity = new WaylineFileEntity();
        for (WaylineFileEntity item : waylineFileEntity) {
            if (item.getWaylineId().equals(waylineFileId)) {
                updateEntity = item;
            }
            if (item.getWaylineId().equals(parentId)) {
                parentEntity = item;
            }
        }

        LambdaUpdateWrapper<WaylineFileEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(WaylineFileEntity::getWaylineId, waylineFileId);
        WaylineFileEntity update1 = new WaylineFileEntity();
        update1.setParentId(parentId);
        if (StringUtils.hasText(parentId) && !"0".equals(parentId)) {
            update1.setAncestors(parentEntity.getAncestors() + StringPool.COMMA + parentId);
        } else {
            update1.setAncestors("0");
        }

//        updateWrapper.set(WaylineFileEntity::getParentId, parentId);
//        updateWrapper.set(WaylineFileEntity::getAncestors, parentEntity.getAncestors() + StringPool.COMMA + parentId);
        // 除了修改这些数据 还得修改 子节点的数据
        int result1 = mapper.update(update1, updateWrapper);

        // 修改 子节点数据
        // List<WaylineFileEntity> children = this.mapper.selectList(new LambdaQueryWrapper<WaylineFileEntity>().eq(WaylineFileEntity::getParentId, waylineFileId));
        LambdaUpdateWrapper<WaylineFileEntity> updateChildWrapper = new LambdaUpdateWrapper<>();
        updateChildWrapper.eq(WaylineFileEntity::getParentId, waylineFileId);
        WaylineFileEntity update2 = new WaylineFileEntity();
        if (StringUtils.hasText(parentId) && !"0".equals(parentId)) {
            update2.setAncestors(parentEntity.getAncestors() + StringPool.COMMA + parentId + StringPool.COMMA + waylineFileId);
        } else {
            update2.setAncestors("0" + StringPool.COMMA + waylineFileId);
        }
        int result2 = mapper.update(update2, updateChildWrapper);

        return result1 > 0 && result2 > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateParentIdBatch(String waylineFileIds, String parentId) {

        List<String> waylineFileIdList = Arrays.asList(waylineFileIds.split(","));
        return updateParentIdBatch(waylineFileIdList, parentId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateParentIdBatch(List<String> waylineFileIdList, String parentId) {

        // 查询数据库
        List<String> allWaylineFileIdList = new ArrayList<>(waylineFileIdList);
        allWaylineFileIdList.add(parentId);
        LambdaQueryWrapper<WaylineFileEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(WaylineFileEntity::getWaylineId, allWaylineFileIdList);

        List<WaylineFileEntity> waylineFileEntity = this.mapper.selectList(lambdaQueryWrapper);

        List<WaylineFileEntity> updateEntityList = new ArrayList<>();
        WaylineFileEntity parentEntity = new WaylineFileEntity();

        for (WaylineFileEntity item : waylineFileEntity) {
            if (waylineFileIdList.contains(item.getWaylineId())) {
                updateEntityList.add(item);
            }
            if (item.getWaylineId().equals(parentId)) {
                parentEntity = item;
            }
        }

        LambdaUpdateWrapper<WaylineFileEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(WaylineFileEntity::getWaylineId, waylineFileIdList);
        updateWrapper.set(WaylineFileEntity::getParentId, parentId);
        if (StringUtils.hasText(parentId) && !"0".equals(parentId)) {
            updateWrapper.set(WaylineFileEntity::getAncestors, (parentEntity.getAncestors() + StringPool.COMMA + parentId));
        } else {
            updateWrapper.set(WaylineFileEntity::getAncestors, "0");
        }
        boolean result1 = this.update(updateWrapper);

        // 修改 子节点数据
        for (String waylineFileId : waylineFileIdList) {
            // todo 只允许修改叶子节点
            LambdaUpdateWrapper<WaylineFileEntity> updateChildWrapper = new LambdaUpdateWrapper<>();
            updateChildWrapper.eq(WaylineFileEntity::getParentId, waylineFileId);
            if (StringUtils.hasText(parentId) && !"0".equals(parentId)) {
                updateChildWrapper.set(WaylineFileEntity::getAncestors, (parentEntity.getAncestors() + StringPool.COMMA + parentId + StringPool.COMMA + waylineFileId));
            } else {
                updateChildWrapper.set(WaylineFileEntity::getAncestors, ("0" + StringPool.COMMA + waylineFileId));
            }
            boolean result2 = this.update(updateChildWrapper);
        }
        return result1;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteDirectory(String workspaceId, String waylineFileId) {

        int delete1 = this.mapper.delete(new LambdaQueryWrapper<WaylineFileEntity>().eq(WaylineFileEntity::getWaylineId, waylineFileId));

        // 删除后续子节点
        int delete2 = this.mapper.delete(new LambdaQueryWrapper<WaylineFileEntity>().like(WaylineFileEntity::getAncestors, waylineFileId));

        return true;
    }

    @Override
    public PaginationData<GetWaylineListResponse> getWaylinesByParam(String workspaceId, String parentId, GetWaylineListRequest param) {
        // Paging Query
        LambdaQueryWrapper<WaylineFileEntity> waylineFileQueryWrapper = new LambdaQueryWrapper<>();

        if (aboveSysAdminRole()) {

        } else {
            waylineFileQueryWrapper.eq(WaylineFileEntity::getOrgId, getOrgId());
        }
        waylineFileQueryWrapper.eq(WaylineFileEntity::getIsDir, WaylineFileTypeEnum.FILE.getVal())
                .eq(WaylineFileEntity::getWorkspaceId, workspaceId)
                .eq(Objects.nonNull(param.getFavorited()), WaylineFileEntity::getFavorited, param.getFavorited());
        waylineFileQueryWrapper.and(param.getTemplateType() != null, wrapper ->  {
            for (WaylineTypeEnum type : param.getTemplateType()) {
                wrapper.like(WaylineFileEntity::getTemplateTypes, type.getValue()).or();
              }
            })
                .and(param.getPayloadModelKey() != null, wrapper ->  {
                    for (DeviceEnum type : param.getPayloadModelKey()) {
                        wrapper.like(WaylineFileEntity::getPayloadModelKeys, type.getType()).or();
                    }
                })
                .and(param.getDroneModelKeys() != null, wrapper ->  {
                    for (DeviceEnum type : param.getDroneModelKeys()) {
                        wrapper.eq(WaylineFileEntity::getDroneModelKey, type.getType()).or();
                    }
                });
        if (StringUtils.hasText(parentId)) {
            waylineFileQueryWrapper.eq(WaylineFileEntity::getParentId, parentId);
        }

        waylineFileQueryWrapper.like(Objects.nonNull(param.getKey()), WaylineFileEntity::getName, param.getKey());
        // There is a risk of SQL injection
        waylineFileQueryWrapper.last(Objects.nonNull(param.getOrderBy()), " order by " + param.getOrderBy().toString());
        Page<WaylineFileEntity> page = mapper.selectPage(new Page<>(param.getPage(), param.getPageSize()),waylineFileQueryWrapper);

        // Wrap the results of a paging query into a custom paging object.
        List<GetWaylineListResponse> records = page.getRecords()
                .stream()
                .map(this::entityConvertToDTO)
                .collect(Collectors.toList());

        return new PaginationData<>(records, new Pagination(page.getCurrent(), page.getSize(), page.getTotal()));
    }

    @Override
    public PaginationData<GetWaylineListResponse> getWaylinesByParam(WaylineSearchParam param, String workspaceId, GetWaylineListRequest request) {
        // Paging Query
        LambdaQueryWrapper<WaylineFileEntity> waylineFileQueryWrapper = new LambdaQueryWrapper<>();

        if (aboveSysAdminRole()) {

        } else {
            waylineFileQueryWrapper.eq(WaylineFileEntity::getOrgId, getOrgId());
        }

        waylineFileQueryWrapper.eq(WaylineFileEntity::getIsDir, WaylineFileTypeEnum.FILE.getVal());
        waylineFileQueryWrapper.eq(WaylineFileEntity::getWorkspaceId, workspaceId);

        waylineFileQueryWrapper.eq(Objects.nonNull(request.getFavorited()), WaylineFileEntity::getFavorited, request.getFavorited());
        waylineFileQueryWrapper.and(request.getTemplateType() != null, wrapper ->  {
            for (WaylineTypeEnum type : request.getTemplateType()) {
                wrapper.like(WaylineFileEntity::getTemplateTypes, type.getValue()).or();
            }
        })
                .and(request.getPayloadModelKey() != null, wrapper ->  {
                    for (DeviceEnum type : request.getPayloadModelKey()) {
                        wrapper.like(WaylineFileEntity::getPayloadModelKeys, type.getType()).or();
                    }
                })
                .and(request.getDroneModelKeys() != null, wrapper ->  {
                    for (DeviceEnum type : request.getDroneModelKeys()) {
                        wrapper.eq(WaylineFileEntity::getDroneModelKey, type.getType()).or();
                    }
                });
        if (StringUtils.hasText(param.getParentId())) {
            waylineFileQueryWrapper.eq(WaylineFileEntity::getParentId, param.getParentId());
        }

        // 名字
        waylineFileQueryWrapper.like(Objects.nonNull(param.getName()), WaylineFileEntity::getName, param.getName());
        waylineFileQueryWrapper.like(Objects.nonNull(request.getKey()), WaylineFileEntity::getName, request.getKey());
        // 设备型号
        waylineFileQueryWrapper.eq(Objects.nonNull(param.getDroneModelKey()), WaylineFileEntity::getDroneModelKey, param.getDroneModelKey());

        // There is a risk of SQL injection
        waylineFileQueryWrapper.last(Objects.nonNull(request.getOrderBy()), " order by " + request.getOrderBy().toString());
        Page<WaylineFileEntity> page = mapper.selectPage(new Page<>(request.getPage(), request.getPageSize()),waylineFileQueryWrapper);

        // Wrap the results of a paging query into a custom paging object.
        List<GetWaylineListResponse> records = page.getRecords()
                .stream()
                .map(this::entityConvertToDTO)
                .collect(Collectors.toList());

        return new PaginationData<>(records, new Pagination(page.getCurrent(), page.getSize(), page.getTotal()));
    }

    @Override
    public boolean updateWaylineFile(String workspaceId, String waylineId, WaylineFileDTO waylineFileDTO) {
        LambdaUpdateWrapper<WaylineFileEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(WaylineFileEntity::getWorkspaceId, workspaceId)
                     .eq(WaylineFileEntity::getWaylineId, waylineId);
        
        // 只更新非空字段
        if (waylineFileDTO.getName() != null) {
            updateWrapper.set(WaylineFileEntity::getName, waylineFileDTO.getName());
        }
        // if (waylineFileDTO.getDroneModelKey() != null) {
        //     updateWrapper.set(WaylineFileEntity::getDroneModelKey, waylineFileDTO.getDroneModelKey());
        // }
        // if (waylineFileDTO.getPayloadModelKeys() != null) {
        //     String payloadKeys = waylineFileDTO.getPayloadModelKeys().stream()
        //             .collect(Collectors.joining(","));
        //     updateWrapper.set(WaylineFileEntity::getPayloadModelKeys, payloadKeys);
        // }
        // if (waylineFileDTO.getTemplateTypes() != null) {
        //     String templateTypes = waylineFileDTO.getTemplateTypes().stream()
        //             .map(String::valueOf)
        //             .collect(Collectors.joining(","));
        //     updateWrapper.set(WaylineFileEntity::getTemplateTypes, templateTypes);
        // }
        
        return mapper.update(null, updateWrapper) > 0;
    }

}
