package com.qmth.sop.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.sop.business.bean.dto.AttachmentInfoDto;
import com.qmth.sop.business.cache.CommonCacheService;
import com.qmth.sop.business.entity.BasicAttachment;
import com.qmth.sop.business.entity.SysConfig;
import com.qmth.sop.business.entity.SysUser;
import com.qmth.sop.business.mapper.BasicAttachmentMapper;
import com.qmth.sop.business.service.BasicAttachmentService;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.ExceptionResultEnum;
import com.qmth.sop.common.enums.UploadFileEnum;
import com.qmth.sop.common.util.FileStoreUtil;
import com.qmth.sop.common.util.ServletUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

/**
 * <p>
 * 所有附件记录表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2023-07-17
 */
@Service
public class BasicAttachmentServiceImpl extends ServiceImpl<BasicAttachmentMapper, BasicAttachment> implements BasicAttachmentService {

    @Resource
    CommonCacheService commonCacheService;

    @Resource
    FileStoreUtil fileStoreUtil;

    /**
     * 验证附件
     *
     * @param file
     * @return
     * @throws IOException
     */
    @Override
    public AttachmentInfoDto validateAttachment(File file) throws IOException {
        String fileName = FilenameUtils.getBaseName(file.getName());
        String format = "." + FilenameUtils.getExtension(file.getName());

        SysConfig sysConfig = commonCacheService.addSysConfigCache(SystemConstant.ATTACHMENT_TYPE);
        Optional.ofNullable(sysConfig).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未配置附件类型"));
        List<String> attachmentTypeList = Arrays.asList(sysConfig.getConfigValue().replaceAll("\\[", "").replaceAll("\\]", "").split(","));
        if (Objects.nonNull(format)) {
            long count = attachmentTypeList.stream().filter(s -> format.equalsIgnoreCase(s)).count();
            if (count == 0) {
                throw ExceptionResultEnum.ERROR.exception("文件格式只能为" + attachmentTypeList.toString());
            }
        }

        SysConfig sysConfigLength = commonCacheService.addSysConfigCache(SystemConstant.ATTACHMENT_LENGTH);
        Optional.ofNullable(sysConfigLength).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未配置附件名称长度"));
        int attachmentLength = Integer.parseInt(sysConfigLength.getConfigValue());
        if (Objects.nonNull(fileName) && fileName.length() > attachmentLength) {
            throw ExceptionResultEnum.ERROR.exception("文件名长度不能超过" + attachmentLength + "个字符");
        }
        long size = file.length();
        BigDecimal b = BigDecimal.valueOf(size);
        BigDecimal num = BigDecimal.valueOf(1024);
        b = b.divide(num, 2, BigDecimal.ROUND_HALF_UP).divide(num, 2, BigDecimal.ROUND_HALF_UP)
                .setScale(2, BigDecimal.ROUND_HALF_UP);

        SysConfig sysConfigSize = commonCacheService.addSysConfigCache(SystemConstant.ATTACHMENT_SIZE);
        Optional.ofNullable(sysConfigSize).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未配置附件大小"));
        double attachmentSize = Double.valueOf(sysConfigSize.getConfigValue());
        if (b.doubleValue() > attachmentSize) {
            throw ExceptionResultEnum.ERROR.exception("文件大小不能超过" + attachmentSize + "MB");
        }
        return new AttachmentInfoDto(fileName, format, b, DigestUtils.md5Hex(new FileInputStream(file)));
    }

