package com.qmth.teachcloud.common.service.impl;

import com.alibaba.fastjson.JSON;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.teachcloud.common.bean.vo.FilePathVo;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SpringContextHolder;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.domain.FssDomain;
import com.qmth.teachcloud.common.entity.BasicAttachment;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.LocalCatalogEnum;
import com.qmth.teachcloud.common.enums.UploadFileEnum;
import com.qmth.teachcloud.common.service.BasicAttachmentService;
import com.qmth.teachcloud.common.service.FileUploadService;
import com.qmth.teachcloud.common.util.FileStoreUtil;
import com.qmth.teachcloud.common.util.FileUtil;
import com.qmth.teachcloud.common.util.ResultUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.StringJoiner;

/**
 * 文件上传服务类
 */
@Service
public class FileUploadServiceImpl implements FileUploadService {
    private static final Logger log = LoggerFactory.getLogger(FileUploadServiceImpl.class);

    @Resource
    private DictionaryConfig dictionaryConfig;
    @Resource
    private BasicAttachmentService basicAttachmentService;
    @Resource
    FileStoreUtil fileStoreUtil;

    /**
     * 上传文件
     *
     * @param sourceFile     上传文件
     * @param uploadFileEnum 文件类型
     * @param fileName       文件名称
     */
    @Override
    public FilePathVo uploadFile(File sourceFile, UploadFileEnum uploadFileEnum, String fileName) {
        try {
            boolean oss = dictionaryConfig.sysDomain().isOss();
            StringJoiner pdfStringJoiner = new StringJoiner("");
            String fssConfig = findFssConfig(oss, uploadFileEnum);
            if (StringUtils.isNotBlank(fssConfig)) {
                pdfStringJoiner.add(dictionaryConfig.fssPrivateDomain().getConfig()).add(File.separator);
            }
            pdfStringJoiner = getDateCatalog(pdfStringJoiner, uploadFileEnum, true);
            pdfStringJoiner.add(fileName);

            String uploadFileName = FileUtil.replaceSplit(pdfStringJoiner.toString());
            uploadFile(oss, uploadFileEnum, uploadFileName, sourceFile);

            String type = isOssUpload(oss, uploadFileEnum) ? SystemConstant.OSS : SystemConstant.LOCAL;
            return new FilePathVo(uploadFileName, uploadFileEnum, type, FileUtil.md5File(sourceFile));
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return null;
    }

    /**
     *
     * @param file
     * @param fileName
     * @param uploadFileEnum
     * @return
     */
    @Override
    public FilePathVo uploadFile(MultipartFile file, UploadFileEnum uploadFileEnum, String fileName, String md5) {
        try {
            boolean oss = dictionaryConfig.sysDomain().isOss();
            StringJoiner pdfStringJoiner = new StringJoiner("");
            String fssConfig = findFssConfig(oss, uploadFileEnum);
            if (StringUtils.isNotBlank(fssConfig)) {
                pdfStringJoiner.add(dictionaryConfig.fssPrivateDomain().getConfig()).add(File.separator);
            }
            pdfStringJoiner.add(uploadFileEnum.getTitle()).add(File.separator).add(fileName);
            String uploadFileName = FileUtil.replaceSplit(pdfStringJoiner.toString());
            uploadFile(oss, uploadFileEnum, uploadFileName, file.getInputStream(), md5);

            String type = isOssUpload(oss, uploadFileEnum) ? SystemConstant.OSS : SystemConstant.LOCAL;
            return new FilePathVo(uploadFileName, uploadFileEnum, type, md5);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return null;
    }

    /**
     *
     * @param file
     * @param fileName
     * @param uploadFileEnum
     * @return
     */
    @Override
    public FilePathVo uploadFile(InputStream inputStream, UploadFileEnum uploadFileEnum, String fileName, String md5) {
        try {
            boolean oss = dictionaryConfig.sysDomain().isOss();
            StringJoiner pdfStringJoiner = new StringJoiner("");
            String fssConfig = findFssConfig(oss, uploadFileEnum);
            if (StringUtils.isNotBlank(fssConfig)) {
                pdfStringJoiner.add(dictionaryConfig.fssPrivateDomain().getConfig()).add(File.separator);
            }
            pdfStringJoiner.add(uploadFileEnum.getTitle()).add(File.separator).add(fileName);
            String uploadFileName = FileUtil.replaceSplit(pdfStringJoiner.toString());
            uploadFile(oss, uploadFileEnum, uploadFileName, inputStream, md5);

            String type = isOssUpload(oss, uploadFileEnum) ? SystemConstant.OSS : SystemConstant.LOCAL;
            return new FilePathVo(uploadFileName, uploadFileEnum, type, md5);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return null;
    }

    /**
     * 获取附件文件
     *
     * @param path     文件路径json
     * @param pathName 文件名
     * @return string
     * @throws Exception
     */
    @Override
    public File downloadFile(String path, String pathName) throws Exception {
        if (StringUtils.isBlank(path)) {
            throw ExceptionResultEnum.ERROR.exception("附件文件路径不存在");
        }
        File file = new File(pathName);
        if (!file.exists()) {
            file.getParentFile().mkdirs();
            file.createNewFile();
        }
        FilePathVo filePathVo = JSON.parseObject(path, FilePathVo.class);
        if (filePathVo == null) {
            return null;
        }
        UploadFileEnum uploadFileEnum = filePathVo.getUploadType();
        if (StringUtils.isNotBlank(filePathVo.getType()) && Objects.equals(filePathVo.getType(), SystemConstant.OSS)) {
            file = fileStoreUtil.ossDownload(filePathVo.getPath(), file, uploadFileEnum.getFssType());
        } else {
            File sourceFile;
            String config = findFssConfig(isOssUpload(Objects.equals(filePathVo.getType(), SystemConstant.OSS), uploadFileEnum), uploadFileEnum);
            if (config.startsWith(SystemConstant.START_PARENT)) {
                sourceFile = new File(config, filePathVo.getPath());
            } else {
                sourceFile = new File(filePathVo.getPath());
            }
            FileUtils.copyFile(sourceFile, file);
        }
        return file;
    }

    /**
     * 获取附件文件
     *
     * @param path     文件路径json
     * @param pathName 文件名
     * @return string
     * @throws Exception
     */
    @Override
    public File downloadFile(String type, UploadFileEnum uploadFileEnum, String path, String pathName) throws Exception {
        if (StringUtils.isBlank(path)) {
            return null;
        }
        File file = new File(pathName);
        if (!file.exists()) {
            file.getParentFile().mkdirs();
            file.createNewFile();
        }
        if (StringUtils.isNotBlank(type) && Objects.equals(type, SystemConstant.OSS)) {
            file = fileStoreUtil.ossDownload(path, file, uploadFileEnum.getFssType());
        } else {
            File sourceFile;
            String config = findFssConfig(isOssUpload(Objects.equals(type, SystemConstant.OSS), uploadFileEnum), uploadFileEnum);
            if (config.startsWith(SystemConstant.START_PARENT)) {
                sourceFile = new File(config, path);
            } else {
                sourceFile = new File(path);
            }
            FileUtils.copyFile(sourceFile, file);
        }
        return file;
    }

    /**
     * 获取附件文件
     *
     * @param attachmentId 附件ID
     * @param pathName     保存路径
     * @return String
     * @throws Exception
     */
    @Override
    public File downloadFile(Long attachmentId, String pathName) throws Exception {
        return downloadFile(basicAttachmentService.getById(attachmentId), pathName);
    }

    /**
     * 获取附件文件
     *
     * @param attachment 附件
     * @param pathName   保存路径
     * @return String
     * @throws Exception
     */
    @Override
    public File downloadFile(BasicAttachment attachment, String pathName) throws Exception {
        if (attachment == null || StringUtils.isBlank(attachment.getPath())) {
            throw ExceptionResultEnum.ERROR.exception("附件文件保存数据不存在");
        }
        return downloadFile(attachment.getPath(), pathName);
    }

    /**
     * 预览文件
     *
     * @param path 文件路径
     */
    @Override
    public String filePreview(String path) {
        if (StringUtils.isBlank(path)) {
            return null;
        }
        String url;
        FilePathVo filePathVo = JSON.parseObject(path, FilePathVo.class);
        String filePath = filePathVo.getPath();
        if (StringUtils.isBlank(filePath)) {
            return null;
        }
        if (Objects.equals(filePathVo.getType(), SystemConstant.LOCAL)) {
            if (filePath.contains(dictionaryConfig.fssPrivateDomain().getServer())) {
                url = filePath.substring(filePath.indexOf(dictionaryConfig.fssPrivateDomain().getServer()), filePath.length());
            } else if (filePath.contains(dictionaryConfig.fssPublicDomain().getServer())) {
                url = filePath.substring(filePath.indexOf(dictionaryConfig.fssPublicDomain().getServer()), filePath.length());
            } else {
                url = fileStoreUtil.getPrivateUrl(filePath, filePathVo.getUploadType().getFssType());
            }
        } else {
            url = fileStoreUtil.getPrivateUrl(filePath, filePathVo.getUploadType().getFssType());
        }
        return FileUtil.replaceSplit(url);
    }

    /**
     * 预览文件
     *
     * @param path 文件路径
     */
    @Override
    public String filePreviewPrefix(String path) {
        if (StringUtils.isBlank(path)) {
            return null;
        }
        String prefix="";
        FilePathVo filePathVo = JSON.parseObject(path, FilePathVo.class);
        String filePath = filePathVo.getPath();
        if (StringUtils.isBlank(filePath)) {
            return null;
        }
        if (Objects.equals(filePathVo.getType(), SystemConstant.LOCAL)) {
            if (filePath.contains(dictionaryConfig.fssPrivateDomain().getServer())) {
//                url = filePath.substring(filePath.indexOf(dictionaryConfig.fssPrivateDomain().getServer()), filePath.length());
            } else if (filePath.contains(dictionaryConfig.fssPublicDomain().getServer())) {
//                url = filePath.substring(filePath.indexOf(dictionaryConfig.fssPublicDomain().getServer()), filePath.length());
            } else {
                prefix = fileStoreUtil.getPrivateUrlPrefix(filePath, filePathVo.getUploadType().getFssType());
            }
        } else {
            prefix = fileStoreUtil.getPrivateUrlPrefix(filePath, filePathVo.getUploadType().getFssType());
        }
        return FileUtil.replaceSplit(prefix);
    }

    /**
     * 预览文件
     *
     * @param attachmentId 附件ID
     */
    @Override
    public String filePreview(Long attachmentId) {
        if (attachmentId == null) {
            return null;
        }
        BasicAttachment basicAttachment = basicAttachmentService.getById(attachmentId);
        if (basicAttachment == null || StringUtils.isBlank(basicAttachment.getPath())) {
            return null;
        }
        return filePreview(basicAttachment.getPath());
    }

    /**
     * 获取oss配置路径
     *
     * @param oss            是否oss上传
     * @param uploadFileEnum 文件类型
     */
    private String findFssConfig(boolean oss, UploadFileEnum uploadFileEnum) {
        if (oss) {
            return null;
        }
        if ("private".equals(uploadFileEnum.getFssType())) {
            FssDomain fssDomain = dictionaryConfig.fssPrivateDomain();
            if (Objects.nonNull(fssDomain) && !StringUtils.isBlank(fssDomain.getConfig()) && !fssDomain.getConfig().startsWith(SystemConstant.START_PARENT)) {
                return fssDomain.getConfig();
            }
        } else if ("public".equals(uploadFileEnum.getFssType())) {
            FssDomain fssDomain = dictionaryConfig.fssPublicDomain();
            if (Objects.nonNull(fssDomain) && !StringUtils.isBlank(fssDomain.getConfig()) && !fssDomain.getConfig().startsWith(SystemConstant.START_PARENT)) {
                return fssDomain.getConfig();
            }
        } else {
            throw ExceptionResultEnum.ERROR.exception("fss类型异常");
        }
        return null;
    }

    /**
     * 是否为oss上传
     *
     * @param oss            oss上传配置
     * @param uploadFileEnum 文件类型
     */
    private boolean isOssUpload(boolean oss, UploadFileEnum uploadFileEnum) {
        if (oss) {
            return true;
        }
        if ("private".equals(uploadFileEnum.getFssType())) {
            FssDomain fssDomain = dictionaryConfig.fssPrivateDomain();
            return Objects.nonNull(fssDomain) && StringUtils.isNotBlank(fssDomain.getConfig()) && fssDomain.getConfig().startsWith(SystemConstant.START_PARENT);
        } else if ("public".equals(uploadFileEnum.getFssType())) {
            FssDomain fssDomain = dictionaryConfig.fssPublicDomain();
            return Objects.nonNull(fssDomain) && StringUtils.isNotBlank(fssDomain.getConfig()) && fssDomain.getConfig().startsWith(SystemConstant.START_PARENT);
        } else {
            throw ExceptionResultEnum.ERROR.exception("fss类型异常");
        }
    }

    /**
     * 上传文件
     *
     * @param oss            是否为oss上传
     * @param uploadFileEnum 文件类型
     * @param uploadFileName 上传文件名
     * @param file           文件
     */
    public void uploadFile(boolean oss, UploadFileEnum uploadFileEnum, String uploadFileName, File file) throws Exception {
        FileStoreUtil fileStoreUtil = SpringContextHolder.getBean(FileStoreUtil.class);
        if (isOssUpload(oss, uploadFileEnum)) {
            fileStoreUtil.ossUpload(uploadFileName, file, DigestUtils.md5Hex(new FileInputStream(file)), fileStoreUtil.getUploadEnumByPath(uploadFileName).getFssType());
        } else {
            fileStoreUtil.localUpload(uploadFileName, new FileInputStream(file), DigestUtils.md5Hex(new FileInputStream(file)), LocalCatalogEnum.LOCAL_PDF);
        }
    }
    public void uploadFile(boolean oss, UploadFileEnum uploadFileEnum, String uploadFileName, InputStream in, String md5) throws Exception {
        FileStoreUtil fileStoreUtil = SpringContextHolder.getBean(FileStoreUtil.class);
        if (isOssUpload(oss, uploadFileEnum)) {
            fileStoreUtil.ossUpload(uploadFileName, in, md5, fileStoreUtil.getUploadEnumByPath(uploadFileName).getFssType());
        } else {
            fileStoreUtil.localUpload(uploadFileName, in, md5, LocalCatalogEnum.LOCAL_PDF);
        }
    }

    /**
     * 按日期生成目录
     *
     * @param stringJoiner     stringJoiner
     * @param uploadFileEnum   文件类型
     * @param endFileSeparator 结尾是否为分隔符"/"
     */
    public static StringJoiner getDateCatalog(StringJoiner stringJoiner, UploadFileEnum uploadFileEnum, boolean endFileSeparator) {
        LocalDateTime nowTime = LocalDateTime.now();
        stringJoiner.add(uploadFileEnum.getTitle()).add(File.separator)
                .add(String.valueOf(nowTime.getYear())).add(File.separator)
                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getMonthValue())).add(File.separator)
                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getDayOfMonth()));
        if (endFileSeparator) {
            stringJoiner.add(File.separator);
        }
        return stringJoiner;
    }
}
