package com.trade.service.filestorage.service.impl;

import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.aspose.words.SaveFormat;
import com.github.pagehelper.util.StringUtil;
import com.trade.common.utils.StringUtils;
import com.trade.common.utils.file.ASopUtil;
import com.trade.common.utils.http.HttpClientUtils;
import com.trade.common.utils.http.HttpConfig;
import com.trade.service.filestorage.dto.FileAbstractDto;
import com.trade.service.filestorage.util.FileUtil;
import com.trade.service.filestorage.util.TextConverterPdfUtil;
import com.trade.service.filestorage.util.watermark.MakeWaterMark;
import com.trade.service.filestorage.util.watermark.PreviewUtil;
import com.trade.service.filestorage.util.watermark.TextMark;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.trade.common.constant.HttpStatus;
import com.trade.common.exception.ServiceException;
import com.trade.service.filestorage.entity.TFileStorage;
import com.trade.service.filestorage.enums.FileModuleEnum;
import com.trade.service.filestorage.service.OSSFileService;
import com.trade.service.filestorage.service.TFileStorageService;
import com.trade.service.filestorage.util.oss.OSSUtil;

import cn.hutool.core.collection.CollectionUtil;

@Service
public class OSSFileServiceImpl implements OSSFileService {

    @Resource
    private TFileStorageService tFileStorageService;
    @Resource
    private MakeWaterMark makeWaterMark;
    @Resource
    private OSSUtil ossUtil;

    @Override
    public TFileStorage uploadAndSaveDBRecord(File file, String fileDestDir) {
        String saveName = FileUtil.randomFileName() + FileUtil.getSuffix(file.getName());
        String objName = fileDestDir + OSSUtil.fileSep + saveName;// oss存储路径
        // 上传oss
        String eTag = ossUtil.uploadFile(objName, file.getAbsolutePath());
        if (StringUtils.isEmpty(eTag)) {
            throw new ServiceException("文件保存失败");
        }
        // 保存文件记录
        TFileStorage tFileStorage = new TFileStorage(eTag, file.getName(), saveName, objName, file.length());
        tFileStorageService.save(tFileStorage);
        return tFileStorage;
    }

    @Override
    public FileAbstractDto uploadFile(MultipartFile file, String fileDestDir) {
        try {
            TFileStorage tFileStorage = addStorage(file, fileDestDir);
            return new FileAbstractDto(tFileStorage);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("文件上传失败", HttpStatus.ERROR);
        }
    }

    @Override
    public TFileStorage uploadAndSaveDBRecord(File file, FileModuleEnum fileModuleEnum) {
        return uploadAndSaveDBRecord(file, fileModuleEnum.getCode());
    }

    @Override
    public TFileStorage addStorage(MultipartFile multipartFile, String fileDestDir) throws IOException {
        String saveName = FileUtil.randomFileName() + FileUtil.getSuffix(multipartFile.getOriginalFilename());
        String objName = fileDestDir + OSSUtil.fileSep + saveName;
        // 上传oss
        String eTag = ossUtil.uploadFile(objName, multipartFile.getInputStream());
        if (StringUtils.isEmpty(eTag)) {
            throw new ServiceException("文件保存失败");
        }
        // 保存文件记录
        TFileStorage tFileStorage = new TFileStorage(eTag, multipartFile.getOriginalFilename(), saveName, objName,
                multipartFile.getSize());
        tFileStorageService.save(tFileStorage);
        return tFileStorage;
    }

    @Override
    public TFileStorage addStorage(MultipartFile multipartFile, FileModuleEnum fileModule) throws IOException {
        return addStorage(multipartFile, fileModule.getCode());
    }

    @Override
    public FileAbstractDto uploadFile(MultipartFile file, FileModuleEnum moduleDict) {
        if (null == moduleDict) {
            moduleDict = FileModuleEnum.COMMON_UPLOAD;
        }
        try {
            TFileStorage tFileStorage = addStorage(file, moduleDict);
            return new FileAbstractDto(tFileStorage);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("文件上传失败", HttpStatus.ERROR);
        }
    }

