package cn.wisenergy.chnmuseum.party.web.controller;

import cn.hutool.core.util.ArrayUtil;
import cn.wisenergy.chnmuseum.party.common.dfs.FastDFSUtils;
import cn.wisenergy.chnmuseum.party.common.enums.*;
import cn.wisenergy.chnmuseum.party.common.mvc.InterfaceException;
import cn.wisenergy.chnmuseum.party.common.util.TimeUtils;
import cn.wisenergy.chnmuseum.party.common.vo.BatchUploadResVO;
import cn.wisenergy.chnmuseum.party.common.vo.ImageUploadResult;
import cn.wisenergy.chnmuseum.party.model.Asset;
import cn.wisenergy.chnmuseum.party.service.AssetService;
import cn.wisenergy.chnmuseum.party.web.controller.base.BaseController;
import com.github.tobato.fastdfs.domain.fdfs.FileInfo;
import com.github.tobato.fastdfs.domain.fdfs.MetaData;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 文件上传 前端控制器
 * </p>
 *
 * @author Danny Lee
 * @since 2021-03-16
 */
@Slf4j
@RestController
@RequestMapping("/file")
@Api(tags = {"文件上传接口"})
public class FileUploadController extends BaseController {

    private static final String[] IMAGE_TYPE = new String[]{"JPG", "JPEG", "PNG", "BMP", "WBMP"};
    private static final String[] AUDIO_TYPE = new String[]{"MP3", "AAC", "WMA", "FLAC", "RM", "OGG"};
    private static final String[] VIDEO_TYPE = new String[]{"MP4", "FLV"};
    private static final String[] DOC_TYPE = new String[]{"PDF", ".TXT", "DOC", "DOCX", "PPT", "PPTX", "XLS", "XLSX"};
    private static final String[] DATUM_TYPE = ArrayUtil.addAll(DOC_TYPE, IMAGE_TYPE, AUDIO_TYPE, VIDEO_TYPE);

    @Resource
    private AssetService assetService;

