package cn.reghao.oss.store.service;

import cn.reghao.jutil.jdk.store.LoadBalancer;
import cn.reghao.jutil.jdk.store.LocalStore;
import cn.reghao.jutil.jdk.store.LocalStores;
import cn.reghao.jutil.jdk.store.StoreDir;
import cn.reghao.oss.store.config.props.OssProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;

import static java.nio.file.StandardOpenOption.*;

/**
 * @author reghao
 * @date 2022-04-26 15:09:06
 */
@Slf4j
@Service
public class FileStoreService {
    private final LoadBalancer loadBalancer;
    private final OssProperties ossProperties;

    public FileStoreService(OssProperties ossProperties) {
        this.loadBalancer = new LoadBalancer();
        this.ossProperties = ossProperties;
    }

    @PostConstruct
    public void initLocalStore() throws IOException {
        log.info("初始化本地磁盘...");
        List<String> diskDirs = ossProperties.getDiskDirs();
        for (String diskDir : diskDirs) {
            File dir = new File(diskDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
        }
        LocalStores.init(diskDirs);

        for (String diskDir : diskDirs) {
            Map<String, Integer> map = new HashMap<>();
            /*List<SubDirCount> list = dataBlockMapper.findSubDirCount("");
            list.forEach(subDirCount -> {
                String relativeDir = subDirCount.getRelativeDir();
                String storeDir = diskDir + relativeDir;
                File file = new File(storeDir);

                int total = subDirCount.getTotal();
                map.put(file.getAbsolutePath(), total);
            });*/

            LocalStores.initStoreDirs(diskDir, map);
        }
        log.info("本地磁盘数据初始化完成...");
    }

    public List<LocalStore> getStoreDisks() {
        return LocalStores.getLocalStores();
    }

    public String genFilePath(String contentId, long size, String suffix) {
        StoreDir storeDir = loadBalancer.getStoreDir(size);
        String fileDir = storeDir.getBaseDir();
        return String.format("%s/%s%s", fileDir, contentId, suffix);
    }

    public void createSparseFile(String absolutePath, long len) throws IOException {
        File file = new File(absolutePath);
        FileUtils.forceMkdirParent(file);
        // 创建一个稀疏文件
        Files.newByteChannel(Paths.get(absolutePath), EnumSet.of(CREATE_NEW, WRITE, SPARSE));
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        raf.setLength(len);
        raf.close();
    }

    public void writeToFile(InputStream in, String absolutePath, long pos) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(absolutePath, "rw");
        raf.seek(pos);
        byte[] buf = new byte[1024*1024];
        int len;
        while ((len = in.read(buf)) != -1) {
            raf.write(buf, 0, len);
        }
        raf.close();
        in.close();
    }

    public File saveFile(byte[] bytes, String contentId) throws IOException {
        String absolutePath = genFilePath(contentId, bytes.length, "");
        File file = new File(absolutePath);
        if (file.exists()) {
            throw new IOException(absolutePath + " exist");
        }

        File parentDir = file.getParentFile();
        if (!parentDir.exists()) {
            FileUtils.forceMkdir(parentDir);
        }

        FileOutputStream fos = new FileOutputStream(file);
        fos.write(bytes);
        fos.close();
        return file;
    }

    public File saveFile(File srcFile, String contentId) throws IOException {
        String absolutePath = genFilePath(contentId, srcFile.length(), "");
        File file = new File(absolutePath);
        if (file.exists()) {
            throw new IOException(absolutePath + " exist");
        }

        Files.move(Path.of(srcFile.getAbsolutePath()), Path.of(absolutePath), StandardCopyOption.REPLACE_EXISTING);
        return file;
    }

    public File saveFile(InputStream inputStream, String contentId, long size) throws IOException {
        String absolutePath = genFilePath(contentId, size, "");
        File file = new File(absolutePath);
        if (file.exists()) {
            throw new IOException(absolutePath + " exist");
        }

        Files.copy(inputStream, Path.of(absolutePath), StandardCopyOption.REPLACE_EXISTING);
        return file;
    }

    public void saveFile(InputStream inputStream, String absolutePath) throws IOException {
        File file = new File(absolutePath);
        if (file.exists()) {
            throw new IOException(absolutePath + " exist");
        }

        File parentDir = file.getParentFile();
        if (!parentDir.exists()) {
            FileUtils.forceMkdir(parentDir);
        }

        FileOutputStream fos = new FileOutputStream(file);
        // 1MiB
        int len = 1024*1024;
        byte[] buf = new byte[len];
        int readLen;
        while ((readLen = inputStream.read(buf, 0, len)) != -1) {
            fos.write(buf, 0, readLen);
        }
        fos.close();
        inputStream.close();
    }
}
