package com.tofly.feesapi.file.fileconfiguration;

import com.tofly.feesapi.common.exception.FeesErrorCode;
import com.tofly.feesapi.common.exception.FeesException;
import com.tofly.feesapi.common.ftp.config.FtpConfiguration;
import com.tofly.feesapi.common.ftp.config.FtpUtil;
import com.tofly.feesapi.file.entity.dto.FileUploadBase64Dto;
import com.tofly.feesapi.file.entity.dto.FileUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.tika.Tika;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;


@Slf4j
@Service
public class FTPFileStorageService implements FileStorageService {

    @Autowired
    private FtpConfiguration.FtpConfigProperties fileStorageProperties;

    // 储存文件
    public String storeFile(MultipartFile file, String path) {
        String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
        try {
            return storeFileByInputStream(path, fileName, file.getContentType(), file.getInputStream());
        } catch (Exception ex) {
            log.error("FTPFileStorageService.storeFile() 失败 =========> ", ex);
            throw new FeesException("当前新增对象中,文件上传失败,请重试", FeesErrorCode.BUSINESS_ERROR);
        }
    }

    public String storeFileBase64(String path, String originalFilename, String contentType, InputStream inputStream) {
        String fileName = StringUtils.cleanPath(Objects.requireNonNull(originalFilename));
        try {
            return storeFileByInputStream(path, fileName, contentType, inputStream);
        } catch (Exception ex) {
            log.error("FTPFileStorageService.storeFileBase64() 失败 =========> ", ex);
            throw new FeesException("当前新增对象中,文件上传失败,请重试", FeesErrorCode.BUSINESS_ERROR);
        }
    }

    @Override
    public FileUploadResponse upload(MultipartFile file, String Path) {
        try {
            long size = file.getSize();
            InputStream inputStream = file.getInputStream();
            if (Objects.requireNonNull(file.getContentType()).startsWith("image/")) {
                long maxSize = 1024 * 1024;//压缩大小
                //如果图片大于20M，则压缩大小不能超过5M
                if (size / maxSize > 20) {
                    maxSize = maxSize * 5;
                } else if (size / maxSize > 10) {
                    maxSize = maxSize * 2;
                }
                Map map = compressionImage(inputStream, maxSize);
                inputStream = (InputStream) map.get("inputStream");
                size = (long) map.get("size");
            }
            log.info("文件名:" + file.getOriginalFilename());
            String fileName = this.storeFileBase64(Path, file.getOriginalFilename(), file.getContentType(), inputStream);
            String staticPath = getPath(fileName);
            String realDownloadPath = null;
            // 获取request
            HttpServletRequest request =
                    ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // 生成下载接口的地址
            realDownloadPath =
                    request.getRequestURL().toString().replace("upload", "downloadFile/" + fileName);

            inputStream.close();
            return new FileUploadResponse(
                    fileName, realDownloadPath, file.getContentType(), size, staticPath);
        } catch (Exception e) {
            e.printStackTrace();
            throw new FeesException("当前新增对象中,文件上传失败,请重试", FeesErrorCode.BUSINESS_ERROR);
        }
    }

    @Override
    public FileUploadResponse upload(InputStream inputStream, String Path, String Filename, String contentType, long size) {
        try {
            String fileName = this.storeFileBase64(Path, Filename, contentType, inputStream);
            String staticPath = getPath(fileName);
            String realDownloadPath = null;
            // 获取request
            HttpServletRequest request =
                    ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // 生成下载接口的地址
            realDownloadPath =
                    request.getRequestURL().toString().replace("upload", "downloadFile/" + fileName);
            return new FileUploadResponse(
                    fileName, realDownloadPath, contentType, size, staticPath);
        } catch (Exception e) {
            e.printStackTrace();
            throw new FeesException("当前新增对象中,文件上传失败,请重试", FeesErrorCode.BUSINESS_ERROR);
        }
    }

