package com.sckw.file.service;


import com.aliyun.oss.model.OSSObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sckw.core.model.constant.NumberConstant;
import com.sckw.core.common.enums.StringConstant;
import com.sckw.core.common.enums.enums.FileDisposeLayoutEnum;
import com.sckw.core.common.enums.enums.FileEnum;
import com.sckw.core.model.file.FileInfo;
import com.sckw.core.model.vo.FileInfoEncryptVO;
import com.sckw.core.model.vo.FileInfoVO;
import com.sckw.core.utils.FileUtils;
import com.sckw.core.utils.IdWorker;
import com.sckw.core.utils.StringUtils;
import com.sckw.core.web.constant.HttpStatus;
import com.sckw.core.web.context.LoginUserHolder;
import com.sckw.core.web.response.HttpResult;
import com.sckw.core.web.response.result.Status;
import com.sckw.file.api.dto.FileInfoDTO;
import com.sckw.file.config.FileListConfig;
import com.sckw.file.dao.KwsFileEncryptDao;
import com.sckw.file.dao.KwsFileInfoDao;
import com.sckw.file.model.kwfFileEncrypt;
import com.sckw.file.model.kwfFileInfo;
import com.sckw.file.model.param.FileImageParam;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * @author lfdc
 * @version 1.0
 * @className FileService
 * @description 文件上传下载service
 * @company sckw
 * @date 2023-06-02 16:06:46
 */
@Slf4j
@Service
public class FileService {

    final static String IMG_FORMAT = "png_jpg_bmp_gif_tif_jpeg_PNG_JPG_BMP_GIF_TIF_JPEG_HEIC_heic";

    @Autowired
    KwsFileInfoDao fileInfoDao;

    @Autowired
    KwsFileEncryptDao fileEncryptDao;

    @Autowired
    private FileListConfig fileListConfig;

    /**
     * 上传文件至OSS
     *
     * @param file
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public HttpResult uploadFile(MultipartFile file) {
        HttpResult result = new HttpResult();
        List<Map<String, Object>> resultList = new ArrayList<>();
        result.setCode(HttpStatus.SUCCESS_CODE);
        boolean isEmpty = file.isEmpty();
        if (isEmpty) {
            result.setCode(HttpStatus.GLOBAL_EXCEPTION_CODE);
            result.setMsg("上传请不要选择空文件");
            return result;
        }
        String oFileName = file.getOriginalFilename();
        BigDecimal fileSize = FileUtils.getFileSize(file, StringConstant.KB);
        kwfFileInfo fileInfo = new kwfFileInfo();
        fileInfo.setOriginalName(oFileName);
        fileInfo.setFileSuffix(FilenameUtils.getExtension(oFileName));
        fileInfo.setFileSize(StringUtils.isBlank(fileSize) ? new BigDecimal(NumberConstant.ZERO) : fileSize);
        Map<String, String> infoMap = FileUtils.uploadFileByInfo(file, FileEnum.DOCUMENT_ADDRESS);
        fileInfo.setId(new IdWorker(1).nextId());
        fileInfo.setType(FileEnum.FILE_STORE_TYPE_OSS.getFileType());
        fileInfo.setFileName(infoMap.get("fileName"));
        fileInfo.setMd5(infoMap.get("fileMd5"));
        fileInfo.setFilePath(infoMap.get("filePath"));
        fileInfo.setCreateTime(new Date());
        fileInfo.setUpdateTime(new Date());
        fileInfo.setStatus(0);
        fileInfo.setDelFlag(0);
        fileInfo.setCreateBy(LoginUserHolder.getUserId());
        fileInfo.setUpdateBy(LoginUserHolder.getUserId());
        fileInfoDao.insert(fileInfo);
        if (StringUtils.isNotBlank(infoMap.get("filePath"))) {
            result.setMsg("上传成功");
            Map<String, Object> map = new HashMap<>(NumberConstant.SIXTEEN);
            map.put("fileKey", infoMap.get("fileMd5"));
            map.put("fileName", infoMap.get("fileName"));
            resultList.add(map);
            result.setData(resultList);
        } else {
            result.setCode(HttpStatus.GLOBAL_EXCEPTION_CODE);
            result.setMsg("上传请选择文件");
        }
        return result;
    }


    /**
     * 批量上传文件至OSS
     *
     * @param file
     * @return
     */
    @Async
    public HttpResult uploadFileList(MultipartFile[] file) {
        HttpResult result = new HttpResult();
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (!ObjectUtils.isEmpty(file) && file.length > 0) {
            List<MultipartFile> multipartFiles = Arrays.asList(file);
            for (MultipartFile multipartFile : multipartFiles) {
                Map<String, Object> map = new HashMap<>(NumberConstant.SIXTEEN);
                //文件大小
//                String fileSize = FileUtils.getFileSize(multipartFile);
                //文件名称
                String originalFilename = multipartFile.getOriginalFilename();
                map.put("code", HttpStatus.SUCCESS_CODE);
                map.put("fileName", originalFilename);
                map.put("message", HttpStatus.SUCCESS_MESSAGE);
                String oosUrl = null;
                //上传文件是否成功
                try {
                    oosUrl = FileUtils.uploadFile(multipartFile, FileEnum.DOCUMENT_ADDRESS);
                } catch (Exception e) {
                    map.put("code", HttpStatus.GLOBAL_EXCEPTION_CODE);
                    map.put("fileName", originalFilename);
                    map.put("message", e);
                }
                if (StringUtils.isBlank(oosUrl)) {
                    map.put("code", HttpStatus.GLOBAL_EXCEPTION_CODE);
                    map.put("fileName", originalFilename);
                    map.put("message", HttpStatus.SUCCESS_MESSAGE);
                }
                //上传至oss文件地址
                map.put("oosUrl", oosUrl);
                resultList.add(map);
            }
        } else {
            result.setCode(HttpStatus.GLOBAL_EXCEPTION_CODE);
            result.setMsg("上传选择文件为空");
        }
        result.setData(resultList);
        return result;
    }


