package cn.com.ctop.shiwan.modules.service.impl;

import cn.com.ctop.common.module.utils.Check;
import cn.com.ctop.common.module.utils.CosUtils;
import cn.com.ctop.common.module.utils.LoadFileUtil;
import cn.com.ctop.common.module.vo.ResFileDTO;
import cn.com.ctop.shiwan.modules.FileUtil;
import cn.com.ctop.shiwan.modules.entity.ShiwanFileUpload;
import cn.com.ctop.shiwan.modules.mapper.ShiwanFileUploadMapper;
import cn.com.ctop.shiwan.modules.service.IShiwanFileUploadService;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxl.job.core.enums.NoEn;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 试玩平台，文件信息
 * try-play
 *
 * @author jeecg-boot
 * @version V1.0
 * @date 2020-10-21
 */
@Slf4j
@Service
public class ShiwanFileUploadServiceImpl extends ServiceImpl<ShiwanFileUploadMapper, ShiwanFileUpload> implements IShiwanFileUploadService {

    @Autowired
    private ShiwanFileUploadMapper shiwanFileUploadMapper;
    @Value("${zip.local.download-path}")
    private String downloadPath;

    @Override
    public ShiwanFileUpload saveOrUpdateFile(MultipartFile file, String userId, Long accountId, String fileType, Long parentId, String uuid) {
        StringBuffer path = new StringBuffer();
        path.append(downloadPath).append(DateUtils.getNowDate(DateUtils.WEB_FORMAT)).append("/").append(UUID.randomUUID().toString()).append("/");
        log.info("-------下载到服务器地址：{}", path.toString());
        ShiwanFileUpload shiwanFileUpload = null;
        String newFileUrl = FileUtil.approvalFile(file, path.toString());
        String md5 = null;
        try {
            md5 = LoadFileUtil.getMD5(newFileUrl);
        } catch (IOException e) {
            log.error("获取MD5签名失败", e);
        }
        ShiwanFileUpload existFiles = getExistFiles(userId, accountId, file.getOriginalFilename(), fileType, parentId, md5);
        if (!Check.isNull(existFiles)) {
            long start = System.currentTimeMillis();
            existFiles.setSignature(md5);
            try {
                shiwanFileUpload = updateFile(file, existFiles);
            } catch (IOException e) {
                log.error("更新文件，上传SOC异常", e);
            }
            if (!Check.isNull(shiwanFileUpload)) {
                List<String> urlList = unzipFiles(shiwanFileUpload, path.toString(), newFileUrl);
                saveInnerFiles(shiwanFileUpload, urlList);
            }
            long end = System.currentTimeMillis();
            log.info("文件更新完成，耗时：{} ms", (end - start));
        } else {
            long start = System.currentTimeMillis();
            ShiwanFileUpload fileUpload = new ShiwanFileUpload();
            fileUpload.setSignature(md5);
            fileUpload.setUserId(userId);
            fileUpload.setAccountId(accountId);
            fileUpload.setFileType(fileType);
            try {
                //新增zip包
                shiwanFileUpload = saveFile(file, fileUpload, null);
            } catch (IOException e) {
                log.error("新增文件，上传SOC异常", e);
            }
            if (!Check.isNull(shiwanFileUpload)) {
                List<String> urlList = unzipFiles(shiwanFileUpload, path.toString(), newFileUrl);
                saveInnerFiles(shiwanFileUpload, urlList);
            }
            long end = System.currentTimeMillis();
            log.info("文件新增完成，耗时：{} ms", (end - start));
        }
        return shiwanFileUpload;
    }

    /**
     * 更新数据并上传文件到COS
     *
     * @param
     * @return cn.com.ctop.shiwan.modules.entity.ShiwanFileUpload
     * @throws
     * @author ZHAOXA
     */
    private ShiwanFileUpload updateFile(MultipartFile file, ShiwanFileUpload existFiles) throws IOException {
        ResFileDTO resultFile = null;
        String url = "";
        ShiwanFileUpload fileUpload = new ShiwanFileUpload();
        if (existFiles.getParentId() != 0) {
            url = "try-play/inner-file/";
        } else {
            url = "try-play/zip/";
        }
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            resultFile = CosUtils.uploadDetailInputStreamV2(inputStream, existFiles.getFileName(), existFiles.getFileSuffix(), file.getSize(), url);
        } catch (IOException e) {
            log.error("更新文件，上传COS失败", e);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
        QueryWrapper<ShiwanFileUpload> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", existFiles.getId());
        existFiles.setFileSize(resultFile.getFileSize() + "B");
        existFiles.setUploadTime(new Date());
        existFiles.setFileUrl(resultFile.getFileUrl());
        existFiles.setCfileName(resultFile.getCFileName());
        shiwanFileUploadMapper.update(existFiles, queryWrapper);
        shiwanFileUploadMapper.deleteByParentId(existFiles.getId());
        return existFiles;
    }