    @ApiOperation(value = "根据文件ID刪除文件", notes = "根据文件ID刪除文件")
    @DeleteMapping(value = "/delete/{id}")
    @RequiresPermissions("file:delete")
    public ResponseEntity<Map<String, Object>> delete(@PathVariable(value = "id") String id) {
        final Asset asset = assetService.getById(id);
        if (asset != null) {
            final String fileUrl = asset.getFileUrl();
            FastDFSUtils.deleteFile(fileUrl);
            this.assetService.removeById(id);
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }

    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "资料", paramType = "form", dataType = "__file", collectionFormat = "array", allowMultiple = true)
    })
    @PostMapping(value = "/datum/upload", headers = "content-type=multipart/form-data", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ApiOperation(value = "展板资料上传", notes = "展板资料上传")
    public Map<String, Object> uploadFile(@RequestPart(value = "file", required = false) MultipartFile[] files) throws IOException {
        if (files.length == 0) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "没有文件可供上传");
        }
        int successCount = 0;
        int failureCount = 0;
        List<BatchUploadResVO.HandleResult> handleList = new ArrayList<>();

        List<Asset> datumUrlList = new ArrayList<>();
        for (MultipartFile file : files) {
            // 当前维度表下线结果
            BatchUploadResVO.HandleResult handleResult = new BatchUploadResVO.HandleResult();
            // 原始文件名
            String originalFilename = file.getOriginalFilename();
            if (StringUtils.isBlank(originalFilename)) {
                handleResult.setFileName("");
                handleResult.setFileUrl("");
                handleResult.setHandleResult(HANDLE_STATUS_ENUM.FAILURE.getName());
                handleResult.setDescription("文件名为空");
                failureCount++;
                continue;
            }

            String extName = FilenameUtils.getExtension(originalFilename);
            boolean anyMatch = Arrays.stream(DATUM_TYPE).anyMatch(s -> Objects.equals(s, extName.toUpperCase()));
            if (anyMatch) {
                final Asset asset = Asset.builder()
                        .fileName(originalFilename)
                        .fileExtName(extName)
                        .fileCat(FileCatEnum.EXHIBITION_BOARD_DATUM.name())
                        .build();

                String fileUrl = null;
                final Set<MetaData> metaDataSet = new HashSet<>();
                metaDataSet.add(new MetaData("fileName", originalFilename));
                if (Arrays.stream(VIDEO_TYPE).anyMatch(s -> Objects.equals(s, extName.toUpperCase()))) {
                    metaDataSet.add(new MetaData("fileType", FileTypeEnum.VIDEO.name()));
                    handleResult.setFileType(FileTypeEnum.VIDEO.name());
                    asset.setFileType(FileTypeEnum.VIDEO.name());
                    fileUrl = FastDFSUtils.uploadVideo(file.getInputStream(), file.getSize(), originalFilename, metaDataSet);
                    final Set<MetaData> fileMetaData = FastDFSUtils.getFileMetaData(fileUrl);
                    String md5 = fileMetaData.stream().filter(x -> "MD5".equals(x.getName())).map(MetaData::getValue).findFirst().get();
                    asset.setMd5(md5);
                } else if (Arrays.stream(AUDIO_TYPE).anyMatch(s -> Objects.equals(s, extName.toUpperCase()))) {
                    metaDataSet.add(new MetaData("fileType", FileTypeEnum.AUDIO.name()));
                    handleResult.setFileType(FileTypeEnum.AUDIO.name());
                    asset.setFileType(FileTypeEnum.AUDIO.name());
                    fileUrl = FastDFSUtils.uploadFile(file.getInputStream(), file.getSize(), originalFilename, metaDataSet);
                } else if (Arrays.stream(IMAGE_TYPE).anyMatch(s -> Objects.equals(s, extName.toUpperCase()))) {
                    metaDataSet.add(new MetaData("fileType", FileTypeEnum.IMAGE.name()));
                    handleResult.setFileType(FileTypeEnum.IMAGE.name());
                    asset.setFileType(FileTypeEnum.IMAGE.name());
                    fileUrl = FastDFSUtils.uploadFile(file.getInputStream(), file.getSize(), originalFilename, metaDataSet);
                    asset.setThumbnail(fileUrl);
                } else if (Arrays.stream(DOC_TYPE).anyMatch(s -> Objects.equals(s, extName.toUpperCase()))) {
                    metaDataSet.add(new MetaData("fileType", FileTypeEnum.DOCUMENT.name()));
                    handleResult.setFileType(FileTypeEnum.DOCUMENT.name());
                    asset.setFileType(FileTypeEnum.DOCUMENT.name());
                    fileUrl = FastDFSUtils.uploadFile(file.getInputStream(), file.getSize(), originalFilename, metaDataSet);
                }
                final FileInfo fileInfo = FastDFSUtils.getFileInfo(fileUrl);
                final LocalDateTime createTime = TimeUtils.getDateTimeOfTimestamp(fileInfo.getCreateTime());
                asset.setFileSize(fileInfo.getFileSize());
                asset.setFileUrl(fileUrl);
                asset.setFileCat(FileCatEnum.EXHIBITION_BOARD_DATUM.name());
                asset.setCreateTime(createTime);
                asset.setUpdateTime(createTime);
                this.assetService.save(asset);
                datumUrlList.add(asset);

                handleResult.setFileUrl(fileUrl);
                handleResult.setFileName(originalFilename);
                handleResult.setHandleResult(HANDLE_STATUS_ENUM.SUCCESS.getName());
                handleResult.setDescription("");
                successCount++;
            } else {
                handleResult.setFileUrl("");
                handleResult.setDescription("文件" + originalFilename + "格式不支持");
                failureCount++;
            }
            // 设置处理的业务表信息
            handleList.add(handleResult);
        }
        BatchUploadResVO batchUploadResVO = new BatchUploadResVO();
        batchUploadResVO.setFailureCount(failureCount);
        batchUploadResVO.setSuccessCount(successCount);
        batchUploadResVO.setTotal(files.length);
        batchUploadResVO.setHandleList(handleList);
        batchUploadResVO.setFileList(datumUrlList);
        return getResult(batchUploadResVO);
    }

    @PostMapping(value = "/image/upload", headers = "content-type=multipart/form-data", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @RequiresPermissions("file:image:upload")
    @ApiOperation(value = "单图片上传(封面/缩略图)", notes = "单图片上传(封面/缩略图)")
    public Map<String, Object> uploadImage(@RequestParam(value = "file") MultipartFile uploadFile) throws Exception {
        if (uploadFile == null || uploadFile.getSize() == 0) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "没有文件可供上传");
        }
        String fileName = uploadFile.getOriginalFilename();
        String extension = FilenameUtils.getExtension(fileName);
        if (StringUtils.isBlank(extension)) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "文件格式不支持");
        }
        boolean anyMatch = Arrays.stream(IMAGE_TYPE).anyMatch(s -> Objects.equals(s, extension.toUpperCase()));
        if (!anyMatch) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "文件格式不支持");
        }

        final Set<MetaData> metaDataSet = new HashSet<>();
        metaDataSet.add(new MetaData("fileName", fileName));
        metaDataSet.add(new MetaData("fileType", FileTypeEnum.IMAGE.name()));
        String url = FastDFSUtils.uploadFile(uploadFile.getInputStream(), uploadFile.getSize(), fileName, metaDataSet);
        ImageUploadResult imageUploadResult = new ImageUploadResult();
        imageUploadResult.setFileName(fileName);
        imageUploadResult.setFileExtName(extension);
        imageUploadResult.setFileSize(uploadFile.getSize());
        imageUploadResult.setUrl(url);
        return getResult(imageUploadResult);
    }

    @PostMapping(value = "/upload/allType", headers = "content-type=multipart/form-data", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @RequiresPermissions("file:upload:allType")
    @ApiOperation(value = "单文件上传(不限格式)", notes = "单文件上传(不限格式)")
    public Map<String, Object> uploadAllType(@RequestParam(value = "file") MultipartFile uploadFile) throws Exception {
        if (uploadFile == null || uploadFile.getSize() == 0) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "没有文件可供上传");
        }
        String fileName = uploadFile.getOriginalFilename();
        String extension = FilenameUtils.getExtension(fileName);
        if (StringUtils.isBlank(extension)) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "文件格式不支持");
        }
        final Set<MetaData> metaDataSet = new HashSet<>();
        metaDataSet.add(new MetaData("fileName", fileName));
        String url = FastDFSUtils.uploadFile(uploadFile.getInputStream(), uploadFile.getSize(), fileName, metaDataSet);
        ImageUploadResult imageUploadResult = new ImageUploadResult();
        imageUploadResult.setFileName(fileName);
        imageUploadResult.setFileExtName(extension);
        imageUploadResult.setFileSize(uploadFile.getSize());
        imageUploadResult.setUrl(url);
        return getResult(imageUploadResult);
    }

    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "音频文件", paramType = "form", dataType = "__file", collectionFormat = "array", allowMultiple = true)
    })
    @PostMapping(value = "/audio/upload", headers = "content-type=multipart/form-data", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @RequiresPermissions("audio:upload")
    @ApiOperation(value = "多音频上传", notes = "多音频上传")
    public Map<String, Object> uploadAudio(@RequestPart(value = "file", required = false) MultipartFile[] files) throws IOException {
        if (files == null || files.length == 0) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "没有文件可供上传");
        }
        final boolean existChineseAudio = Arrays.stream(files).anyMatch(s -> Objects.requireNonNull(s.getOriginalFilename()).contains("汉语"));
        if (!existChineseAudio) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "必须包含汉语音频");
        }

        int successCount = 0;
        int failureCount = 0;
        List<BatchUploadResVO.HandleResult> handleList = new ArrayList<>();

        List<Asset> videoUrlList = new ArrayList<>();
        for (MultipartFile file : files) {
            // 当前维度表下线结果
            BatchUploadResVO.HandleResult handleResult = new BatchUploadResVO.HandleResult();
            // 原始文件名
            String originalFilename = file.getOriginalFilename();
            if (StringUtils.isBlank(originalFilename)) {
                handleResult.setFileName("");
                handleResult.setFileType(FileTypeEnum.AUDIO.getName());
                handleResult.setFileUrl("");
                handleResult.setHandleResult(HANDLE_STATUS_ENUM.FAILURE.getName());
                handleResult.setDescription("文件名为空");
                failureCount++;
                continue;
            }

            String extName = FilenameUtils.getExtension(originalFilename);
            boolean anyMatch = Arrays.stream(AUDIO_TYPE).anyMatch(s -> Objects.equals(s, extName.toUpperCase()));
            if (anyMatch) {
                String language = null;
                final Set<MetaData> metaDataSet = new HashSet<>();
                metaDataSet.add(new MetaData("fileName", originalFilename));
                metaDataSet.add(new MetaData("fileType", FileTypeEnum.AUDIO.name()));
                if (originalFilename.contains("汉语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.CHINESE.name()));
                    language = LanguageEnum.CHINESE.name();
                } else if (originalFilename.contains("英语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.ENGLISH.name()));
                    language = LanguageEnum.ENGLISH.name();
                } else if (originalFilename.contains("蒙语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.MONGOLIAN.name()));
                    language = LanguageEnum.MONGOLIAN.name();
                } else if (originalFilename.contains("藏语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.TIBETAN.name()));
                    language = LanguageEnum.TIBETAN.name();
                } else if (originalFilename.contains("维吾尔语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.UYGHUR.name()));
                    language = LanguageEnum.UYGHUR.name();
                }
                String fileUrl = FastDFSUtils.uploadFile(file.getInputStream(), file.getSize(), originalFilename, metaDataSet);
                final FileInfo fileInfo = FastDFSUtils.getFileInfo(fileUrl);
                final LocalDateTime createTime = TimeUtils.getDateTimeOfTimestamp(fileInfo.getCreateTime());
                final long fileSize = fileInfo.getFileSize();
                final Asset asset = Asset.builder()
                        .fileName(originalFilename)
                        .fileExtName(extName)
                        .fileType(FileTypeEnum.AUDIO.name())
                        .fileUrl(fileUrl)
                        .fileSize(fileSize)
                        .fileCat(FileCatEnum.EXHIBITION_BOARD_AUDIO.name())
                        .language(language)
                        .createTime(createTime)
                        .updateTime(createTime)
                        .build();
                this.assetService.save(asset);
                videoUrlList.add(asset);

                handleResult.setFileUrl(fileUrl);
                handleResult.setFileName(originalFilename);
                handleResult.setFileType(FileTypeEnum.AUDIO.getName());
                handleResult.setHandleResult(HANDLE_STATUS_ENUM.SUCCESS.getName());
                handleResult.setDescription("");
                successCount++;
            } else {
                handleResult.setFileUrl("");
                handleResult.setFileType(FileTypeEnum.AUDIO.getName());
                handleResult.setHandleResult(HANDLE_STATUS_ENUM.FAILURE.getName());
                handleResult.setDescription("文件" + originalFilename + "格式不支持");
                failureCount++;
            }
            // 设置处理的业务表信息
            handleList.add(handleResult);
        }

        BatchUploadResVO batchUploadResVO = new BatchUploadResVO();
        batchUploadResVO.setFailureCount(failureCount);
        batchUploadResVO.setSuccessCount(successCount);
        batchUploadResVO.setTotal(files.length);
        batchUploadResVO.setHandleList(handleList);
        batchUploadResVO.setFileList(videoUrlList);
        return getResult(batchUploadResVO);
    }

    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "视频文件", paramType = "form", dataType = "__file", collectionFormat = "array", allowMultiple = true)
    })
    @PostMapping(value = "/video/upload", headers = "content-type=multipart/form-data", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @RequiresPermissions("video:upload")
    @ApiOperation(value = "视频上传", notes = "视频上传")
    public Map<String, Object> uploadVideo(@RequestPart(value = "file", required = true) MultipartFile[] files) throws IOException {
        if (files == null || files.length == 0) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "没有文件可供上传");
        }
        final boolean matchChinese = Arrays.stream(files).anyMatch(s -> Objects.requireNonNull(s.getOriginalFilename()).contains("汉语"));
        if (!matchChinese) {
            throw new InterfaceException(RESPONSE_CODE_ENUM.SERVER_ERROR.getResultCode(), "文件必须包含汉语视频");
        }

        int successCount = 0;
        int failureCount = 0;
        List<BatchUploadResVO.HandleResult> handleList = new ArrayList<>();

        List<Asset> fileList = new ArrayList<>();
        for (MultipartFile file : files) {
            // 当前维度表下线结果
            BatchUploadResVO.HandleResult handleResult = new BatchUploadResVO.HandleResult();
            // 原始文件名
            String originalFilename = file.getOriginalFilename();
            if (StringUtils.isBlank(originalFilename)) {
                handleResult.setFileName("");
                handleResult.setFileType(FileTypeEnum.VIDEO.getName());
                handleResult.setFileUrl("");
                handleResult.setHandleResult(HANDLE_STATUS_ENUM.FAILURE.getName());
                handleResult.setDescription("文件名为空");
                failureCount++;
                continue;
            }

            String extName = FilenameUtils.getExtension(originalFilename);
            boolean anyMatch = Arrays.stream(VIDEO_TYPE).anyMatch(s -> Objects.equals(s, extName.toUpperCase()));
            if (anyMatch) {
                String language = null;
                final Set<MetaData> metaDataSet = new HashSet<>();
                metaDataSet.add(new MetaData("fileName", originalFilename));
                metaDataSet.add(new MetaData("fileType", FileTypeEnum.VIDEO.name()));
                if (originalFilename.contains("汉语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.CHINESE.name()));
                    language = LanguageEnum.CHINESE.name();
                } else if (originalFilename.contains("英语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.ENGLISH.name()));
                    language = LanguageEnum.ENGLISH.name();
                } else if (originalFilename.contains("蒙语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.MONGOLIAN.name()));
                    language = LanguageEnum.MONGOLIAN.name();
                } else if (originalFilename.contains("藏语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.TIBETAN.name()));
                    language = LanguageEnum.TIBETAN.name();
                } else if (originalFilename.contains("维吾尔语")) {
                    metaDataSet.add(new MetaData("language", LanguageEnum.UYGHUR.name()));
                    language = LanguageEnum.UYGHUR.name();
                }
                String fileUrl = FastDFSUtils.uploadVideo(file.getInputStream(), file.getSize(), originalFilename, metaDataSet);
                final FileInfo fileInfo = FastDFSUtils.getFileInfo(fileUrl);
                final Set<MetaData> fileMetaData = FastDFSUtils.getFileMetaData(fileUrl);
                String md5 = fileMetaData.stream().filter(x -> "MD5".equals(x.getName())).map(MetaData::getValue).findFirst().get();
                final LocalDateTime createTime = TimeUtils.getDateTimeOfTimestamp(fileInfo.getCreateTime());
                final long fileSize = fileInfo.getFileSize();
                final Asset asset = Asset.builder()
                        .fileName(originalFilename)
                        .fileExtName(extName)
                        .fileType(FileTypeEnum.AUDIO.name())
                        .fileSize(fileSize)
                        .fileUrl(fileUrl)
                        .fileCat(FileCatEnum.VIDEO_CONTENT.name())
                        .language(language)
                        .md5(md5)
                        .createTime(createTime)
                        .updateTime(createTime)
                        .build();
                this.assetService.save(asset);
                fileList.add(asset);

                handleResult.setFileUrl(fileUrl);
                handleResult.setFileName(originalFilename);
                handleResult.setFileType(FileTypeEnum.VIDEO.getName());
                handleResult.setHandleResult(HANDLE_STATUS_ENUM.SUCCESS.getName());
                handleResult.setHandleResult(HANDLE_STATUS_ENUM.SUCCESS.getName());
                handleResult.setDescription("");
                successCount++;
            } else {
                handleResult.setFileUrl("");
                handleResult.setHandleResult("上传失败");
                handleResult.setDescription("文件" + originalFilename + "格式不支持");
                failureCount++;
            }
            // 设置处理的业务表信息
            handleList.add(handleResult);
        }

        BatchUploadResVO batchUploadResVO = new BatchUploadResVO();
        batchUploadResVO.setFailureCount(failureCount);
        batchUploadResVO.setSuccessCount(successCount);
        batchUploadResVO.setTotal(files.length);
        batchUploadResVO.setHandleList(handleList);
        batchUploadResVO.setFileList(fileList);
        return getResult(batchUploadResVO);
    }

}