    /**
     * 验证附件
     *
     * @param file
     * @return
     * @throws IOException
     */
    @Override
    public AttachmentInfoDto validateAttachment(MultipartFile file) throws IOException {
        String fileName = FilenameUtils.getBaseName(file.getOriginalFilename());
        String format = "." + FilenameUtils.getExtension(file.getOriginalFilename());

        SysConfig sysConfig = commonCacheService.addSysConfigCache(SystemConstant.ATTACHMENT_TYPE);
        Optional.ofNullable(sysConfig).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未配置附件类型"));
        List<String> attachmentTypeList = Arrays.asList(sysConfig.getConfigValue().replaceAll("\\[", "").replaceAll("\\]", "").split(","));
        if (Objects.nonNull(format)) {
            long count = attachmentTypeList.stream().filter(s -> format.equalsIgnoreCase(s)).count();
            if (count == 0) {
                throw ExceptionResultEnum.ERROR.exception("文件格式只能为" + attachmentTypeList.toString());
            }
        }

        SysConfig sysConfigLength = commonCacheService.addSysConfigCache(SystemConstant.ATTACHMENT_LENGTH);
        Optional.ofNullable(sysConfigLength).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未配置附件名称长度"));
        int attachmentLength = Integer.parseInt(sysConfigLength.getConfigValue());
        if (Objects.nonNull(fileName) && fileName.length() > attachmentLength) {
            throw ExceptionResultEnum.ERROR.exception("文件名长度不能超过" + attachmentLength + "个字符");
        }
        long size = file.getSize();
        BigDecimal b = BigDecimal.valueOf(size);
        BigDecimal num = BigDecimal.valueOf(1024);
        b = b.divide(num, 2, BigDecimal.ROUND_HALF_UP).divide(num, 2, BigDecimal.ROUND_HALF_UP)
                .setScale(2, BigDecimal.ROUND_HALF_UP);

        SysConfig sysConfigSize = commonCacheService.addSysConfigCache(SystemConstant.ATTACHMENT_SIZE);
        Optional.ofNullable(sysConfigSize).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未配置附件大小"));
        double attachmentSize = Double.valueOf(sysConfigSize.getConfigValue());
        if (b.doubleValue() > attachmentSize) {
            throw ExceptionResultEnum.ERROR.exception("文件大小不能超过" + attachmentSize + "MB");
        }
        String fileMd5 = DigestUtils.md5Hex(file.getBytes());
        String md5 = ServletUtil.getRequestMd5();
        if (!Objects.equals(fileMd5, md5)) {
            throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
        }
        return new AttachmentInfoDto(fileName, format, b, fileMd5);
    }

    /**
     * 上传文件接口
     *
     * @param file
     * @param type
     * @return
     * @throws Exception
     */
    @Override
    public BasicAttachment saveAttachment(File file, UploadFileEnum type) throws Exception {
        BasicAttachment basicAttachment = null;
        AttachmentInfoDto attachmentInfoDto = this.validateAttachment(file);
        if (Objects.nonNull(attachmentInfoDto)) {
            String fileName = SystemConstant.getNanoId();
            StringJoiner stringJoiner = SystemConstant.getDirName(type, true).add(fileName).add(attachmentInfoDto.getFormat());
            String path = stringJoiner.toString().replaceAll("\\\\", SystemConstant.ORG_SPLIT);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(SystemConstant.TYPE, fileStoreUtil.uploadFileEnumIsOss(type) ? SystemConstant.OSS : SystemConstant.LOCAL);
            fileStoreUtil.ossUpload(path, new FileInputStream(file), attachmentInfoDto.getMd5(), type.getFssType());
            jsonObject.put(SystemConstant.UPLOAD_TYPE, type);
            jsonObject.put(SystemConstant.PATH, path);
            attachmentInfoDto.setFileName(fileName);
            basicAttachment = new BasicAttachment(jsonObject.toJSONString(), attachmentInfoDto, null, null);
            this.save(basicAttachment);
        }
        Optional.ofNullable(basicAttachment).orElseThrow(() -> ExceptionResultEnum.ATTACHMENT_ERROR.exception());
        return basicAttachment;
    }

    /**
     * 上传文件接口
     *
     * @param file
     * @param type
     * @param key
     * @return
     * @throws IOException
     */
    @Override
    @Transactional
    public BasicAttachment saveAttachment(MultipartFile file, UploadFileEnum type, String key) throws Exception {
        BasicAttachment basicAttachment = null;
        AttachmentInfoDto attachmentInfoDto = this.validateAttachment(file);
        if (Objects.nonNull(attachmentInfoDto)) {
            StringJoiner stringJoiner = SystemConstant.getDirName(type, true).add(SystemConstant.getNanoId()).add(attachmentInfoDto.getFormat());
            String path = stringJoiner.toString().replaceAll("\\\\", SystemConstant.ORG_SPLIT);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(SystemConstant.TYPE, fileStoreUtil.uploadFileEnumIsOss(type) ? SystemConstant.OSS : SystemConstant.LOCAL);
            fileStoreUtil.ossUpload(path, file.getInputStream(), attachmentInfoDto.getMd5(), type.getFssType());
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            jsonObject.put(SystemConstant.UPLOAD_TYPE, type);
            jsonObject.put(SystemConstant.PATH, path);
            basicAttachment = new BasicAttachment(jsonObject.toJSONString(), attachmentInfoDto, sysUser.getId(), key);
            this.save(basicAttachment);
        }
        Optional.ofNullable(basicAttachment).orElseThrow(() -> ExceptionResultEnum.ATTACHMENT_ERROR.exception());
        return basicAttachment;
    }

