/**
 * BladeX Commercial License Agreement
 * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
 * <p>
 * Use of this software is governed by the Commercial License Agreement
 * obtained after purchasing a license from BladeX.
 * <p>
 * 1. This software is for development use only under a valid license
 * from BladeX.
 * <p>
 * 2. Redistribution of this software's source code to any third party
 * without a commercial license is strictly prohibited.
 * <p>
 * 3. Licensees may copyright their own code but cannot use segments
 * from this software for such purposes. Copyright of this software
 * remains with BladeX.
 * <p>
 * Using this software signifies agreement to this License, and the software
 * must not be used for illegal purposes.
 * <p>
 * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
 * not liable for any claims arising from secondary or illegal development.
 * <p>
 * Author: Chill Zhuang (bladejava@qq.com)
 */
package org.springblade.file.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.file.config.ObsProperties;
import org.springblade.file.convert.BusinessAttachmentConvert;
import org.springblade.file.exception.UnzipFailException;
import org.springblade.file.mapper.AttachmentMapper;
import org.springblade.file.pojo.entity.Attachment;
import org.springblade.file.pojo.vo.AttachmentDetailVO;
import org.springblade.file.pojo.vo.FileHandleVO;
import org.springblade.file.pojo.vo.FileVO;
import org.springblade.file.service.IAttachmentService;
import org.springblade.file.service.IFileHandleService;
import org.springblade.file.service.IFileService;
import org.springblade.file.util.ObjectKeyUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 服务实现类
 * @author zhaowei
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class AttachmentServiceImpl extends ServiceImpl<AttachmentMapper, Attachment> implements IAttachmentService {

    private final IFileService fileService;
    private final BusinessAttachmentConvert convert;
    private final ObsProperties obsProperties;
    private final IFileHandleService fileHandleService;
    private IAttachmentService self;

    @Override
    public R<List<Attachment>> batchOssUpload(MultipartFile[] files, String overWriteFileName){
        try {
            List<Attachment> attachmentList = new ArrayList<>();
            // 文件数量为1个，且重写的文件名不为空，使用重写的文件名，给uniapp上传使用，uniapp上传的文件名不是原始文件名
            boolean useOverWriteFileName = files.length == 1 && StringUtil.isNotBlank(overWriteFileName);
            for(MultipartFile file : files){
                String fileName = useOverWriteFileName ? overWriteFileName : file.getOriginalFilename();
                String objectkey = ObjectKeyUtils.generateObjectKey(fileName);
                fileService.uploadFile(objectkey, file.getInputStream());
                // 插入附件数据
                Attachment attachment = new Attachment();
                Long id = IdWorker.getId();
                attachment.setId(id);
                attachment.setAttachmentName(fileName);
                attachment.setSize(file.getSize());
                attachment.setObjectKey(objectkey);
                attachmentList.add(attachment);
            }
            this.saveBatch(attachmentList);
            log.info("OBS上传 successfully");
            return R.data(attachmentList,"OBS上传 successfully");
        } catch (Exception e) {
            log.error("OBS上传 failed,Exception", e);
        }
        return R.fail("OBS上传 failed");
    }

    @Override
    public List<Attachment> getAttachment(String id) {
        return this.list(new LambdaQueryWrapper<Attachment>()
                .in(Attachment::getId, Arrays.stream(id.split(",")).toList())
        );
    }

    @Override
    public List<Attachment> batchOssUploadUnzipFilesAndDelete(List<File> files, File workDir) {
        if (CollectionUtil.isEmpty(files)) {
            return Collections.emptyList();
        }
        List<Attachment> attachmentList = new ArrayList<>();
        AtomicInteger index = new AtomicInteger(1);
        try {
            log.info("上传文件总数：{}", files.size());
            for(File file : files){
                String fileName = file.getName();
                // 压缩文件所在压缩包父目录相对路径
                String dir = this.getDir(workDir, file);
                // 获取上传的目录前缀
                String prefixDir = getUploadPrefix();
                String objectkey = prefixDir + ObjectKeyUtils.generateObjectKey(fileName);
                fileService.uploadFile(objectkey, file);
                int completeFiles = index.getAndIncrement();
                if (completeFiles % 10 == 0) {
                    log.info("上传文件总进度：{}/{}", completeFiles, files.size());
                }
                // 插入附件数据
                Attachment attachment = new Attachment();
                Long id = IdWorker.getId();
                attachment.setId(id);
                attachment.setDir(dir);
                attachment.setAttachmentName(fileName);
                attachment.setSize(file.length());
                attachment.setObjectKey(objectkey);
                attachmentList.add(attachment);
            }
            log.info("上传文件完成：{}", files.size());
            // 新事务批量保存
            this.getSelf().batchSaveWithNewTransaction(attachmentList);
            log.info("OBS上传 successfully");
        } catch (Exception e) {
            log.error("OBS上传失败", e);
            throw new ServiceException("OBS上传失败");
        }
        // 返回新增的附件列表
        return attachmentList;
    }

    /**
     * 获取上传文件目录前缀
     * @return
     */
    private String getUploadPrefix() {
        // 上传路径前缀 = 配置的解压上传路径 + /
        return obsProperties.getUnzipUploadDir() + StringPool.SLASH;
    }

    /**
     *  获取压缩文件所在压缩包父目录的相对路径，结尾不带/
     * @param workDir 工作目录，解压的根目录
     * @param file 解压后的文件
     * @return
     */
    private String getDir(File workDir, File file) {
        if (workDir == null) {
            return StringPool.EMPTY;
        }
        Path workDirPath = workDir.toPath();
        Path filePath = file.toPath();
        return workDirPath.relativize(filePath).toString()
                // 去掉文件名以及文件所在父目录的后缀/或\
                .replace(File.separator + file.getName(), "")
                // 将文件分隔符替换为/，windows下是\，需要处理
                .replace(File.separator, StringPool.SLASH);
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public void batchSaveWithNewTransaction(List<Attachment> list) {
        this.saveBatch(list);
    }

    @Override
    public AttachmentDetailVO getDetail(Long id) {
        if (id == null) {
            return null;
        }
        Attachment attachment = this.getById(id);
        if (attachment == null) {
            return null;
        }
        AttachmentDetailVO detailVO = convert.entity2vo(attachment);
        // 查询下载链接
        String fileUrl = fileService.getFileUrl(attachment.getObjectKey(), null);
        detailVO.setUrl(fileUrl);
        return detailVO;
    }

    @Override
    public List<Attachment> unzipAndUpload(FileVO file) {
        // 1. 下载并解压文件
        try {
            FileHandleVO fileHandleVO = fileHandleService.downloadAndUnzip(file);
            List<File> unzippedFiles = fileHandleVO.getUnzippedFiles();
            if (CollectionUtil.isEmpty(unzippedFiles)) {
                log.warn("下载解压文件为空 objectKey:{}", file.getObjectKey());
                throw new UnzipFailException("解压文件为空", fileHandleVO.getUnzippedDir());
            }
            File unzippedDir = fileHandleVO.getUnzippedDir();
            // 2. 上传文件
            long start = System.currentTimeMillis();
            try {
                List<Attachment> attachments = getSelf().batchOssUploadUnzipFilesAndDelete(unzippedFiles, unzippedDir);
                if (CollectionUtil.isEmpty(attachments)) {
                    log.warn("上传解压文件为空 objectKey:{}", file.getObjectKey());
                    return Collections.emptyList();
                }
               // 删除下载的压缩文件
                fileHandleVO.getDownloadFile().delete();
                // 删除解压后的目录
                FileUtils.deleteQuietly(unzippedDir);
                return attachments;
            } finally {
                long end = System.currentTimeMillis();
                log.info("文件：{} 解压后上传完成 耗时：{}", file.getObjectKey(), end - start);
            }
        } catch (UnzipFailException e) {
            // 文件解压失败，删除解压后的文件
            File unzipDir = e.getUnzipDir();
            // 删除解压后的目录
            FileUtils.deleteQuietly(unzipDir);
            throw e;
        }
    }

    /**
     *  获取自己
     * @return
     */
    private IAttachmentService getSelf() {
        if (self == null) {
            self = SpringUtil.getBean(IAttachmentService.class);
        }
        return self;
    }
}
