package com.philisense.www.cloud;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharUtil;
import com.philisense.www.constant.Constants;
import com.philisense.www.enums.SliceTypeEnum;
import com.philisense.www.exception.StorageException;
import com.philisense.www.utils.StorageUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.util.*;

/**
 * @description: 本地文件存储
 */
@Component
public class localStorageService extends OssStorageService {

    private static Logger log = LogManager.getLogger(localStorageService.class);

    public localStorageService(OssStorageConfig config) {
        this.config = config;
    }

    @Override
    public String upload(InputStream inputStream, String path) {
        File file = FileUtil.file(config.getUploadFolder() + File.separator + path);
        //目录是否存在
        if (!FileUtil.exist(file.getParent())) {
            FileUtil.mkdir(file.getParent());
        }
        FileUtil.writeFromStream(inputStream, file);
        return config.getDomain() + Constants.RESOURCE_PREFIX + path;
    }

    @Override
    public String upload(byte[] data, String path) {
        File file = FileUtil.file(config.getUploadFolder() + File.separator + path);
        //目录是否存在
        if (!FileUtil.exist(file.getParent())) {
            FileUtil.mkdir(file.getParent());
        }
        FileUtil.writeBytes(data, file);
        return config.getDomain() + Constants.RESOURCE_PREFIX + path;
    }

    @Override
    public void delete(String path) {
        try {
            FileUtil.del(config.getUploadFolder() + File.separator + path);
            int i = path.indexOf("/");
            String folderPath = path.substring(0, i);
            StorageUtils.clear(new File(config.getUploadFolder() + File.separator + folderPath));
        } catch (Exception e) {
            throw new StorageException("删除失败", e);
        }
    }

    @Override
    public String getFileUrl(String path) {
        return config.getDomain() + Constants.RESOURCE_PREFIX + path;
    }

    @Override
    public Map<String, Object> chunksCheck(String path, String name, String hash, Integer fileSize, Integer total) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 上传的文件哈希文件夹加名
            String chunksPath = config.getUploadFolder() + File.separator + path + File.separator + hash + CharUtil.UNDERLINE + fileSize + CharUtil.SLASH;
            String filePath = config.getUploadFolder() + File.separator + path + File.separator + name;
            if (FileUtil.exist(filePath)) {
                // 文件已存在
                result.put("type", SliceTypeEnum.UPLOADSPEED.getValue());
                result.put("msg", SliceTypeEnum.UPLOADSPEED.getText());
            }else {
                if(FileUtil.exist(chunksPath)) {
                    File folder = FileUtil.file(chunksPath);
                    // 存在文件切片文件夹，上传没有上传完
                    // 上次没有上传完成，找到以及上传的切片
                    Integer sum = Objects.requireNonNull(folder.listFiles()).length;
                    if (sum.equals(total)) {
                        // 切片上传完了，没有合并
                        result.put("type", SliceTypeEnum.NOMERGE.getValue());
                        result.put("msg", SliceTypeEnum.NOMERGE.getText());
                    }else {
                        // 切片没有上传完
                        result.put("type", SliceTypeEnum.BREAKPOINTRESUME.getValue());
                        result.put("sum", sum);
                        result.put("msg", SliceTypeEnum.BREAKPOINTRESUME.getText());
                    }
                }else {
                    // 没有这个文件的切片和文件
                    result.put("type", SliceTypeEnum.NEVERUPLOAD.getValue());
                    result.put("msg", SliceTypeEnum.NEVERUPLOAD.getText());
                }
            }
            return result;
        } catch (Exception e) {
            throw new StorageException("大文件上传检查文件失败", e);
        }

    }

    @Override
    public Map<String, Object> chunksUpload(MultipartFile file, String path, String hash, String uploadId, Integer index, String fileSize) {
        try {
            String chunksPath = config.getUploadFolder() + File.separator + path + File.separator + hash + CharUtil.UNDERLINE + fileSize + CharUtil.SLASH;

            if (!FileUtil.exist(chunksPath)) {
                FileUtil.mkdir(chunksPath);
            }
            byte[] bytes = file.getBytes();
            Path paths = Paths.get(chunksPath + hash + CharUtil.UNDERLINE + index);
            Files.write(paths, bytes);
        } catch (Exception e) {
            throw new StorageException("上传切片失败", e);
        }
        return null;
    }

    @Override
    public String chunksMerge(String path, String name, String hash, String uploadId, String fileSize) {
        String url = "";
        try {
            // 根据hash值，获取分片文件。
            String chunksPath = config.getUploadFolder() + File.separator + path + File.separator + hash + CharUtil.UNDERLINE + fileSize + CharUtil.SLASH;
            String filePath = config.getUploadFolder() + File.separator + path + File.separator + name;

            // 读取所有的chunks 文件名存放在数组中, 并进行排序
            merge(filePath, chunksPath, name);
            url = config.getDomain()  + Constants.RESOURCE_PREFIX + "/" + path + "/" + name;
        } catch (Exception e) {
            throw new StorageException("合并切片失败", e);
        }
        return url;
    }

    /**
     * 合并
     * @param file
     * @param folder
     * @param filename
     * @return 状态
     */
    public static void merge(String file, String folder, String filename){
        try {
            //先判断文件是否存在
            if(!FileUtil.exist(file)) {
                //不存在的话，进行合并
                Files.createFile(Paths.get(file));
                Files.list(Paths.get(folder))
                        .filter(path -> !path.getFileName().toString().equals(filename))
                        .sorted((o1, o2) -> {
                            String p1 = o1.getFileName().toString();
                            String p2 = o2.getFileName().toString();
                            int i1 = p1.lastIndexOf(CharUtil.UNDERLINE) + 1;
                            int i2 = p2.lastIndexOf(CharUtil.UNDERLINE) + 1;
                            return Integer.valueOf(p2.substring(i2)).compareTo(Integer.valueOf(p1.substring(i1)));
                        })
                        .forEach(path -> {
                            try {
                                //以追加的形式写入文件
                                Files.write(Paths.get(file), Files.readAllBytes(path), StandardOpenOption.APPEND);
                                //合并后删除该块
                                Files.delete(path);
                            } catch (IOException e) {
                                log.error("删除文件失败：{},{}",e.getMessage(), e);
                            }
                        });

                // 删除文件夹
                File file1 = new File(folder);
                if (file1.isDirectory() && file1.list().length ==  0) {
                    file1.delete();
                }
            }
        } catch (Exception e) {
            log.error("合并失败：{},{}",e.getMessage(), e);
            //合并失败
            FileUtil.del(file);
            throw new StorageException("合并失败", e);
        }
    }

}