    /**
     * 图片压缩
     *
     * @param inputStream 文件流
     * @return
     */
    private Map compressionImage(InputStream inputStream, long maxSize) {
        try {
            String fileTypNew = new Tika().detect(inputStream).split("/")[1];
            double compressionRation = 0.5;
            //原始图片填充
            BufferedImage orignalImage = ImageIO.read(inputStream);
            //压缩图片，得到新的高度，宽度
            int newWidth = (int) (orignalImage.getWidth() * compressionRation);
            int newHeight = (int) (orignalImage.getHeight() * compressionRation);
            //得到新的图片
            Image scImage = orignalImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
            //图片填充
            BufferedImage bufferedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
            bufferedImage.getGraphics().drawImage(scImage, 0, 0, null);
            //图片流输入
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, fileTypNew, outputStream);
            long size = outputStream.size();
            inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            if (size > maxSize) {
                return this.compressionImage(inputStream, maxSize);
            }
            Map map = new HashMap();
            map.put("inputStream", inputStream);
            map.put("size", size);
            return map;
        } catch (IOException e) {
            throw new FeesException("图片压缩失败，请重试！", FeesErrorCode.BUSINESS_ERROR);
        }
    }
    public FileUploadResponse uploadFileBase64(FileUploadBase64Dto file, String Path) {
        String fileBase64 = file.getBase64String();
        if (fileBase64.contains(",")) {
            fileBase64 = fileBase64.split(",")[1];
        }

        byte[] bytes = Base64.getDecoder().decode(fileBase64);
        InputStream inputStream = new ByteArrayInputStream(bytes);
        long fileSize = bytes.length;
        String fileName = this.storeFileBase64(Path, file.getOriginalFilename(), file.getContentType(), inputStream);
        String staticPath = getPath(fileName);
        String realDownloadPath = null;
        try {
            HttpServletRequest request =
                    ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            realDownloadPath = request.getRequestURL().toString().replace("upload", "downloadFile/" + fileName);
        } catch (Exception e) {
            log.error("FTPFileStorageService.uploadFileBase64() 失败 =========> ", e);
        }
        return new FileUploadResponse(fileName, realDownloadPath, file.getContentType(), fileSize, staticPath);
    }

    @Override
    public ResponseEntity<?> download(
            String fileName, HttpServletRequest request, HttpServletResponse response) {
        try {
            // 获取文件流
            InputStream inputStream = FtpUtil.getInputStremforFile(fileName);
            // 获取输出流
            OutputStream outputStream = response.getOutputStream();
            // 清空
            response.reset();
            // 写入流
            byte[] buffer = readAllBytes(inputStream);
            outputStream.write(buffer);
            outputStream.flush();
            inputStream.close();
            // 配置header
            response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
            response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(buffer.length));
            return ResponseEntity.status(HttpStatus.OK).body("download...");
        } catch (Exception e) {
            String err = fileName + "file not found";
            log.error(err);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(err);
        }
    }

    @Override
    public byte[] getByteDataforFile(String fileName) {
        try {
            byte[] buffer = FtpUtil.getByteDataforFile(fileName);

            return buffer;
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        } finally {

        }
    }

    public InputStream getByteDataforFileInputStream(String fileName) {
        try {
            InputStream inputStream = FtpUtil.getInputStremforFile(fileName);
            // 写入流
            return inputStream;
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 获取文件输入流（不关闭文件流）
     * @param fileName
     * @return
     */
    public InputStream getFileInputStream(String fileName) {
        try {
            //fileName = new String(fileName.getBytes("GBK"), "ISO-8859-1");
            InputStream inputStream = FtpUtil.getFileInputStream(fileName);
            // 写入流
            return inputStream;
        } catch (Exception e) {
            log.info(e.getMessage());
            return null;
        }
    }
    @Override
    public Boolean retrieveFile(String remotePath, OutputStream outputStream) {
        try {
            // 获取文件流
            InputStream inputStream = FtpUtil.getInputStremforFile(remotePath);
            if (IOUtils.copy(inputStream, outputStream) > -1) {
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error(e.getMessage());
            return false;
        }
    }
    @Override
    public String uploadFile(String Path, String fileName, String contentType, InputStream inputStream)
            throws IOException {
        try {
            return getPath(storeFileByInputStream(Path, fileName, contentType, inputStream));
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * 删除文件
     */
    @Override
    public void removeFile(String PathfileName) throws IOException {
        try {
            PathfileName = new String(PathfileName.getBytes("GBK"), StandardCharsets.ISO_8859_1);
            FtpUtil.deleteFile(PathfileName);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    @NotNull
    private String getPath(String fileName) {
        return fileStorageProperties
                .getServer()
                .concat(":")
                .concat(String.valueOf(fileStorageProperties.getPort()))
                .concat("/")
                .concat(fileName);
    }

    private String storeFileByInputStream(String Path,
                                          String fileName, String contentType, InputStream inputStream) throws IOException {
        if (!fileName.contains(".")) {
            fileName = RandomStringUtils.randomAlphabetic(15);
        } else {
            fileName = RandomStringUtils.randomAlphabetic(15) + fileName.substring(fileName.lastIndexOf("."));
        }

        Path += "/" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        String filesPath = Path + "/" + fileName;
        FtpUtil.uploadFile2Ftp(inputStream, filesPath);
        return filesPath;
    }

    private static byte[] readAllBytes(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int bytesRead;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        return outputStream.toByteArray();
    }
}