    @Override
    public void uploadFile(HttpServletRequest request, FileModuleEnum moduleDict) {
        if (null == moduleDict) {
            moduleDict = FileModuleEnum.COMMON_UPLOAD;
        }
        uploadAndSaveFile(request, moduleDict);
    }

    @Override
    public void downloadFile(HttpServletRequest request, HttpServletResponse res, Long fileId) {
        TFileStorage tFileStorage = tFileStorageService.getById(fileId);
        if (null == tFileStorage) {
            throw new ServiceException("文件不存在");
        }
        if (null == tFileStorage.getPath()) {
            throw new ServiceException("文件存储非法");
        }
        String objName = tFileStorage.getPath();
        String fileName = tFileStorage.getOriginalName();
        // 获取oss文件流
        try (InputStream in = ossUtil.downloadFile(objName); OutputStream out = res.getOutputStream()) {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            res.setHeader("content-disposition", "attachment;fileName=" + fileName);
            byte[] b = new byte[1024];
            int len;
            while ((len = in.read(b)) != -1) {
                out.write(b, 0, len);
            }
        } catch (Exception e) {
            throw new ServiceException("文件下载失败");
        }
        // TODO 文件下载成功后，新增下载日志
    }

    @Override
    public String previewFile(Long fileId, HttpServletRequest request, HttpServletResponse response, Model model,
            String watermarkText) {
        TFileStorage fileStorage = tFileStorageService.getById(fileId);
        if (null == fileStorage) {
            // 文件不存在
            throw new ServiceException("文件不存在");
        }
        if (StringUtil.isNotEmpty(fileStorage.getFileSuffix())) {
            String fileSuffix = fileStorage.getFileSuffix();
            String fileName = fileStorage.getName();
            // 从oss下载文件到本地
            String downloadPath = FileUtil.getOSSFileOnlinePreviewPath() + File.separator + fileName;
            File file = ossUtil.download2LocalFile(fileStorage.getPath(), downloadPath);
            if (!file.exists()) {
                throw new ServiceException("获取文件失败");
            }
            if (fileSuffix.equalsIgnoreCase(".doc") || fileSuffix.equalsIgnoreCase(".docx")
                    || fileSuffix.equalsIgnoreCase(".rtf") || fileSuffix.equalsIgnoreCase(".wps")) {
                String destFile = FileUtil.getFileOnlinePreviewPath() + "/word.pdf";
                File newPDFile = ASopUtil.doc2pdf(downloadPath, destFile);
                PreviewUtil.previewPdf(response, newPDFile, fileName, "", watermarkText);
            } else if (fileSuffix.equalsIgnoreCase(".txt")) {
                // 2、单独处理txt文件，转换成pdf
                TextConverterPdfUtil p = TextConverterPdfUtil.getInstance();
                String pdf = FileUtil.getFileOnlinePreviewPath() + "/txt.pdf";
                p.text2Pdf(downloadPath, pdf);
                PreviewUtil.previewPdf(response, new File(pdf), fileName, "", watermarkText);
            } else if (fileSuffix.equalsIgnoreCase(".xls") || fileSuffix.equalsIgnoreCase(".xlsx")
                    || fileSuffix.equalsIgnoreCase(".et")) {
                        // 3、处理Excel、转换成HTML
                        String destFile = FileUtil.getFileOnlinePreviewPath() + "/excel.html";
                        File fileHtml = null;
                        try {
                            fileHtml = ASopUtil.docTransfer(new FileInputStream(downloadPath), destFile,
                                    SaveFormat.HTML);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                        // 加水印
                        makeWaterMark.textMarkForHtml(fileHtml, new TextMark(watermarkText));
                        PreviewUtil.previewHtmlFile(response, fileHtml);
                    } else
                if (fileSuffix.equalsIgnoreCase(".pdf")) {
                    PreviewUtil.previewPdf(response, file, fileName, "", watermarkText);
                } else if (fileSuffix.equalsIgnoreCase(".zip") || fileSuffix.equalsIgnoreCase(".rar")) {
                    downloadFile(request, response, fileId);
                } else {
                    // 图片的预览
                    // 添加水印
                    String fileNameP = fileStorage.getName();
                    String destFile = FileUtil.getAbsolutePreviewPicturePath() + File.separator + fileNameP;
                    makeWaterMark.textMarkForPicture(downloadPath, destFile, new TextMark(watermarkText));
                    // 由于预览的图片不能立即删除，故用定时任务去删除
                    return FileUtil.getPreviewPicturePath(fileNameP);
                }
        }
        return null;
    }

    @Override
    public InputStream getFileInputStream(String fileKey) {
        return ossUtil.downloadFile(fileKey);
    }

    @Override
    public TFileStorage downloadFileFromUrl(String originalName, String fileUrl, FileModuleEnum moduleType) {
        TFileStorage tFileStorage = null;
        try (InputStream inputStream = HttpClientUtils.downFile(HttpConfig.custom().url(fileUrl))) {
            String saveName = FileUtil.randomFileName() + FileUtil.getSuffix(originalName);
            String objName = moduleType.getCode() + OSSUtil.fileSep + saveName;
            // 上传oss
            String eTag = ossUtil.uploadFile(objName, inputStream);
            if (StringUtils.isEmpty(eTag)) {
                throw new ServiceException("文件保存失败");
            }
            // 保存文件记录
            tFileStorage = new TFileStorage(eTag, originalName, saveName, objName, 0L);
            tFileStorageService.save(tFileStorage);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return tFileStorage;
    }

    @Override
    public TFileStorage addStorageWithBytes(String originalName, byte[] bytes, FileModuleEnum fileModule) {
        try (InputStream inputStream = new ByteArrayInputStream(bytes)) {
            String saveName = FileUtil.randomFileName() + FileUtil.getSuffix(originalName);
            String objName = fileModule.getCode() + OSSUtil.fileSep + saveName;
            // 上传oss
            String eTag = ossUtil.uploadFile(objName, inputStream);
            if (StringUtils.isEmpty(eTag)) {
                throw new ServiceException("oss upload file failed, eTag is null");
            }
            // 保存文件记录
            TFileStorage tFileStorage = new TFileStorage(eTag, originalName, saveName, objName, bytes.length);
            tFileStorageService.save(tFileStorage);
            return tFileStorage;
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("文件保存失败" + e.getMessage(), HttpStatus.ERROR);
        }
    }

    @Override
    public TFileStorage addStorageWithBase64Str(String originalName, String base64Str, FileModuleEnum fileModule) {
        return addStorageWithBytes(originalName, Base64.decodeBase64(base64Str), fileModule);
    }

    private void uploadAndSaveFile(HttpServletRequest request, FileModuleEnum moduleDict) {
        if (!(request instanceof MultipartHttpServletRequest)) {
            return;
        }
        MultiValueMap<String, MultipartFile> multiValueMap = ((MultipartHttpServletRequest) request).getMultiFileMap();
        if (CollectionUtil.isEmpty(multiValueMap.values())) {
            throw new ServiceException("文件不能为空", HttpStatus.ERROR);
        }
        for (Map.Entry<String, List<MultipartFile>> entry : multiValueMap.entrySet()) {
            if (entry == null || CollectionUtil.isEmpty(entry.getValue())) {
                continue;
            }
            try {
                for (MultipartFile multipartFile : entry.getValue()) {
                    addStorage(multipartFile, moduleDict);
                }
            } catch (IOException e) {
                e.printStackTrace();
                throw new ServiceException("文件上传失败", HttpStatus.ERROR);
            }
        }
    }
}