    /**
     * OSS下载文件/获取文件地址
     *
     * @param fileName kll/uploads/20230621/07ccbec381a011d121a215719199ac49.html
     */
    public void fileDownload(String fileName) {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        FileUtils.downloadByUrl(response, fileName);
    }


    public void remove(String fileName) {
        String str = FileUtils.getOSSAddressPrefix();
//        String fileNameSubstring = StringUtils.substring(fileName, str.length() + 1);
        FileUtils.remove(fileName);
    }

    public HttpResult selectAll() {
        BigDecimal bigDecimal1 = new BigDecimal(NumberConstant.ZERO);
        BigDecimal bigDecimal2 = new BigDecimal(NumberConstant.SIX);
        BigDecimal multiply = bigDecimal2.divide(bigDecimal1);
        return HttpResult.ok("", multiply);
    }

    public HttpResult downAllOssFile(String fileAllName, HttpServletRequest request, HttpServletResponse response) {
        try {
            String momentFileName = "全部资源下载.zip";
            // 创建临时文件
            File zipFile = File.createTempFile("批量下载", ".zip");
            FileOutputStream f = new FileOutputStream(zipFile);
            CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
            // 用于将数据压缩成Zip文件格式
            ZipOutputStream zos = new ZipOutputStream(csum);
            String[] files = fileAllName.split(",");
            for (String string : files) {
                String fName = string.trim();
                String eachFileName = fName.substring(fName.lastIndexOf("/") + 1);
                String tmp = fName.substring(0, fName.lastIndexOf("/"));
                tmp = tmp.substring(tmp.lastIndexOf("/") + 1);
                String fileName = tmp + "/" + eachFileName;
                OSSObject ossObject = FileUtils.downloadOssFile(response.getOutputStream(), fileName);
                InputStream inputStream = ossObject.getObjectContent();
                zos.putNextEntry(new ZipEntry(eachFileName));
                int bytesRead;
                // 向压缩文件中输出数据
                while ((bytesRead = inputStream.read()) != -1) {
                    zos.write(bytesRead);
                }
                inputStream.close();
                zos.closeEntry();
            }
            zos.close();
            String header = request.getHeader("User-Agent").toUpperCase();
            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                momentFileName = URLEncoder.encode(momentFileName, StringConstant.ENCODING_UTF_EIGHT);
                //IE下载文件名空格变+号问题
                momentFileName = momentFileName.replace("+", "%20");
            } else {
                momentFileName = new String(momentFileName.getBytes(), StringConstant.ENCODING_ISO);
            }
            response.reset();
            response.setContentType("text/plain");
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Location", momentFileName);
            response.setHeader("Cache-Control", "max-age=0");
            response.setHeader("Content-Disposition", "attachment; filename=" + momentFileName);
            FileInputStream fis = new FileInputStream(zipFile);
            BufferedInputStream buff = new BufferedInputStream(fis);
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            byte[] car = new byte[1024];
            int l = 0;
            while (l < zipFile.length()) {
                int j = buff.read(car, 0, 1024);
                l += j;
                out.write(car, 0, j);
            }
            // 关闭流
            fis.close();
            buff.close();
            out.close();
            // 删除临时文件
            zipFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
            log.info("batch list down file error :{}", e.getMessage(), e);
        }
        return null;
    }

    public HttpResult uploadFileInfo(MultipartFile file) {
        boolean flag = checkFileFormat(file);
        if (!flag) {
            throw new RuntimeException("上传文件格式错误!");
        }
        FileInfo fileInfo = FileUtils.getFileDataList(file);
        fileInfo.setType(FileEnum.FILE_STORE_TYPE_OSS.getFileType());
        FileInfo returnFileInfo = FileUtils.uploadFileInfo(file, fileInfo, FileEnum.FILE_STORE_TYPE_OSS);
        FileInfoVO fileInfoVO = new FileInfoVO();
        BeanUtils.copyProperties(returnFileInfo, fileInfoVO);
        boolean isImage = FileUtils.isImage(file);
        insertFile(returnFileInfo, isImage);
        return HttpResult.ok(fileInfoVO);
    }

    public FileInfoVO uploadFileInfoV2(MultipartFile file) {
        boolean flag = checkFileFormat(file);
        if (!flag) {
            throw new RuntimeException("上传文件格式错误!");
        }
        FileInfo fileInfo = FileUtils.getFileDataList(file);
        fileInfo.setType(FileEnum.FILE_STORE_TYPE_OSS.getFileType());
        FileInfo returnFileInfo = FileUtils.uploadFileInfo(file, fileInfo, FileEnum.FILE_STORE_TYPE_OSS);
        FileInfoVO fileInfoVO = new FileInfoVO();
        BeanUtils.copyProperties(returnFileInfo, fileInfoVO);
        boolean isImage = FileUtils.isImage(file);
        insertFile(returnFileInfo, isImage);
        return fileInfoVO;
    }

    private void insertFile(FileInfo fileInfo, boolean isImage) {
        kwfFileInfo kwfFileInfo = new kwfFileInfo();
        kwfFileInfo.setOriginalName(fileInfo.getFileOriginalName());
        kwfFileInfo.setFileSuffix(FilenameUtils.getExtension(fileInfo.getFileOriginalName()));
        kwfFileInfo.setFileSize(StringUtils.isBlank(fileInfo.getFileSize()) ? new BigDecimal(NumberConstant.ZERO) : fileInfo.getFileSize());
        kwfFileInfo.setId(new IdWorker(1).nextId());
        kwfFileInfo.setType(fileInfo.getType());
        kwfFileInfo.setFileName(fileInfo.getFileName());
        kwfFileInfo.setMd5(fileInfo.getFileMd5());
        kwfFileInfo.setFilePath(fileInfo.getFilePath());
        kwfFileInfo.setCreateTime(new Date());
        kwfFileInfo.setUpdateTime(new Date());
        kwfFileInfo.setStatus(0);
        kwfFileInfo.setDelFlag(0);
        kwfFileInfo.setCreateBy(LoginUserHolder.getUserId());
        kwfFileInfo.setUpdateBy(LoginUserHolder.getUserId());
        if (FileEnum.FILE_STORE_TYPE_OSS.getFileType().equals(fileInfo.getType())) {
            if (isImage) {
                kwfFileInfo.setFileParameter(FileDisposeLayoutEnum.OSS_ABBREVIATE_LEFT.getParameter() + "200"
                        + FileDisposeLayoutEnum.OSS_ABBREVIATE_RIGHT.getParameter() + "200");
            }
        }
        fileInfoDao.insert(kwfFileInfo);
    }


    @Transactional
    public HttpResult appUploadFileInfo(MultipartFile file) {
        boolean flag = checkFileFormat(file);
        if (!flag) {
            return HttpResult.error(Status.FAILURE.getCode(), "上传文件格式错误!");
        }
        FileInfo fileInfo = FileUtils.getFileDataList(file);
        fileInfo.setType(FileEnum.FILE_STORE_TYPE_OSS.getFileType());
        FileInfo returnFileInfo = FileUtils.uploadFileInfo(file, fileInfo, FileEnum.FILE_STORE_TYPE_OSS);
        FileInfoVO fileInfoVO = new FileInfoVO();
        BeanUtils.copyProperties(returnFileInfo, fileInfoVO);
        boolean isImage = FileUtils.isImage(file);
        insertFile(returnFileInfo, isImage);
        return HttpResult.ok(Status.SUCCESS.getCode(), "", fileInfoVO);
    }

    /**
     * 上传文件->oss文件格式校验
     *
     * @param file
     * @return
     */
    private boolean checkFileFormat(MultipartFile file) {
        List<String> ossList = fileListConfig.getOss();
        String fileSuffix = FilenameUtils.getExtension(file.getOriginalFilename());
        if (!ossList.contains(fileSuffix)) {
            return false;
        }
        return true;
    }


    public HttpResult fileDubboUpload(InputStream inputStream, FileInfoDTO fileInfo) {
        Map<String, String> map = FileUtils.uploadFileByInfo(inputStream, fileInfo.getOriginalName(), FileEnum.Local_Address);
        return HttpResult.ok(map);
    }


    @Transactional(rollbackFor = Exception.class)
    public HttpResult uploadFileEncrypt(MultipartFile file) {
        boolean flag = checkFileFormat(file);
        if (!flag) {
            throw new RuntimeException("上传文件格式错误!");
        }
        FileInfo fileInfo = FileUtils.getFileDataList(file);
        String fileType = FileEnum.FILE_STORE_TYPE_OSS.getFileType();
        fileInfo.setType(fileType);
        /**上传文件*/
        FileInfo returnFileInfo = FileUtils.uploadFileInfo(file, fileInfo, FileEnum.FILE_STORE_TYPE_OSS);
        boolean isImage = FileUtils.isImage(file);
        /**存储文件到数据库*/
        insertFile(returnFileInfo, isImage);
        /**存储到加密属性文件信息*/
        String fileMd5 = returnFileInfo.getFileMd5();
//        String filePath = returnFileInfo.getFilePath();
        FileInfoEncryptVO vo = new FileInfoEncryptVO();
        vo.setFileMd5(fileMd5);
//        vo.setFilePath(filePath);
//        insertFileEncrypt(fileMd5, filePath, FileEnum.FILE_STORE_TYPE_OSS, isImage);
        return HttpResult.ok(vo);
    }

    /**
     * 文件加密存储
     *
     * @param fileMd5  md5加密key
     * @param filePath 文件相对路径
     * @param fileEnum 文件类型
     * @param isImage  是否是图片
     */
    public void insertFileEncrypt(String fileMd5, String filePath, FileEnum fileEnum, boolean isImage) {
        Date date = new Date();
        kwfFileEncrypt encrypt = new kwfFileEncrypt();
        if (FileEnum.FILE_STORE_TYPE_OSS.getFileType().equals(fileEnum.getFileType())) {
            if (isImage) {
                encrypt.setFileParameter(FileDisposeLayoutEnum.OSS_ABBREVIATE_LEFT.getParameter() + "200"
                        + FileDisposeLayoutEnum.OSS_ABBREVIATE_RIGHT.getParameter() + "200");
            }
        }
        encrypt.setType(fileEnum.getFileType());
        encrypt.setStatus(NumberConstant.ZERO);
        encrypt.setCreateBy(LoginUserHolder.getUserId());
        encrypt.setCreateTime(date);
        encrypt.setUpdateBy(LoginUserHolder.getUserId());
        encrypt.setUpdateTime(date);
        encrypt.setDelFlag(NumberConstant.ZERO);
        encrypt.setId(new IdWorker(NumberConstant.ONE).nextId());
        encrypt.setEncryptKey(fileMd5);
        encrypt.setFilePath(filePath);
        fileEncryptDao.insert(encrypt);
    }

    @Transactional(rollbackFor = Exception.class)
    public HttpResult appUploadFileEncrypt(MultipartFile file) {
        boolean flag = checkFileFormat(file);
        if (!flag) {
            throw new RuntimeException("上传文件格式错误!");
        }
        FileInfo fileInfo = FileUtils.getFileDataList(file);
        String fileType = FileEnum.FILE_STORE_TYPE_OSS.getFileType();
        fileInfo.setType(fileType);
        /**上传文件*/
        FileInfo returnFileInfo = FileUtils.uploadFileInfo(file, fileInfo, FileEnum.FILE_STORE_TYPE_OSS);
        boolean isImage = FileUtils.isImage(file);
        /**存储文件到数据库*/
        insertFile(returnFileInfo, isImage);
        /**存储到加密属性文件信息*/
        String fileMd5 = returnFileInfo.getFileMd5();
//        String filePath = returnFileInfo.getFilePath();
//        insertFileEncrypt(fileMd5, filePath, FileEnum.FILE_STORE_TYPE_OSS, isImage);
        FileInfoEncryptVO vo = new FileInfoEncryptVO();
        vo.setFileMd5(fileMd5);
//        vo.setFilePath(filePath);
        return HttpResult.ok(Status.SUCCESS.getCode(), "", vo);
    }

    public HttpResult getImage(FileImageParam fileImageParam) {
        String lessenRatio = fileImageParam.getLessenRatio();
        String fileHeight = fileImageParam.getFileHeight();
        String fileWidth = fileImageParam.getFileWidth();
        boolean flag = true;
        if (fileImageParam.isLessen()) {
            if (org.apache.commons.lang3.StringUtils.isBlank(lessenRatio)) {
                lessenRatio = String.valueOf(NumberConstant.TWELVE);
            }
        } else {
            flag = false;
            if (org.apache.commons.lang3.StringUtils.isBlank(fileHeight)) {
                fileHeight = String.valueOf(NumberConstant.TWO_HUNDRED);

            }
            if (org.apache.commons.lang3.StringUtils.isBlank(fileWidth)) {
                fileWidth = String.valueOf(NumberConstant.TWO_HUNDRED);
            }
        }
        String returnImageAddress = FileUtils.getOSSAddressPrefix();
        LambdaQueryWrapper<kwfFileInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(com.sckw.file.model.kwfFileInfo::getMd5, fileImageParam.getFileKey())
                .orderByDesc(kwfFileInfo::getCreateTime);
        kwfFileInfo kwfFileInfo = fileInfoDao.selectList(
                wrapper).get(0);
        if (IMG_FORMAT.indexOf(kwfFileInfo.getFileSuffix()) == -1) {
            returnImageAddress = returnImageAddress + kwfFileInfo.getFilePath();
        } else {
            if (flag) {
                returnImageAddress = returnImageAddress + kwfFileInfo.getFilePath()
                        + FileDisposeLayoutEnum.OSS_LESSEN.getParameter() + lessenRatio;
            } else {
                returnImageAddress = returnImageAddress + kwfFileInfo.getFilePath()
                        + FileDisposeLayoutEnum.OSS_ABBREVIATE_LEFT.getParameter() + fileHeight
                        + FileDisposeLayoutEnum.OSS_ABBREVIATE_RIGHT.getParameter() + fileWidth;
            }
        }
        return HttpResult.ok("",returnImageAddress);
    }
}