    /**
     * 上传文件接口
     *
     * @param file
     * @param type
     * @param key
     * @return
     * @throws Exception
     */
    @Override
    public BasicAttachment saveAttachmentNoAuth(MultipartFile file, UploadFileEnum type, String key) throws Exception {
        BasicAttachment basicAttachment = null;
        AttachmentInfoDto attachmentInfoDto = this.validateAttachment(file);
        if (Objects.nonNull(attachmentInfoDto)) {
            StringJoiner stringJoiner = SystemConstant.getDirName(type, true).add(SystemConstant.getNanoId()).add(attachmentInfoDto.getFormat());
            String path = stringJoiner.toString().replaceAll("\\\\", SystemConstant.ORG_SPLIT);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(SystemConstant.TYPE, fileStoreUtil.uploadFileEnumIsOss(type) ? SystemConstant.OSS : SystemConstant.LOCAL);
            fileStoreUtil.ossUpload(path, file.getInputStream(), attachmentInfoDto.getMd5(), type.getFssType());
            jsonObject.put(SystemConstant.UPLOAD_TYPE, type);
            jsonObject.put(SystemConstant.PATH, path);
            basicAttachment = new BasicAttachment(jsonObject.toJSONString(), attachmentInfoDto, null, key);
            this.save(basicAttachment);
        }
        Optional.ofNullable(basicAttachment).orElseThrow(() -> ExceptionResultEnum.ATTACHMENT_ERROR.exception());
        return basicAttachment;
    }

    /**
     * 删除附件
     *
     * @param basicAttachment
     */
    @Override
    public void deleteAttachment(BasicAttachment basicAttachment) throws Exception {
        if (Objects.nonNull(basicAttachment) && Objects.nonNull(basicAttachment.getPath())) {
            JSONObject jsonObject = JSONObject.parseObject(basicAttachment.getPath());
            fileStoreUtil.ossDelete((String) jsonObject.get(SystemConstant.PATH), UploadFileEnum.valueOf((String) jsonObject.get(SystemConstant.UPLOAD_TYPE)).getFssType());
        }
    }

    /**
     * 文件预览
     *
     * @param path
     * @return
     */
    @Override
    public String filePreview(String path) throws Exception {
        String url = null;
        JSONObject jsonObject = JSONObject.parseObject(path);
        UploadFileEnum uploadFileEnum = Enum.valueOf(UploadFileEnum.class, (String) jsonObject.get(SystemConstant.UPLOAD_TYPE));
        String attachmentType = (String) jsonObject.get(SystemConstant.TYPE);
        String filePath = (String) jsonObject.get(SystemConstant.PATH);
        if (Objects.equals(attachmentType, SystemConstant.LOCAL)) {
            url = fileStoreUtil.getDictionaryConfig().fssPrivateDomain().getServer() + filePath;
        } else {
            if (uploadFileEnum == UploadFileEnum.UPLOAD) {
                url = fileStoreUtil.getPrivateUrl(filePath, uploadFileEnum.getFssType());
            } else {
                url = fileStoreUtil.getDictionaryConfig().fssPublicDomain().getServer() + SystemConstant.ORG_SPLIT + filePath;
            }
        }
        return url;
    }

    /**
     * 文件预览
     *
     * @param key
     * @return
     * @throws Exception
     */
    @Override
    public List<BasicAttachment> filePreviewApp(String key) throws Exception {
        return this.list(new QueryWrapper<BasicAttachment>().lambda().eq(BasicAttachment::getRemark, key).last(" limit 10"));
    }

    /**
     * 文件预览
     *
     * @param attachmentId
     * @return
     */
    @Override
    public String filePreview(Long attachmentId) throws Exception {
        BasicAttachment basicAttachment = this.getById(attachmentId);
        Optional.ofNullable(basicAttachment).orElseThrow(() -> ExceptionResultEnum.ATTACHMENT_NO_DATA.exception());
        return this.filePreview(basicAttachment.getPath());
    }

    /**
     * 文件预览
     *
     * @param basicAttachment
     * @return
     * @throws Exception
     */
    @Override
    public String filePreview(BasicAttachment basicAttachment) throws Exception {
        return this.filePreview(basicAttachment.getPath());
    }

    @Override
    public String filePreviewPath(String filePath) {
        return fileStoreUtil.getDictionaryConfig().fssPublicDomain().getServer() + SystemConstant.ORG_SPLIT + filePath;
    }
}
