package cc.smtweb.system.bpm.util;

import cc.smtweb.framework.core.db.jdbc.IdGenerator;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.mime.MimeType;
import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.mime.MimeTypes;
import org.springframework.web.multipart.MultipartFile;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * 文件名生成规则 subDir/[yyyymm]/[d]/[hex(fileid)]_[hex(rand)].[fileExt] 如果文件是图片格式，会生成缩略图，文件名会直接添加.thumb.jpg后缀 规则参数 yyyymm：
 * 时间的年月，固定6位字符。如200505 d： 时间的日期，值范围1~31。如5 fileid: 上传文件的ID，hex(int64) rand： 防盗链随机数，hex(int32)。 fileExt： 文件扩展名。
 */
@Slf4j
public class FilePathGenerator {

    public static final String THUMB_FILE_EXT = ".thumb.jpg";
    // 文件时间是否作为PK
    private SimpleDateFormat sdf;
    @Getter
    private String rootPath;
    private Map<String, FileFixPath> fileFxPathMap = new HashMap<>();
    private Map<String, FileDynPath> fileDynPathMap = new HashMap<>();
    private String fileUrl;
    private IdGenerator idGenerator;

    public FilePathGenerator(String rootPath, String fileUrl, IdGenerator idGenerator) {
        this.fileUrl = fixEnd(fileUrl);
        this.idGenerator = idGenerator;
        this.rootPath = fixEnd(rootPath);

        sdf = new SimpleDateFormat("yyyyMM/dd/");
    }

    private static String fixEnd(String path) {
        if (path.endsWith("/") || path.endsWith("\\")) {
            return path;
        } else {
            return path + "/";
        }
    }

    /**
     * 生成文件路径，根据日期分目录存储
     *
     * @param subPath          子目录，区分不同应用的文件
     * @param originalFileName 原始的文件名，用于提取扩展名用
     * @return 文件路径信息类
     */
    public FilePathInfo make(String subPath, String originalFileName) {
        return make(subPath, originalFileName, null, false);
    }

    public FilePathInfo make(String subPath, String originalFileName, boolean keepName) {
        return make(subPath, originalFileName, null, keepName);
    }

    /**
     * 生成文件路径，根据日期分目录存储
     *
     * @param subPath       子目录，区分不同应用的文件
     * @param multipartFile 上传文件流，用于提取扩展名用
     * @return 文件路径信息类
     */
    public FilePathInfo make(String subPath, MultipartFile multipartFile) {
        return make(subPath, multipartFile.getOriginalFilename(), multipartFile.getContentType(), false);
    }

    private synchronized FilePathInfo make(String subPath, String originFileName, String contentType, boolean keepName) {
        if (keepName) {
            FileFixPath filePathSub = fileFxPathMap.get(subPath);
            if (filePathSub == null) {
                filePathSub = new FileFixPath(this.rootPath, subPath);
                fileFxPathMap.put(subPath, filePathSub);
            }

            return filePathSub.makeDatePath(this.idGenerator.nextId(), originFileName);
        } else {
            FileDynPath filePathSub = fileDynPathMap.get(subPath);
            if (filePathSub == null) {
                filePathSub = new FileDynPath(this.rootPath, subPath, sdf);
                fileDynPathMap.put(subPath, filePathSub);
            }

            return filePathSub.makeDatePath(this.idGenerator.nextId(), ext(originFileName, contentType));
        }
    }

    private static String ext(String filename, String contentType) {
        int index = filename.lastIndexOf(".");

        if (index == -1) {
            if (contentType != null) {
                MimeTypes allTypes = MimeTypes.getDefaultMimeTypes();
                try {
                    MimeType jpeg = allTypes.forName(contentType);
                    return jpeg.getExtension();
                } catch (MimeTypeException e) {
                    log.error(contentType, e);
                }
            }

            return "";
        }

        return filename.substring(index);
    }

    // 根据数据库存储文件路径获取URL
    public String getFileUrl(FilePathInfo filePathInfo) {
        return this.fileUrl + filePathInfo.getMysqlFilePath();
    }

    // 根据数据库存储文件路径获取URL
    public String getFileUrl(String mysqlFilePath) {
        return this.fileUrl + mysqlFilePath;
    }

    // 根据数据库存储文件路径获取磁盘存储路径
    public String getFileDiskPath(String mysqlFilePath) {
        return this.rootPath + mysqlFilePath;
    }

    // 获取下载路径前缀
    public String getDownloadUrl() {
        return this.fileUrl;
    }
}
