package com.tofly.xrtygis.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tofly.common.ftp.config.FtpUtil;
import com.tofly.common.oauth.exception.ToflyDeniedException;
import com.tofly.xrtygis.constant.Constant;
import com.tofly.xrtygis.entity.IFile;
import com.tofly.xrtygis.entity.dto.FileInfoDto;
import com.tofly.xrtygis.entity.vo.FileInfo;
import com.tofly.xrtygis.mapper.IFileMapper;
import com.tofly.xrtygis.service.IFileInfoService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author JF
 */
@Service
public class IFileInfoServiceImpl extends ServiceImpl<IFileMapper, IFile> implements IFileInfoService {

    public static int BYTE_LEN = 10240;

    /**
     * 文件上传
     */
    @Override
    public List<FileInfo> saveFileList(List<MultipartFile> fileList, String filePathBean, String tableName) {
        List<FileInfo> fileInfoList = new ArrayList<>();
        //保存照片
        fileList.forEach(file -> {
            FileInfo fileInfo = new FileInfo();
            //文件名
            String fileName = file.getOriginalFilename();
            fileInfo.setFileName(fileName);
            if (!"".equals(fileName)) {
                //文件路径
                assert fileName != null;
                //获取后缀
                String suffix = fileName.substring(fileName.lastIndexOf(Constant.POINT));
                fileInfo.setFileFormat(suffix);
                String filePath = filePathBean + IdWorker.getIdStr() + suffix;
                fileInfo.setFilePath(filePath);

                IFile localFile = new IFile();
                localFile.setFilePath(filePath);
                localFile.setFileName(fileName);
                localFile.setOtherName(filePath.substring(filePath.lastIndexOf("/") + 1));
                fileInfo.setOtherName(filePath.substring(filePath.lastIndexOf("/") + 1));
                //关联表名称
                localFile.setTableName(tableName);

                localFile.setFileFormat(suffix);
                localFile.setFileSize(new BigDecimal(file.getSize()).divide(BigDecimal.valueOf(1024),1, RoundingMode.HALF_UP) + "KB");
                fileInfo.setFileSize(new BigDecimal(file.getSize()).divide(BigDecimal.valueOf(1024),1, RoundingMode.HALF_UP) + "KB");

                FtpUtil.uploadFile2Ftp(file, filePath);
                this.save(localFile);

                fileInfoList.add(fileInfo);
            }
        });
        return fileInfoList;
    }

    @Override
    public List<FileInfo> saveFileInfoList(List<MultipartFile> fileList, String filePathBean, String tableName) {
        List<FileInfo> fileInfoList = new ArrayList<>();
        //保存照片
        fileList.forEach(file -> {
            FileInfo fileInfo = new FileInfo();
            //文件名
            String fileName = file.getOriginalFilename();
            fileInfo.setFileName(fileName);
            if (!"".equals(fileName)) {
                //文件路径
                assert fileName != null;
                //获取后缀
                String suffix = fileName.substring(fileName.lastIndexOf(Constant.POINT));
                fileInfo.setFileFormat(suffix);
                String filePath = filePathBean + IdWorker.getIdStr() + suffix;
                fileInfo.setFilePath(filePath);

                IFile localFile = new IFile();
                localFile.setFilePath(filePath);
                localFile.setFileName(fileName);
                localFile.setOtherName(filePath.substring(filePath.lastIndexOf("/") + 1));
                fileInfo.setOtherName(filePath.substring(filePath.lastIndexOf("/") + 1));
                //关联表名称
                localFile.setTableName(tableName);

                localFile.setFileFormat(suffix);
                localFile.setFileSize(new BigDecimal(file.getSize()).divide(BigDecimal.valueOf(1024),1, RoundingMode.HALF_UP) + "KB");
                fileInfo.setFileSize(new BigDecimal(file.getSize()).divide(BigDecimal.valueOf(1024),1, RoundingMode.HALF_UP) + "KB");

                FtpUtil.uploadFile2Ftp(file, filePath);
                this.save(localFile);

                fileInfoList.add(fileInfo);
            }
        });
        return fileInfoList;
    }

    @Override
    public Boolean saveFile(List<IFile> fileList) {
        for (IFile iFile : fileList) {
            this.save(iFile);
        }
        return true;
    }

