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

import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dao.TBAttachmentMapper;
import com.qmth.themis.business.entity.SysConfig;
import com.qmth.themis.business.entity.TBAttachment;
import com.qmth.themis.business.enums.UploadFileEnum;
import com.qmth.themis.business.service.ThemisCacheService;
import com.qmth.themis.business.service.TBAttachmentService;
import com.qmth.themis.business.util.JacksonUtil;
import com.qmth.themis.business.util.OssUtil;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.HexUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.IOException;
import java.math.BigDecimal;
import java.util.*;

/**
 * @Description: 附件 服务实现类
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020-04-17
 */
@Service
public class TBAttachmentServiceImpl extends ServiceImpl<TBAttachmentMapper, TBAttachment>
        implements TBAttachmentService {
    private final static Logger log = LoggerFactory.getLogger(TBAttachmentServiceImpl.class);

    @Resource
    OssUtil ossUtil;

    @Resource
    ThemisCacheService themisCacheService;

    /**
     * 保存附件
     *
     * @param file
     * @param md5
     * @param path
     * @param map
     * @param orgId
     * @param userId
     * @return
     * @throws IOException
     */
    @Override
    @Transactional
    public TBAttachment saveAttachment(MultipartFile file, String md5, String path, Map<String, Object> map,
                                       UploadFileEnum type, Long orgId, Long userId) throws IOException {
        log.info("ossMap:{}", JacksonUtil.parseJson(map));
        TBAttachment tbAttachment = null;
        try {
            String fileName = FilenameUtils.getBaseName(file.getOriginalFilename());
            String format = "." + FilenameUtils.getExtension(file.getOriginalFilename());

            SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.ATTACHMENT_TYPE);
            Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置附件类型"));
            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 new BusinessException("文件格式只能为" + attachmentTypeList.toString());
                }
            }
            if (Objects.nonNull(fileName) && fileName.length() > 64) {
                throw new BusinessException("文件名长度不能超过64个字符");
            }
            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);
            if (b.doubleValue() > 200) {
                throw new BusinessException("文件大小不能超过200MB");
            }
            log.info("fileName:{}", fileName);
            log.info("format:{}", format);
            log.info("size:{}", b);
            log.info("getOriginalFilename:{}", file.getOriginalFilename());
            String fileMd5 = DigestUtils.md5Hex(file.getBytes());
            log.info("fileMd5:{}", fileMd5);
            log.info("md5:{}", md5);
            if (Objects.isNull(md5)) {
                throw new BusinessException("request里的md5为空");
            }
            if (!Objects.equals(fileMd5, md5)) {
                throw new BusinessException("md5不一致");
            }
            tbAttachment = new TBAttachment(path, fileName, format, b, fileMd5);

            boolean oss = (boolean) map.get(SystemConstant.OSS);
            StringJoiner stringJoiner = new StringJoiner("");
            if (!oss) {
                stringJoiner.add(SystemConstant.FILES_DIR).add(File.separator);
            }

            String uploadType = String.valueOf(map.get(SystemConstant.UPLOAD_TYPE));
            if (Objects.isNull(uploadType)) {
                throw new BusinessException(ExceptionResultEnum.ATTACHMENT_TYPE_IS_NULL);
            } else if (Objects.equals(uploadType, UploadFileEnum.base_photo.name())) {
                stringJoiner.add(uploadType).add(File.separator).add(String.valueOf(orgId));
            } else if (Objects.equals(uploadType, UploadFileEnum.client.name())) {
                stringJoiner.add(uploadType).add(map.get(SystemConstant.VERSION).toString()).add(File.separator);
            } else if (Objects.equals(uploadType, UploadFileEnum.upload.name())) {
                stringJoiner.add(uploadType).add(File.separator);
            }
            JSONObject jsonObject = new JSONObject();
            stringJoiner = SystemConstant.getDirName(stringJoiner, false);
            if (oss) {//上传至oss
                jsonObject.put(SystemConstant.TYPE, SystemConstant.OSS);
                stringJoiner.add(File.separator).add(SystemConstant.getNanoId())
                        .add(tbAttachment.getType());
                ossUtil.upload(ossUtil.isPublic(type), stringJoiner.toString(), file.getInputStream(), md5);
            } else {//上传至服务器
                jsonObject.put(SystemConstant.TYPE, SystemConstant.LOCAL);
                File finalFile = new File(
                        stringJoiner.add(File.separator).add(SystemConstant.getNanoId())
                                .add(tbAttachment.getType()).toString());
                if (!finalFile.exists()) {
                    finalFile.getParentFile().mkdirs();
                    finalFile.createNewFile();
                }
                FileUtils.copyInputStreamToFile(file.getInputStream(), finalFile);
            }
            jsonObject.put(SystemConstant.PATH, stringJoiner.toString());
            jsonObject.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.valueOf(uploadType).ordinal());
            tbAttachment.setRemark(jsonObject.toJSONString());
            tbAttachment.setCreateId(userId);
            this.save(tbAttachment);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            deleteAttachment(map, type, tbAttachment);
            if (e instanceof BusinessException) {
                throw new BusinessException(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        return tbAttachment;
    }

    @Override
    @Transactional
    public TBAttachment saveAttachment(File file, String md5, String path, Map<String, Object> map, UploadFileEnum type,
                                       Long orgId, Long userId) throws IOException {
        log.info("ossMap:{}", JacksonUtil.parseJson(map));
        TBAttachment tbAttachment = null;
        try {
            int temp = file.getName().indexOf(".");
            String fileName = file.getName().substring(0, temp);
            String format = file.getName().substring(temp, file.getName().length());

            SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.ATTACHMENT_TYPE);
            Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置附件类型"));
            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 new BusinessException("文件格式只能为" + attachmentTypeList.toString());
                }
            }
            long size = file.length();
            BigDecimal b = BigDecimal.valueOf(size);
            BigDecimal num = BigDecimal.valueOf(1024);
            b = b.divide(num).divide(num).setScale(2, BigDecimal.ROUND_HALF_UP);
            if (b.doubleValue() > 200) {
                throw new BusinessException("文件大小不能超过200MB");
            }
            log.info("fileName:{}", fileName);
            log.info("format:{}", format);
            log.info("size:{}", b);
            log.info("md5:{}", md5);
            tbAttachment = new TBAttachment(path, fileName, format, b, md5);

            boolean oss = (boolean) map.get(SystemConstant.OSS);
            StringJoiner stringJoiner = new StringJoiner("");
            if (!oss) {
                stringJoiner.add(SystemConstant.FILES_DIR).add(File.separator);
            }

            String uploadType = String.valueOf(map.get(SystemConstant.UPLOAD_TYPE));
            if (Objects.isNull(uploadType)) {
                throw new BusinessException(ExceptionResultEnum.ATTACHMENT_TYPE_IS_NULL);
            } else if (Objects.equals(uploadType, UploadFileEnum.base_photo.name())) {
                stringJoiner.add(uploadType).add(File.separator).add(String.valueOf(orgId));
            } else {
                stringJoiner.add(uploadType);
            }
            stringJoiner = SystemConstant.getDirName(stringJoiner, false);
            if (oss) {//上传至oss
                stringJoiner.add(File.separator).add(SystemConstant.getNanoId())
                        .add(tbAttachment.getType());
                ossUtil.upload(ossUtil.isPublic(type), stringJoiner.toString(), file);
            } else {//上传至服务器
                File finalFile = new File(
                        stringJoiner.add(File.separator).add(SystemConstant.getNanoId())
                                .add(tbAttachment.getType()).toString());
                if (!finalFile.exists()) {
                    finalFile.getParentFile().mkdirs();
                    finalFile.createNewFile();
                }
                FileUtils.copyFile(file, finalFile);
            }
            JSONObject jsonObject = new JSONObject();
            if (oss) {
                jsonObject.put(SystemConstant.TYPE, SystemConstant.OSS);
            } else {
                jsonObject.put(SystemConstant.TYPE, SystemConstant.LOCAL);
            }
            jsonObject.put(SystemConstant.PATH, stringJoiner.toString());
            jsonObject.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.valueOf(uploadType).ordinal());
            tbAttachment.setRemark(jsonObject.toJSONString());
            tbAttachment.setCreateId(userId);
            this.save(tbAttachment);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            deleteAttachment(map, type, tbAttachment);
            if (e instanceof BusinessException) {
                throw new BusinessException(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        return tbAttachment;
    }

    @Override
    public TBAttachment saveAttachment(String path, Map<String, Object> map, UploadFileEnum type, Long orgId,
                                       Long userId) throws IOException {
        return null;
    }

    /**
     * 删除附件
     *
     * @param map
     * @param tbAttachment
     */
    @Override
    public void deleteAttachment(Map<String, Object> map, UploadFileEnum fileType, TBAttachment tbAttachment) {
        if (Objects.nonNull(tbAttachment) && Objects.nonNull(tbAttachment.getRemark())) {
            JSONObject jsonObject = JSONObject.parseObject(tbAttachment.getRemark());
            String type = String.valueOf(jsonObject.get(SystemConstant.TYPE));
            if (Objects.nonNull(type) && Objects.equals(type, SystemConstant.OSS)) {//删除阿里云附件
                ossUtil.delete(ossUtil.isPublic(fileType), jsonObject.get(SystemConstant.PATH).toString());
            } else {//删除服务器附件
                File file = new File(jsonObject.get(SystemConstant.PATH).toString());
                file.delete();
            }
        }
    }

    /**
     * 批量删除附件
     *
     * @param map
     * @param list
     */
    @Override
    public void batchDeleteAttachment(Map<String, Object> map, UploadFileEnum fileType, List<Map> list) {
        List<String> paths = null;
        String type = null;
        if (Objects.nonNull(map)) {
            paths = new ArrayList<>();
            for (Map m : list) {
                JSONObject jsonObject = JSONObject.parseObject((String) m.get(SystemConstant.REMARK));
                type = (String) jsonObject.get(SystemConstant.TYPE);
                paths.add((String) jsonObject.get(SystemConstant.PATH));
            }
        }
        if (Objects.nonNull(type) && Objects.equals(type, SystemConstant.OSS)) {//删除阿里云附件
            ossUtil.batchDelete(ossUtil.isPublic(fileType), paths);
        } else if (Objects.nonNull(type) && Objects.equals(type, SystemConstant.LOCAL)) {//删除服务器附件
            for (String s : paths) {
                File file = new File(s);
                file.delete();
            }
        }
    }
}