package com.ruoyi.project.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.project.business.domain.dto.RemoveDirectoryDto;
import com.ruoyi.project.business.domain.train.TtFileDirectory;
import com.ruoyi.project.business.domain.vo.train.FileDataVisibleRangeVo;
import com.ruoyi.project.business.domain.vo.train.FileDirReq;
import com.ruoyi.project.business.domain.vo.train.FileDirectoryTreeVo;
import com.ruoyi.project.business.mapper.TtFileDirectoryMapper;
import com.ruoyi.project.business.service.train.ITtFileDirectoryService;
import com.ruoyi.project.config.AppConfig;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author fangk
 * @description 针对表【tt_file_directory】的数据库操作Service实现
 * @createDate 2024-05-20 15:28:25
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TtFileDirectoryServiceImpl extends ServiceImpl<TtFileDirectoryMapper, TtFileDirectory>
        implements ITtFileDirectoryService, SmartInitializingSingleton {
    
    // root from db
    private static TtFileDirectory root;

    @Override
    public List<TtFileDirectory> listRange() {
        FileDirReq build = FileDirReq.builder().roleKeys(SecurityUtils.getRoleKeys()).areaIds(SecurityUtils.getAreaIds()).build();
        List<TtFileDirectory> dirs = baseMapper.listRange(build);
        // 排除回收站
        dirs.removeIf(item -> AppConfig.getRecycleFolder().equals(item.getDerectoryName()));
        // 补充添加根目录
        if (root != null) {
            dirs.add(root);
        }
        return dirs;
    }
    
    /**
     * 由于使用了静态变量，所以不适合使用 @PostConstruct 注解初始化
     */
    @Override
    public void afterSingletonsInstantiated() {
        initRoot();
        initRecycleBin();
    }

    /**
     * 初始化根目录，如果不存在则创建
     */
    @SneakyThrows
    private void initRoot() {
        log.info("配置资料库根目录:{}", AppConfig.getDataFolder());
        boolean existRoot = baseMapper.exists(Wrappers.lambdaQuery(TtFileDirectory.class).eq(TtFileDirectory::getDerectoryName, AppConfig.getDataFolder()));
        if (!existRoot) {
            log.info("资料库根目录不存在，正在创建");
            TtFileDirectory directory = TtFileDirectory.builder().id(0L).derectoryName(AppConfig.getDataFolder()).superDirectory(null).build();
            save(directory);
            log.info("资料库根目录创建成功");
            root = directory;
        } else {
            root = getOne(Wrappers.lambdaQuery(TtFileDirectory.class).eq(TtFileDirectory::getDerectoryName, AppConfig.getDataFolder()),false);
        }
        FileUtils.createDirectory(AppConfig.getDataFolder());
    }

    @SneakyThrows
    private void initRecycleBin() {
        log.info("配置回收站目录:{}", AppConfig.getRecycleFolder());
        boolean existRecycle = baseMapper.exists(Wrappers.lambdaQuery(TtFileDirectory.class).eq(TtFileDirectory::getDerectoryName, AppConfig.getRecycleFolder()));
        if (!existRecycle) {
            log.info("回收站目录不存在，正在创建");
            TtFileDirectory directory = TtFileDirectory.builder().derectoryName(AppConfig.getRecycleFolder()).superDirectory(null).build();
            save(directory);
            log.info("回收站目录创建成功");
        }
        FileUtils.createDirectory(AppConfig.getRecyclePath());
    }

    @Override
    // public List<Tree<String>> getDirectoryTree() {
    public List<FileDirectoryTreeVo> getDirectoryTree() {
        // 排除回收站
        List<TtFileDirectory> all = list(Wrappers.lambdaQuery(TtFileDirectory.class).ne(TtFileDirectory::getDerectoryName, AppConfig.getRecycleFolder()));
        // List<TtFileDirectory> all = listRange();

        List<FileDirectoryTreeVo> treeVos = getDireTree(null, all);
        log.info("treeVos:{}", treeVos);

        return treeVos;
    }

    @Override
    public String getDataFolder(Long curFolderId) {
        TtFileDirectory ttFileDirectory = getById(curFolderId);
        if (ttFileDirectory == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(ttFileDirectory.getDerectoryName());
        // TODO 不要递归查询，稍后处理
        while (ttFileDirectory.getSuperDirectory() != null) {
            ttFileDirectory = getById(ttFileDirectory.getSuperDirectory());
            // 如果以 / 结尾，不加 /
            // 如果 分隔符 需要处理的话，使用Paths.get().toString() 来获取统一的标准化的路径字符串，再处理

            sb.insert(0, ttFileDirectory.getDerectoryName().endsWith("/") ? ttFileDirectory.getDerectoryName() : ttFileDirectory.getDerectoryName() + "/");
        }
        return sb.toString();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RemoveDirectoryDto removeDirectoryAndChildren(Long id) {
        List<TtFileDirectory> list = list();
        // List<TtFileDirectory> list = listRange();

        List<TtFileDirectory> result = new ArrayList<>();
        // 递归获取所有子目录
        getChildren(id, list, result);
        // 获取所有子目录id集合
        List<Long> ids = result.stream().map(TtFileDirectory::getId).collect(Collectors.toList());
        ids.add(id);

        // 删除所有子目录
        boolean r = removeByIds(ids);

        // 获取当前目录
        String currentDirectory = getCurrentDirectory(id, list);

        return new RemoveDirectoryDto(ids, currentDirectory);
    }

    @SneakyThrows
    @Override
    public boolean add(TtFileDirectory fileDirectory) {
        // 检验是否和回收站重名
        if (fileDirectory.getDerectoryName().equals(AppConfig.getRecycleFolder())) {
            throw new Exception("目录名称不能和系统回收站重名");
        }


        // 添加物理路径   // TODO 是不是可以在插入时保存除根路径以外的完整路径  /   或者使用SpringCache
        String currentDirectory = getCurrentDirectory(fileDirectory.getSuperDirectory(), list());
        String newDirectory = currentDirectory + File.separator + fileDirectory.getDerectoryName();
        
        // fileDirectory.setVisibleRange1(String.join(",", SecurityUtils.getRoleKeys()));
        // fileDirectory.setVisibleRange2(SecurityUtils.getAreaIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
        boolean save = save(fileDirectory);
        FileUtils.createDirectory(newDirectory);
        
        return save;
    }

    @Override
    public List<TtFileDirectory> getSubDirectoryWithSelf(Long id) {
        // List<TtFileDirectory> list = list();
        // List<TtFileDirectory> result = new ArrayList<>();
        // getChildren(id, list, result);
        // result.add(list.stream().filter(item -> id.equals(item.getId())).findFirst().orElse(null));
        // return result;
        return getSubDirectoryWithSelf(Collections.singletonList(id));
    }

    @Override
    public List<TtFileDirectory> getSubDirectoryWithSelf(List<Long> dIds) {
        if (CollectionUtils.isEmpty(dIds)) {
            return Collections.emptyList();
        }
        List<TtFileDirectory> list = list();
        // List<TtFileDirectory> list = listRange();
        List<TtFileDirectory> result = new ArrayList<>();
        for (Long id : dIds) {
            getChildren(id, list, result);
            result.add(list.stream().filter(item -> id.equals(item.getId())).findFirst().orElse(null));
        }
        // 去重
        return dIds.size() == 1 ? result : new ArrayList<>(new HashSet<>(result));
    }

    @Override
    public boolean editVisibleRange(FileDataVisibleRangeVo vo) {
        List<Long> dirIds = vo.getDirectoryIds();
        if (CollectionUtils.isEmpty(dirIds)) {
            throw new RuntimeException("选中目录不能为空");
        }
        List<TtFileDirectory> subDirs = getSubDirectoryWithSelf(dirIds);
        for (TtFileDirectory subDir : subDirs) {
            // subDir.setVisibleRange1(vo.getVisibleRange1());
            // subDir.setVisibleRange2(vo.getVisibleRange2());
        }
        return updateBatchById(subDirs);
    }

    /**
     * 获取当前目录完整路径 包含根目录
     *
     * @param id   当前目录id
     * @param list 所有目录
     */
    private String getCurrentDirectory(Long id, List<TtFileDirectory> list) {

        // 将所有目录转换为id和item的map
        Map<Long, TtFileDirectory> dirId2NameMap = list.stream().collect(Collectors.toMap(TtFileDirectory::getId, item -> item));
        // 获取当前目录
        TtFileDirectory ttFileDirectory = dirId2NameMap.get(id);
        if (ttFileDirectory == null) {
            return null;
        }

        StringBuilder sb = new StringBuilder(ttFileDirectory.getDerectoryName());
        while (ttFileDirectory.getSuperDirectory() != null) {
            ttFileDirectory = dirId2NameMap.get(ttFileDirectory.getSuperDirectory());
            if (ttFileDirectory == null) {
                break;
            }
            sb.insert(0, ttFileDirectory.getDerectoryName().endsWith("/") ? ttFileDirectory.getDerectoryName() : ttFileDirectory.getDerectoryName() + "/");
        }
        return sb.toString();

    }


    /**
     * 递归获取指定目录下的所有子目录
     * 
     * @param id    当前目录id
     * @param list  所有目录
     * @param result    结果集
     */
    private void getChildren(Long id, List<TtFileDirectory> list, List<TtFileDirectory> result) {
        List<TtFileDirectory> children = list.stream().filter(node -> id.equals(node.getSuperDirectory())).collect(Collectors.toList());
        for (TtFileDirectory child : children) {
            result.add(child);
            getChildren(child.getId(), list, result);
        }
    }

    private List<TtFileDirectory> getChildren(Long id, List<TtFileDirectory> list) {
        List<TtFileDirectory> result = new ArrayList<>();
        List<TtFileDirectory> children = list.stream()
                .filter(node -> id.equals(node.getSuperDirectory()))
                .collect(Collectors.toList());

        for (TtFileDirectory child : children) {
            result.add(child);
            result.addAll(getChildren(child.getId(), list));
        }

        return result;
    }


    /**
     * 递归获取指定目录下的所有目录树
     * 
     * @param parentId  父目录id
     * @param all   所有目录
     */
    private List<FileDirectoryTreeVo> getDireTree(Long parentId, List<TtFileDirectory> all) {
        List<FileDirectoryTreeVo> children = all.stream()
                .filter(node -> null == parentId ? null == node.getSuperDirectory() : parentId.equals(node.getSuperDirectory()))
                .map(item -> {
                    FileDirectoryTreeVo vo = new FileDirectoryTreeVo();
                    BeanUtils.copyProperties(item, vo);
                    vo.setChildren(getDireTree(item.getId(), all));
                    vo.setText(item.getDerectoryName());
                    vo.setValue(item.getId());

                    if (AppConfig.getDataFolder().equals(item.getDerectoryName())) {
                        vo.setOpened(true);
                    }

                    return vo;
                })
                .collect(Collectors.toList());
        return children;
    }


}