    /**
     * 存储数据并上传文件到COS
     *
     * @param
     * @return cn.com.ctop.shiwan.modules.entity.ShiwanFileUpload
     * @throws
     * @author ZHAOXA
     */
    private ShiwanFileUpload saveFile(MultipartFile file, ShiwanFileUpload fileUpload, String uuid) throws IOException {
        String filename = file.getOriginalFilename();
        ResFileDTO resultFile = null;
        String url = "";
        if (Check.isNull(fileUpload.getParentId())) {
            url = "try-play/zip/";
            fileUpload.setParentId((long) NoEn.NO0.valueInt());
        } else {
            url = "try-play/inner-file/".concat(uuid).concat("/");
        }
        String fileSuffix = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            resultFile = CosUtils.uploadDetailInputStreamV2(inputStream, filename, fileSuffix, file.getSize(), url);
        } catch (IOException e) {
            log.error("上传COS异常", e);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
        if (!Check.isNull(resultFile)) {
            fileUpload.setCfileName(resultFile.getCFileName());
            fileUpload.setFileName(filename);
            fileUpload.setFileSuffix(fileSuffix);
            if ("zip".equals(fileSuffix)) {
                fileUpload.setFileLevel(NoEn.NO0.valueInt());
            } else {
                fileUpload.setFileLevel(NoEn.NO1.valueInt());
            }
            fileUpload.setFileStatus(NoEn.NO1.valueStr());
            fileUpload.setFileSize(resultFile.getFileSize() + "B");
            fileUpload.setFileUrl(resultFile.getFileUrl());
            fileUpload.setUploadTime(new Date());
            shiwanFileUploadMapper.insert(fileUpload);
        }
        return shiwanFileUploadMapper.selectById(fileUpload.getId());
    }


    @Override
    public Result<Object> createNewZipFile(MultipartFile file, String id, String updateName, String userId, Long accountId, String fileType, String isCreate, String newFileName) throws IOException {
        ShiwanFileUpload oldFile = shiwanFileUploadMapper.selectById(id);
        if (Check.isNull(oldFile)) {
            return Result.error("查询原文件数据失败");
        }
        StringBuffer path = new StringBuffer();
        String uuid = UUID.randomUUID().toString();
        path.append(downloadPath).append(DateUtils.getNowDate(DateUtils.WEB_FORMAT)).append("/").append(uuid).append("/");
        log.info("----------服务器下载地址：{}", path.toString());
        //解压zip文件
        List<String> urlList = unzipFiles(oldFile, path.toString(), null);
        String newFilePath = "";
        if (null != urlList && urlList.size() > 0) {
            Iterator it = urlList.iterator();
            while (it.hasNext()) {
                String url = it.next().toString();
                String fileName = url.substring(url.lastIndexOf("/") + 1);
                if (updateName.equals(fileName)) {
                    newFilePath = url.substring(0, url.lastIndexOf("/") + 1);
                    //移除被替换的对象
                    it.remove();
                    deletelocalFiles(url);
                    break;
                }
            }
        }
        if (Check.isNull(newFilePath)) {
            return Result.error("被替换文件名称有误");
        }
        //添加上新的对象
        String newUrl = FileUtil.approvalFile(file, newFilePath);
        urlList.add(newUrl);
        //压缩zip文件
        if (Check.isNull(newFileName) && NoEn.NO1.valueStr().equals(isCreate)) {
            newFileName = DateUtils.getNowDate(DateUtils.LONG_FORMAT).concat("_COPY-FROM_").concat(oldFile.getFileName());
        }
        ShiwanFileUpload newFile = new ShiwanFileUpload();
        newFile.setUserId(userId);
        newFile.setAccountId(accountId);
        newFile.setFileType(fileType);
        newFile.setFileName(newFileName);
        oldFile.setUserId(userId);
        oldFile.setAccountId(accountId);
        oldFile.setFileType(fileType);
        oldFile.setFileName(newFileName);
        ShiwanFileUpload newZipFiles = createNewZipFiles(urlList, path.toString(), newFile, isCreate, oldFile);
        return Result.ok(newZipFiles);
    }

    @Override
    public List<ShiwanFileUpload> selectListByParentId(String id) {
        return shiwanFileUploadMapper.selectListByParentId(id);
    }

