package com.izouma.nineth.web;

import com.izouma.nineth.domain.FileObject;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.service.storage.StorageService;
import com.izouma.nineth.utils.ImageUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.pngquant.PngQuant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;


@RestController
@RequestMapping("/upload")
@Slf4j
public class FileUploadController {

    @Autowired
    private StorageService storageService;

    @PostMapping("/file")
    public String uploadFile(@RequestParam("file") MultipartFile file,
                             @RequestParam(value = "path", required = false) String path,
                             @RequestParam(value = "compress", defaultValue = "false") boolean compress,
                             @RequestParam(value = "width", required = false) Integer width,
                             @RequestParam(value = "height", required = false) Integer height) throws IOException {

        String ext = Optional.ofNullable(FilenameUtils.getExtension(file.getOriginalFilename())).orElse("")
                .toLowerCase().replace("jpeg", "jpg");
        if (path == null) {
            String basePath = Optional.ofNullable(file.getContentType()).orElse("application").split("/")[0];
            path = basePath + "/" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())
                    + RandomStringUtils.randomAlphabetic(8)
                    + "." + ext;
        }

        if (Pattern.matches("(jpg|png)", ext) && (compress || width != null || height != null)) {
            if (width == null && height == null) {
                width = Integer.MAX_VALUE;
                height = Integer.MAX_VALUE;
            } else if (height == null) {
                height = Integer.MAX_VALUE;
            } else if (width == null) {
                width = Integer.MAX_VALUE;
            }
            BufferedImage img = null;
            if ("jpg".equals(ext)) {
                img = ImageUtils.resizeJpg(file.getInputStream(), width, height, compress);
            } else if ("png".equals(ext)) {
                img = ImageUtils.resizePng(file.getInputStream(), width, height, compress);
            }
            Objects.requireNonNull(img, "图片压缩失败");
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(img, ext, os);
            InputStream is = new ByteArrayInputStream(os.toByteArray());
            return storageService.uploadFromInputStream(is, path);
        }
        return storageService.uploadFromInputStream(file.getInputStream(), path);
    }

    @PostMapping("/base64")
    public String uploadImage(@RequestParam("base64") String base64,
                              @RequestParam(value = "path", required = false) String path) {
        base64 = base64.substring(base64.indexOf(',') + 1);
        Base64.Decoder decoder = Base64.getDecoder();
        if (path == null) {
            String ext = ".jpg";
            try {
                InputStream is = new ByteArrayInputStream(decoder.decode(base64.getBytes()));
                String type = URLConnection.guessContentTypeFromStream(is);
                ext = type.replace("image/", ".").replace("jpeg", "jpg");
            } catch (Exception ignored) {
            }
            path = "image/" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())
                    + RandomStringUtils.randomAlphabetic(8) + ext;
        }
        InputStream is;
        try {
            is = new ByteArrayInputStream(decoder.decode(base64.getBytes()));
        } catch (Exception e) {
            log.error("上传失败", e);
            throw new BusinessException("上传失败", e.getMessage());
        }
        return storageService.uploadFromInputStream(is, path);
    }

    @PostMapping("/fileObject")
    public FileObject uploadFileObject(@RequestParam("file") MultipartFile file,
                                       @RequestParam(value = "compress", defaultValue = "false") boolean compress,
                                       @RequestParam(value = "width", required = false) Integer width,
                                       @RequestParam(value = "height", required = false) Integer height) throws IOException {
        String ext = Optional.ofNullable(FilenameUtils.getExtension(file.getOriginalFilename())).orElse("")
                .toLowerCase().replace("jpeg", "jpg");
        String[] extList = new String[]{"jpg", "jpeg", "png", "gif", "mp4"};
        if (!ArrayUtils.contains(extList, ext.toLowerCase())) {
            throw new BusinessException("仅支持" + StringUtils.join(extList, "、") + "格式");
        }
        File tmpFile = File.createTempFile("upload_", "." + ext);
        FileUtils.copyToFile(file.getInputStream(), tmpFile);
        String path = "nft/" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())
                + RandomStringUtils.randomAlphabetic(8)
                + "." + ext;
        String url;


        if (Pattern.matches("(jpg|png)", ext) && (compress || width != null || height != null)) {
            if (width == null && height == null) {
                width = Integer.MAX_VALUE;
                height = Integer.MAX_VALUE;
            } else if (height == null) {
                height = Integer.MAX_VALUE;
            } else if (width == null) {
                width = Integer.MAX_VALUE;
            }
            BufferedImage img;
            if ("jpg".equals(ext)) {
                img = ImageUtils.resizeJpg(new FileInputStream(tmpFile), width, height, compress);
            } else {
                img = ImageUtils.resizePng(new FileInputStream(tmpFile), width, height, compress);
            }
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(img, ext, os);
            InputStream is = new ByteArrayInputStream(os.toByteArray());
            url = storageService.uploadFromInputStream(is, path);
        } else {
            url = storageService.uploadFromInputStream(new FileInputStream(tmpFile), path);
        }

        String thumbUrl = null;


        if ("mp4".equalsIgnoreCase(ext)) {
            String thumbPath = "thumb_image/" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())
                    + RandomStringUtils.randomAlphabetic(8) + ".jpg";

            FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(tmpFile);
            Java2DFrameConverter frameConverter = new Java2DFrameConverter();
            try {
                frameGrabber.start();

                Frame frame = null;
                while (frame == null) {
                    frame = frameGrabber.grabKeyFrame();
                }
                Objects.requireNonNull(frame, "获取视频缩略图失败");
                BufferedImage thumbBi = frameConverter.convert(frame);

                BufferedImage thumbResized = ImageUtils.resizeJpg(thumbBi, 1000, 1000, false);
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                ImageIO.write(thumbResized, "jpg", os);
                InputStream is = new ByteArrayInputStream(os.toByteArray());
                thumbUrl = storageService.uploadFromInputStream(is, thumbPath);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                frameGrabber.stop();
            }
        }

        return new FileObject(file.getOriginalFilename(), url, thumbUrl, file.getContentType());
    }
}
