package com.nanjing.water.host.controller.base;

import com.google.gson.Gson;
import com.nanjing.water.common.ConstantFactory;
import com.nanjing.water.common.ExecutedResult;
import com.nanjing.water.common.config.SysConfig;
import com.nanjing.water.common.enums.EContentType;
import com.nanjing.water.common.util.FileUtil;
import com.nanjing.water.common.util.LocalDateTimeUtil;
import com.nanjing.water.common.util.ParameterUtil;
import com.nanjing.water.common.util.StringUtil;
import com.nanjing.water.common.validator.EParameterValidateType;
import com.nanjing.water.entity.dto.NameValueDTO;
import com.nanjing.water.entity.dto.UploadResultVo;
import com.nanjing.water.host.api.NonLogin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 01.文件上传
 * @author lin.liu
 * @date 2021/11/26
 * @order 01
 */
@NonLogin
@RestController
@RequestMapping("/file")
public class FileController {
    @Autowired
    private ResourceLoader resourceLoader;

    /**
     * 上传文件
     *
     * @param requestMultipart 复杂请求对象
     */
    @PostMapping(value = "upload")
    @NonLogin
    public ExecutedResult<UploadResultVo> upload(MultipartHttpServletRequest requestMultipart, HttpServletRequest request) throws Exception {
        UploadResultVo result = this.defaultResult();

        Map<String, MultipartFile> mapFile = requestMultipart.getFileMap();
        if (mapFile.isEmpty()) {
            return ExecutedResult.failed("请求中没有找到文件。");
        }
        if (mapFile.size() > 1) {
            return ExecutedResult.failed("仅支持单个文件上传。");
        }
        String fileId = requestMultipart.getParameter("fileId");
        String toPath = requestMultipart.getParameter("path");
        if (StringUtil.isNullOrEmpty(toPath)) {
            return ExecutedResult.failed(ParameterUtil.named("path") + EParameterValidateType.NOT_NULLOREMPTY.getDesc());
        }
        Iterator<Map.Entry<String, MultipartFile>> iterator = mapFile.entrySet().iterator();
        Map.Entry<String, MultipartFile> fileEntry = iterator.next();

        MultipartFile file = fileEntry.getValue();
        String contentType = file.getContentType();

        InputStream fConfig = resourceLoader.getResource("classpath:upload_allow.json").getInputStream();
        String allowStr = FileUtil.readFileContent(fConfig);
        Map<String, String> map = new Gson().fromJson(allowStr, Map.class);
        List<NameValueDTO<String>> listConfig = map.entrySet().stream().map(c -> new NameValueDTO<>(c.getKey(), c.getValue()))
                .collect(Collectors.toList());
        NameValueDTO<String> findConfig = listConfig.stream().filter(c -> c.getName().equals(contentType))
                .findAny().orElse(null);
        if (Objects.isNull(findConfig)) {
            return ExecutedResult.failed(file.getOriginalFilename() + ", 不支持的文件类型。" + file.getContentType());
        }
        if (null != file.getOriginalFilename() && !file.getOriginalFilename().contains(findConfig.getValue())) {
            return ExecutedResult.failed(file.getOriginalFilename() + ", 不支持的文件类型。" + file.getContentType());
        }

        EContentType type = EContentType.getByValue(file.getContentType());
        String filePath = "/" + toPath + "/files/";
        boolean isMultimedia = false;
        long size = file.getSize() / ConstantFactory.FILE_UNIT;
        if (null != type) {
            switch (type) {
                case GIF:
                case JPG:
                case PNG:
                case JPEG:
                    filePath = "/" + toPath + "/images/";
                    if (size > ConstantFactory.FILE_SIZE_20M) {
                        return ExecutedResult.failed("图片不能超过20M!");
                    }
                    break;
                case GZ:
                case RAR:
                case X7Z:
                case ZIP:
                    if (size > ConstantFactory.FILE_SIZE_20M) {
                        return ExecutedResult.failed("压缩文件不能超过20M!");
                    }
                    break;

                case MP3:
                    isMultimedia = true;
                    if (size > ConstantFactory.FILE_SIZE_20M) {
                        return ExecutedResult.failed("音频文件不能超过20M!");
                    }
                    break;
                case MP4:
                    isMultimedia = true;
                    if (size > ConstantFactory.FILE_SIZE_20M) {
                        return ExecutedResult.failed("视频文件不能超过20M!");
                    }
                    break;
                default:
                    break;
            }
        }
        filePath += LocalDateTimeUtil.toFormatString(LocalDateTimeUtil.todayFirst(), "yyyyMMdd") + "/";
        InputStream fsIn = file.getInputStream();

        String fileSuffix = "." + findConfig.getValue();
        File directory = new File(SysConfig.fileUploadBasic() + filePath);
        //如果路径不存在,新建
        if (!directory.exists() && !directory.isDirectory()) {
            directory.mkdirs();
        }

        String newFileName = filePath + StringUtil.md5(file.getOriginalFilename() + LocalDateTimeUtil.nowDateTimeFullStr() + SysConfig.fileDisturbStr()) + fileSuffix;
        File newFile = new File(SysConfig.fileUploadBasic() + newFileName);
        if (!newFile.exists()) {
            //每次写入1MB
            byte[] buffer = new byte[ConstantFactory.FILE_UNIT * ConstantFactory.FILE_SIZE_1M];
            int len;
            FileOutputStream fsOut = null;
            try {
                fsOut = new FileOutputStream(newFile);
                while ((len = fsIn.read(buffer)) != -1) {
                    fsOut.write(buffer, 0, len);
                    fsOut.flush();
                }
            } finally {
                if (Objects.nonNull(fsOut)) {
                    fsOut.close();
                }
            }
        }
        if (Objects.nonNull(fsIn)) {
            fsIn.close();
        }

        result.setFileId(fileId);
        result.setName(file.getOriginalFilename());
        result.setNewFileName(newFileName);
        result.setSize(size);
        result.setFileType(findConfig.getValue());
        result.setContentType(findConfig.getName());
        result.setNewFileView(SysConfig.fileViewBasic() + newFileName);

        file = null;
        newFile = null;

        return ExecutedResult.success(result);
    }


    private UploadResultVo defaultResult() {
        UploadResultVo result = new UploadResultVo();
        result.setFileId("");
        result.setName("");
        result.setNewFileName("");
        result.setNewFileView("");
        result.setFileType("");
        result.setContentType("");
        result.setSize(0L);
        return result;
    }
}