package com.qmth.boot.core.fss.store.impl;

import com.qmth.boot.core.exception.NotFoundException;
import com.qmth.boot.core.fss.store.FileStore;
import com.qmth.boot.core.fss.utils.FssSigner;
import com.qmth.boot.tools.crypto.AES;
import com.qmth.boot.tools.io.IOUtils;
import com.qmth.boot.tools.models.ByteArray;
import com.qmth.boot.tools.uuid.FastUUID;
import org.springframework.util.Assert;

import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import java.io.*;
import java.nio.file.Files;
import java.time.Duration;

/**
 * 本地磁盘文件管理工具
 */
public class DiskStore implements FileStore {

    private String server;

    private File rootDir;

    private File tempDir;

    private FssSigner fssSigner;

    private String cipherKey;

    public DiskStore(String rootPath, String server, String temp, FssSigner fssSigner, String cipherKey) {
        this.fssSigner = fssSigner;
        this.cipherKey = cipherKey;
        this.server = server;
        this.rootDir = new File(rootPath);
        this.tempDir = new File(temp);
        if (!rootDir.exists() && !rootDir.mkdirs()) {
            //自动创建目录失败
            throw new IllegalArgumentException("Fss disk store: " + rootPath + ": auto mkdir faile");
        } else if (rootDir.isFile()) {
            //判断是否不是目录
            throw new IllegalArgumentException("Fss disk store: " + rootPath + ": is a file");
        }
        if (!tempDir.exists() && !tempDir.mkdirs()) {
            //自动创建目录失败
            throw new IllegalArgumentException("Fss disk store: temp auto mkdir faile");
        } else if (tempDir.isFile()) {
            //判断是否不是目录
            throw new IllegalArgumentException("Fss disk store: temp dir is a file");
        }
    }

    @Override
    public String getServer() {
        return server;
    }

    @Override
    public FssSigner getFssSigner() {
        return fssSigner;
    }

    @Override
    public String getServerUrl(String path, Duration expireDuration) {
        path = formatPath(path);
        String url = getServer().concat(path);
        if (fssSigner != null) {
            Assert.notNull(expireDuration, "expireDuration is required");
            url = url.concat("?").concat(fssSigner.buildUrlParam(path, expireDuration));
        }
        return url;
    }

    @Override
    public void write(String path, InputStream ins, String md5) throws Exception {
        path = formatPath(path);
        //临时目录创建临时文件
        File tempFile = new File(tempDir, FastUUID.get());
        try {
            //final MessageDigest digest = CodecUtils.getMd5();
            FileOutputStream ous = new FileOutputStream(tempFile);
            IOUtils.copy(ins, ous);
            //IOUtils.copy(ins, ous, (buffer, length) -> digest.update(buffer, 0, length));
            //校验写入内容的摘要信息
            //if (!CodecUtils.toBase64(digest.digest()).equalsIgnoreCase(toBase64(md5))) {
            //throw new RuntimeException("Write md5 validate faile");
            //}
            IOUtils.closeQuietly(ins);
            IOUtils.closeQuietly(ous);
            //校验写入内容的摘要信息
            if (!ByteArray.md5(tempFile).toBase64().equalsIgnoreCase(toBase64(md5))) {
                throw new RuntimeException("Write md5 validate faile");
            }
            //检查正式文件及目录
            File targetFile = new File(rootDir, path);
            if (targetFile.isDirectory()) {
                throw new RuntimeException("Target file is directory: " + path);
            }
            targetFile.getParentFile().mkdirs();
            //加密模式下，正式文件写入流需要加密
            if (cipherKey != null) {
                OutputStream outputStream = new CipherOutputStream(new FileOutputStream(targetFile),
                        AES.getEncryptCipher(cipherKey));
                Files.copy(tempFile.toPath(), outputStream);
                IOUtils.closeQuietly(outputStream);
            }
            //临时文件拷贝到正式文件
            else {
                IOUtils.copy(tempFile, targetFile);
            }
        } finally {
            tempFile.delete();
        }
    }

    @Override
    public InputStream read(String path) throws Exception {
        path = formatPath(path);
        File file = new File(rootDir, path);
        if (file.exists() && file.isFile()) {
            InputStream inputStream = new FileInputStream(file);
            if (cipherKey != null) {
                inputStream = new CipherInputStream(inputStream, AES.getDecryptCipher(cipherKey));
            }
            return inputStream;
        } else {
            throw new NotFoundException("Read file unexist:" + path);
        }
    }

    @Override
    public boolean exist(String path) {
        path = formatPath(path);
        File file = new File(rootDir, path);
        return file.exists() && file.isFile();
    }

    @Override
    public boolean delete(String path) {
        path = formatPath(path);
        File file = new File(rootDir, path);
        if (file.exists() && file.isFile()) {
            return file.delete();
        } else {
            throw new NotFoundException(path + " not exist");
        }
    }

    @Override
    public void copy(String source, String target) throws Exception {
        source = formatPath(source);
        target = formatPath(target);
        File targetFile = new File(rootDir, target);
        targetFile.getParentFile().mkdirs();
        IOUtils.copy(new File(rootDir, source), targetFile);
    }

}