    /**
     * 上传文件解压
     *
     * @param multipartFile 上传文件
     * @return 解压后的文件列表
     */
    @Override
    public List<FileInfo> unzip(MultipartFile multipartFile, String filePathBean, String tableName, String tempFilePath) throws Exception {
        File file = getFile(multipartFile, tempFilePath);
        if (!file.exists()) {
            return null;
        }
        List<FileInfo> fileInfos = new ArrayList<>();
        ZipFile zip = new ZipFile(file);
        Enumeration<? extends ZipEntry> entries = zip.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();

            String originalName = entry.getName();
            String fileSize = entry.getSize() + "kb";
            String suffix = entry.getName().substring(entry.getName().lastIndexOf(Constant.POINT));
            String fileOtherName = IdWorker.getIdStr() + suffix;
            String filePath = filePathBean + fileOtherName;

            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(originalName);
            fileInfo.setFileSize(fileSize);
            fileInfo.setFileFormat(suffix);
            fileInfo.setOtherName(fileOtherName);
            fileInfo.setFilePath(filePath);
            fileInfos.add(fileInfo);

            IFile localFile = new IFile();
            localFile.setTableName(tableName);
            localFile.setFilePath(filePath);
            localFile.setFileName(originalName);
            localFile.setOtherName(fileOtherName);
            localFile.setTableName(tableName);
            localFile.setFileFormat(suffix);
            localFile.setFileSize(fileSize);
            this.save(localFile);

            InputStream in = zip.getInputStream(entry);
            FtpUtil.uploadFile2Ftp(in, filePath);
            in.close();
        }
        zip.close();
        if (file.exists()) {
            file.delete();
        }
        return fileInfos;
    }

    @Override
    public Boolean removeFileList(List<FileInfo> fileInfoList) {
//        List<IFile> files = this.listByIds(filePathList);
        // 删除远程文件
        if (fileInfoList == null || fileInfoList.size() == 0) {
            return true;
        }
        List<String> filePathList = fileInfoList.stream().map(FileInfo::getFilePath).filter(Objects::nonNull).collect(Collectors.toList());
        filePathList.forEach(FtpUtil::deleteFile);
        return true;
    }

    @Override
    public Boolean removeFileInfoList(List<FileInfo> fileInfoList) {
        // 删除远程文件
        if (fileInfoList == null || fileInfoList.size() == 0) {
            return true;
        }
        List<String> filePathList = fileInfoList.stream().map(FileInfo::getFilePath).filter(Objects::nonNull).collect(Collectors.toList());
        filePathList.forEach(FtpUtil::deleteFile);
        return true;
    }

    @Override
    public String getFileInfo(FileInfoDto fileInfoDto) {
        if(fileInfoDto == null){
            throw new ToflyDeniedException("请求参数对象为空");
        }
        if(org.apache.commons.lang3.StringUtils.isBlank(fileInfoDto.getPath())){
            throw new ToflyDeniedException("文件存储路径为空");
        }
        if(org.apache.commons.lang3.StringUtils.isBlank(fileInfoDto.getTableName())){
            throw new ToflyDeniedException("表名为空");
        }
        List<FileInfo> newFileInfos = new ArrayList<>();

        List<FileInfo> agoFileInfos = null;
        List<FileInfo> nowFileInfos = null;
        if(org.apache.commons.lang3.StringUtils.isNotBlank(fileInfoDto.getAgoInfos())){
            //获取之前文件信息集合
            agoFileInfos = JSON.parseArray(fileInfoDto.getAgoInfos(),FileInfo.class);
        }
        if(org.apache.commons.lang3.StringUtils.isNotBlank(fileInfoDto.getNowInfos())){
            //获取当前文件信息集合
            nowFileInfos = JSON.parseArray(fileInfoDto.getNowInfos(),FileInfo.class);
        }
        List<MultipartFile> uploadFileList = new ArrayList<>();

        if(fileInfoDto.getFileNameList() != null && fileInfoDto.getFileList() != null){
            for(String fileName : fileInfoDto.getFileNameList()){
                for(MultipartFile file : fileInfoDto.getFileList()){
                    if(fileName.equals(file.getOriginalFilename())){
                        //找到此文件名列表中对应的文件
                        uploadFileList.add(file);
                    }
                }
            }
        }

        if(uploadFileList.size() != 0){
            //保存文件并将保存的文件信息集合添加进最新的文件信息集合中
            newFileInfos.addAll(this.saveFileList(uploadFileList, fileInfoDto.getPath(), fileInfoDto.getTableName()));
        }

        if(agoFileInfos != null && nowFileInfos != null){
            //获取待删文件信息集合
            List<FileInfo> finalNowFileInfos = nowFileInfos;
            List<FileInfo> reduce = agoFileInfos.stream().filter(item -> !finalNowFileInfos.contains(item)).collect(Collectors.toList());
            //删除文件
            this.removeFileList(reduce);
            //将当前文件信息集合添加进最新的文件信息集合中
            newFileInfos.addAll(nowFileInfos);
        }else if(agoFileInfos != null){
            this.removeFileList(agoFileInfos);
        }

        if(newFileInfos.size() != 0){
            //返回最新的文件信息集合
            return JSON.toJSONString(newFileInfos);
        }
        return "";
    }


    /**
     * 文件上传更新操作方法，支持删除旧文件，添加新文件
     *
     * @param fileList  新上传的文件列表
     * @param newAnnex  修改编辑过的附件集合json字符串
     * @param oldAnnex  数据库中的附件集合json字符串
     * @param folder    上传路径文件夹
     * @param tableName 上传附件对应的数据库表名
     * @return 上传文件更新删除之后的新附件集合json字符串
     */
    @Override
    public String uploadFiles(List<MultipartFile> fileList, String newAnnex, String oldAnnex, String folder, String tableName) {
        String annex = null;
        //编辑的时候需要删除文件
        if (StringUtils.hasText(newAnnex) && StringUtils.hasText(oldAnnex) && !newAnnex.equals(oldAnnex)) {
            List<FileInfo> fileInfos = JSONObject.parseArray(newAnnex, FileInfo.class);
            List<FileInfo> fileInfosDb = JSONObject.parseArray(oldAnnex, FileInfo.class);
            //获取差集删除部分
            List<FileInfo> collect = fileInfosDb.stream()
                    .filter(fileInfo -> !fileInfos.contains(fileInfo))
                    .collect(Collectors.toList());
            //如果差集存在
            if (!CollectionUtils.isEmpty(collect)) {
                //删除文件
                removeFileList(collect);
            }
            //获取交集部分
            List<FileInfo> collect1 = fileInfosDb.stream()
                    .filter(fileInfos::contains)
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect1)) {
                annex = JSONObject.toJSONString(collect1);
            }
        }else if(!StringUtils.isEmpty(newAnnex)&&newAnnex.equals(oldAnnex)){
            annex = oldAnnex;
        }
        //上传新文件
        if (fileList != null && fileList.size() != 0 && !CollectionUtils.isEmpty(fileList)) {
            List<FileInfo> fileInfos = saveFileList(fileList, folder, tableName);
            //以前没有上传过文件
            if (!CollectionUtils.isEmpty(fileInfos) && !StringUtils.hasText(annex)) {
                //直接返回现在新文件对象json
                return JSONObject.toJSONString(fileInfos);
                //以前上传过文件，则在以前的数据上进行累加
            } else if (!CollectionUtils.isEmpty(fileInfos) && StringUtils.hasText(annex)) {
                List<FileInfo> list = JSONObject.parseArray(annex, FileInfo.class);
                list.addAll(fileInfos);
                return JSONObject.toJSONString(list);
            }
        }
        return annex;
    }

    @Override
    public String getNewAnnex(String nowAnnex, List<MultipartFile> annexList, String agoAnnex, String path, String tableName) {
        // 获取传入的文件集合
        List<FileInfo> nowAnnexFileInfos = new ArrayList<>();
        if (org.apache.commons.lang3.StringUtils.isNotBlank(nowAnnex)) {
            nowAnnexFileInfos = JSON.parseArray(nowAnnex,FileInfo.class);
        }
        // 获取之前的文件集合
        List<FileInfo> agoAnnexFileInfos = new ArrayList<>();
        if (org.apache.commons.lang3.StringUtils.isNotBlank(agoAnnex)) {
            agoAnnexFileInfos = JSON.parseArray(agoAnnex,FileInfo.class);
        }
        // 保存文件并获得其文件集合
        List<FileInfo> newAnnexFileInfos = new ArrayList<>();
        if (annexList != null && annexList.size() != 0) {
            newAnnexFileInfos = this.saveFileList(annexList, path, tableName);
        }
        // 获取需要删除的文件集合并删除
        List<FileInfo> finalNowAnnexFileInfos = nowAnnexFileInfos;
        List<FileInfo> reduce = agoAnnexFileInfos.stream().filter(item -> !finalNowAnnexFileInfos.contains(item)).collect(Collectors.toList());
        this.removeFileList(reduce);
        // 获取最新的文件集合
        newAnnexFileInfos.addAll(nowAnnexFileInfos);
        return JSON.toJSONString(newAnnexFileInfos);
    }


    /********************************************************************************************************************************************/


    /**
     * MultipartFile 转 File
     *
     * @param multipartFile 上传文件
     * @return 本地文件
     */
    private static File getFile(MultipartFile multipartFile, String tempFilePath) throws Exception {
        String fileName = System.currentTimeMillis() + "_" + multipartFile.getOriginalFilename();
        File file = new File(tempFilePath + fileName);
        OutputStream os = new FileOutputStream(file);
        InputStream in = multipartFile.getInputStream();
        byte[] buffer = new byte[BYTE_LEN];
        int len;
        while ((len = in.read(buffer, 0, BYTE_LEN)) != -1) {
            os.write(buffer, 0, len);
        }
        os.close();
        in.close();
        return file;
    }

}