    /**
     * 解压文件
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private List<String> unzipFiles(ShiwanFileUpload fileUpload, String path, String innerZipPath) {
        long start = System.currentTimeMillis();
        List<String> urlList = new ArrayList<>();
        //下载到本地服务器
        if (Check.isNull(innerZipPath)) {
            innerZipPath = FileUtil.writeFiles(path.concat("oldZip/"), fileUpload.getFileUrl(), fileUpload.getCfileName());
        }
        try {
            log.info("----------zip包下载地址：{}", innerZipPath);
            ZipFile zipFile = new ZipFile(innerZipPath, Charset.forName("GBK"));
            Enumeration<?> enumeration = zipFile.entries();
            ZipEntry zipEntry = null;
            String innerPath = path.concat("innerFiles/");
            while (enumeration.hasMoreElements()) {
                zipEntry = (ZipEntry) enumeration.nextElement();
                // 如果是文件夹，就创建个文件夹
                if (zipEntry.isDirectory()) {
                    String dirPath = innerPath + zipEntry.getName();
                    File dir = new File(dirPath);
                    dir.mkdirs();
                } else {
                    String url = innerPath + zipEntry.getName();
                    // 如果是文件，就先创建一个文件，然后用io流把内容copy过去
                    File targetFile = new File(url);
                    // 保证这个文件的父文件夹必须要存在
                    if (!targetFile.getParentFile().exists()) {
                        targetFile.getParentFile().mkdirs();
                    }
                    targetFile.createNewFile();
                    // 将压缩文件内容写入到这个文件中
                    InputStream is = zipFile.getInputStream(zipEntry);
                    FileOutputStream fos = new FileOutputStream(targetFile);
                    int len;
                    byte[] buf = new byte[2048];
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    // 关流顺序，先打开的后关闭
                    fos.close();
                    is.close();
                    urlList.add(url);
                }
            }
        } catch (IOException e) {
            log.error("解压原zip文件失败 ", e);
        }
        deletelocalFiles(innerZipPath);
        long end = System.currentTimeMillis();
        log.info("文件解压完成，耗时：{} ms", (end - start));
        return urlList;
    }

    /**
     * 压缩ZIP文件，并存储数据到本地
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private ShiwanFileUpload createNewZipFiles(List<String> list, String path, ShiwanFileUpload addFile, String isCreate, ShiwanFileUpload updateFile) throws IOException {
        String zipUrl = FileUtil.zipCompress(path.concat(addFile.getFileName()), new File(path.concat("innerFiles/")));
        try {
            String md5 = LoadFileUtil.getMD5(zipUrl);
            addFile.setSignature(md5);
            updateFile.setSignature(md5);
        } catch (IOException e) {
            log.error("获取MD5签名失败", e);
        }
        ShiwanFileUpload newZipEntity = null;
        //判断是新增还是更新
        if (NoEn.NO1.valueStr().equals(isCreate)) {
            //存储新ZIP包
            newZipEntity = saveFile(FileUtil.getMulFileByPath(zipUrl), addFile, null);
            deletelocalFiles(zipUrl);
            if (!Check.isNull(newZipEntity)) {
                saveInnerFiles(newZipEntity, list);
            }
        } else {
            newZipEntity = updateFile(FileUtil.getMulFileByPath(zipUrl), updateFile);
            deletelocalFiles(zipUrl);
            if (!Check.isNull(newZipEntity)) {
                saveInnerFiles(newZipEntity, list);
            }
        }
        return newZipEntity;
    }

    /**
     * 存储zip内部文件
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void saveInnerFiles(ShiwanFileUpload newZipEntity, List<String> list) {
        MultipartFile mulFileByPath = null;
        for (String url : list) {
            ShiwanFileUpload shiwanFileUpload = new ShiwanFileUpload();
            shiwanFileUpload.setUserId(newZipEntity.getUserId());
            shiwanFileUpload.setAccountId(newZipEntity.getAccountId());
            shiwanFileUpload.setFileType(newZipEntity.getFileType());
            shiwanFileUpload.setParentId(newZipEntity.getId());
            String urlpath = url.substring(0, url.lastIndexOf("/"));
            String lastFileName = urlpath.substring(urlpath.lastIndexOf("/") + 1);
            String cosFilePath = "";
            if ("innerFiles".equals(lastFileName)) {
                cosFilePath = newZipEntity.getCfileName();
            } else {
                cosFilePath = newZipEntity.getCfileName().concat("/").concat(lastFileName);
            }
            try {
                shiwanFileUpload.setSignature(LoadFileUtil.getMD5(url));
            } catch (IOException e) {
                log.error("获取MD5签名失败", e);
            }
            mulFileByPath = FileUtil.getMulFileByPath(url);
            try {
                saveFile(mulFileByPath, shiwanFileUpload, cosFilePath);
            } catch (IOException e) {
                log.error("新增内部文件，上传SOC异常", e);
            }
            deletelocalFiles(url);
        }
    }

    /**
     * 删除本地文件
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void deletelocalFiles(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * 查询现有的文件
     *
     * @param
     * @return cn.com.ctop.shiwan.modules.entity.ShiwanFileUpload
     * @throws
     * @author ZHAOXA
     */
    private ShiwanFileUpload getExistFiles(String userId, Long accountId, String fileName, String fileType, Long parentId, String md5) {
        JSONObject job = new JSONObject();
        job.put("useId", userId);
        job.put("accountId", accountId);
        job.put("fileName", fileName);
        job.put("fileType", fileType);
        job.put("parentId", parentId);
        job.put("signature", md5);
        ShiwanFileUpload fileEntity = shiwanFileUploadMapper.selectEntity(job);
        if (Check.isNull(fileEntity)) {
            return null;
        }
        return fileEntity;
    }
}
