package com.persagy.dmp.file.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.common.utils.ResourceUtil;
import com.persagy.dmp.file.constant.FileCommonConst;
import com.persagy.dmp.file.context.OldFileAppContext;
import com.persagy.dmp.file.model.FileInfo;
import com.persagy.dmp.file.model.FileInfoCreator;
import com.persagy.dmp.file.model.FileMd5;
import com.persagy.dmp.file.model.FileMd5Creator;
import com.persagy.dmp.file.service.*;
import com.persagy.dmp.file.utils.FileStorageHelper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

/***
 * Description: 兼容旧的文件服务接口
 * @author : lijie
 * Update By lijie 2021/11/18 10:06
 */
@Controller
@RequiredArgsConstructor
public class CompatibleOldFileController {

    private static final String DOCUMENTATION= "doc/documentation.md";
    private static final String TEMPLATE= "doc/template.html";
    private static final String ERROR_HTML="<html><head>" +
            "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>" +
            "<title>image-service</title></head></html><body>{}</body>";
    private static final String INCORRECT_PARAMETERS="Incorrect parameters inputted.";
    private static final String FILE_EXISTED="File existed.";
    private static final String CAMEL_HTTP_RESPONSE_CODE="CamelHttpResponseCode";
    private static final Integer MAX_CHUCK_NUMBER=10000;
    private final CompatibleOldFileService compatibleOldFileService;
    private final IFileService fileService;
    private final FileMd5Service fileMd5Service;

    /***
     * Description: 获取接口文档
     * @param response : 响应体
     * @author : lijie
     * Update By lijie 2021/11/19 9:57
     */
    @GetMapping(value = "/doc",produces = {"text/html;charset=UTF-8"})
    public void doc(HttpServletResponse response) throws IOException {
        Resource[] docResources = ResourceUtil.getResource(DOCUMENTATION, DOCUMENTATION);
        Resource[] templateResources = ResourceUtil.getResource(TEMPLATE, TEMPLATE);
        if (templateResources.length<=0
                || docResources.length<=0
                || !templateResources[0].exists()
                || !docResources[0].exists()){
            response.getWriter()
                    .write(StrUtil.format(ERROR_HTML,"暂无内容"));
            return;
        }
        Resource docResource = docResources[0];
        Resource templateResource = templateResources[0];
        String tempStr = FileUtil.readString(templateResource.getFile(), Charset.defaultCharset());
        String docContentStr = FileUtil.readString(docResource.getFile(), Charset.defaultCharset());
        response.getWriter()
                .write(StrUtil.replace(tempStr, "#replaceMePlease#", docContentStr));
    }
    /***
     * Description: 工单文件上传
     * @return : void
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @PostMapping(value = "/work_order/upload")
    @ResponseBody
    public String workOrderFileUpload(HttpServletRequest request) throws IOException {
        if(StrUtil.isBlank(OldFileAppContext.getContext().getKey())
                || StrUtil.isBlank(OldFileAppContext.getContext().getProjectId())
                || StrUtil.isBlank(OldFileAppContext.getContext().getWorkOrderId())
                || OldFileAppContext.getContext().getProjectId().length()<=2){
            return INCORRECT_PARAMETERS;
        }
        // 1.如果非覆盖且文件信息已存在则直接返回文件已存在
        if (!OldFileAppContext.getContext().getOverwrite() &&
                null!=fileService.load(OldFileAppContext.getContext().getKey())){
            return FILE_EXISTED;
        }
        compatibleOldFileService.uploadWorkOrderFile(request.getInputStream());
        return FileStorageHelper.oldSuccessResultCreate("");
    }
    /***
     * Description: 文件上传
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @GetMapping(value = "/work_order/get",produces = {"image/jpg"})
    public void workOrderFileGet(HttpServletResponse response) {
        compatibleOldFileService.getWorkOrderFile(response);
    }

    /***
     * Description: 图片上传
     * @return : void
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @PostMapping(value = "/common/image_upload")
    @ResponseBody
    public String imageUpload(HttpServletRequest request) throws IOException {
        if(StrUtil.isBlank(OldFileAppContext.getContext().getKey())){
            return INCORRECT_PARAMETERS;
        }
        compatibleOldFileService.uploadFile(request.getInputStream());
        return FileStorageHelper.oldSuccessResultCreate("");
    }
    /***
     * Description: 图片下载
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @GetMapping(value = "/common/image_get")
    public void imageGet(HttpServletResponse response) {
        compatibleOldFileService.getFile(response);
        response.setContentType("image/jpg");
    }
    /***
     * Description: 图片下载
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @GetMapping(value = "/common/svg_get")
    public void imageSvgGet(HttpServletResponse response) {
        compatibleOldFileService.getFile(response);
        response.setContentType("image/svg+xml");
    }
    /***
     * Description: 图片下载
     * @return : void
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @PostMapping(value = {"/common/images_list","/common/files_list"})
    @ResponseBody
    public JSONObject imagesList(@RequestBody(required = false) JSONObject param) {
        List<String> fileIds = compatibleOldFileService.listFiles(param);
        JSONObject result = new JSONObject();
        result.put("result", "success");
        result.put("Count", (long)fileIds.size());
        result.put("Content", fileIds);
        result.put("ResultMsg", "");
        return result;
    }
    /***
     * Description: 图片/文件删除
     * @return : void
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @PostMapping(value = {"/common/images_delete","/common/files_delete"})
    @ResponseBody
    public JSONObject filesDelete(@RequestBody(required = false) JSONObject param){
        JSONObject result = new JSONObject();
        result.put("result", "success");
        result.put("Count", 0L);
        result.put("Content", new ArrayList<>());
        result.put("ResultMsg", "");
        JSONArray keys = param.getJSONArray("keys");
        if (null==keys || keys.size()<=0){
            return result;
        }
        List<String> deleteKeys = JSONArray.parseArray(JSON.toJSONString(keys), String.class);
        Set<String> fileIds = compatibleOldFileService.deleteFileByIds(deleteKeys);
        result.put("Count",fileIds.size());
        result.put("Content",fileIds);
        return result;
    }
    /***
     * Description: 更改图片/文件key
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @PostMapping(value = {"/common/image_key_change","/common/files_key_change"})
    @ResponseBody
    public JSONObject keyChange(@RequestBody(required = false) JSONObject param){
        JSONObject result = new JSONObject();
        result.put("result", "success");
        result.put("ResultMsg", "");
        if (StrUtil.isBlank(OldFileAppContext.getContext().getSystemId())
            || StrUtil.isBlank(param.getString("from"))
            || StrUtil.isBlank(param.getString("to"))){
            result.put("result", "failed");
            result.put("ResultMsg","Incorrect parameters inputted.");
            return result;
        }
        compatibleOldFileService.fileKeyChange(param.getString("from"),param.getString("to"));
        return result;
    }

    /***
     * Description: 文件上传
     * @return : void
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @PostMapping(value = "/common/file_upload")
    @ResponseBody
    public String fileUpload(HttpServletRequest request) throws IOException {
        if(StrUtil.isBlank(OldFileAppContext.getContext().getKey())){
            return INCORRECT_PARAMETERS;
        }
        compatibleOldFileService.uploadFile(request.getInputStream());
        return FileStorageHelper.oldSuccessResultCreate("");
    }

    /***
     * Description: 文件下载
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @GetMapping(value = "/common/file_get/{key}")
    public void fileGetByKey(@PathVariable(value = "key",required = false) String key,HttpServletResponse response) {
        if (StrUtil.isNotBlank(key)){
            OldFileAppContext.getContext().setKey(key);
        }
        compatibleOldFileService.getFile(response);
        response.setContentType("application/octet-stream");
    }
    /***
     * Description: 文件下载
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @GetMapping(value = "/common/file_get")
    public void fileGet(HttpServletResponse response) {
        compatibleOldFileService.getFile(response);
        response.setContentType("application/octet-stream");
    }
    /***
     * Description: 断点续传的注册
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @PostMapping(value = "/common/register_multipart_upload")
    @ResponseBody
    @Transactional(rollbackFor = BusinessException.class)
    public String registerMultipartUpload() {
        if (StrUtil.isBlank(OldFileAppContext.getContext().getSystemId())
            || StrUtil.isBlank(OldFileAppContext.getContext().getKey())){
            return INCORRECT_PARAMETERS;
        }
        // 1.如果非覆盖且文件信息已存在则直接返回文件已存在
        FileInfo fileInfo = fileService.load(OldFileAppContext.getContext().getKey());
        if (!OldFileAppContext.getContext().getOverwrite() && null!=fileInfo){
            return FILE_EXISTED;
        }
        String uploadId = compatibleOldFileService.registerMultipartUpload();
        JSONObject jsonObj = new JSONObject();
        if (StrUtil.isBlank(uploadId)){
            jsonObj.put("Result", "failure");
            jsonObj.put("ResultMsg", "获取UploadId失败");
            return jsonObj.toJSONString();
        }
        jsonObj.put("Result", "success");
        jsonObj.put("ResultMsg", "");
        jsonObj.put("UploadId", uploadId);
        return jsonObj.toJSONString();
    }
    /***
     * Description: 断点续传的注册
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @SneakyThrows
    @RequestMapping(value = "/common/multipart_upload",consumes = {MediaType.MULTIPART_FORM_DATA})
    @ResponseBody
    @Transactional(rollbackFor = BusinessException.class)
    public String multipartUpload(@RequestParam(value = "file",required = false) MultipartFile file,
                                  @RequestParam(value = "uploadId",required = false) String uploadId,
                                  @RequestParam(value = "chunkNumber",required = false) Integer chunkNumber,
                                  @RequestParam(value = "chunkSize",required = false) Integer chunkSize,
                                  @RequestParam(value = "totalSize",required = false) Long totalSize,
                                  @RequestParam(value = "filename",required = false) String fileName,
                                  @RequestParam(value = "totalChunks",required = false) Integer totalChunks,
                                  @RequestParam(value = "md5",required = false) String md5,
                                  HttpServletResponse response) {
        if(null==file || null==uploadId || null==chunkNumber || null==chunkSize || null == md5 || null==totalSize){
            response.setIntHeader(CAMEL_HTTP_RESPONSE_CODE, 500);
            return INCORRECT_PARAMETERS;
        }
        if(chunkNumber < 1 || chunkNumber > MAX_CHUCK_NUMBER){
            response.setIntHeader(CAMEL_HTTP_RESPONSE_CODE, 500);
            return "Invalid chunkSize.";
        }
        FileInfo fileInfo = getFileInfoByUploadId(uploadId);
        if (null==fileInfo){
            response.setIntHeader(CAMEL_HTTP_RESPONSE_CODE, 500);
            return "UploadId has expired.";
        }
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "success");
        jsonObj.put("ResultMsg", "");
        jsonObj.put("TotalCount", 0);
        // 1.根据文件md5判断是否已经存在
        FileMd5 fileMd5 = fileMd5Service.queryFileMd5ByFileMd5(md5);
        // 2.更新fileInfo
        if (StrUtil.isBlank(fileInfo.getFileMd5())){
            fileInfo.setFileMd5(md5);
            if (StrUtil.isNotBlank(fileName)){
                fileInfo.setFileName(fileName);
            }
            fileService.updateById(fileInfo);
        }
        if (ObjectUtil.isNotEmpty(fileMd5) && FileCommonConst.UPLOAD_SUCCESS.equals(fileMd5.getUploadStatus())){
            // MD5文件已存在
            jsonObj.put("TotalCount", totalChunks);
            return jsonObj.toJSONString();
        }
        // 3.先上传分片信息
        IFileStorageService service = FileStorageFactory.getService();
        service.uploadChuck(service.getChuckBucketName(null),md5,file.getInputStream(),totalChunks,chunkNumber);
        // 4.如果MD5信息没有的话则创建一个
        if (ObjectUtil.isEmpty(fileMd5)){
            FileMd5 fileMd5Create = FileMd5Creator.of(fileInfo.getCreator(), md5, FileCommonConst.UPLOAD_PART);
            fileMd5Create.setFileSize(totalSize);
            fileMd5Create.setCreator(FileInfoCreator.formatBucket(fileInfo.getCreator()));
            fileMd5Create.setId(md5);
            fileMd5Service.saveOrUpdate(fileMd5Create);
        }
        Map<Integer,String> okChunkMap = service.mapChunkObjectNames(service.getChuckBucketName(null), md5);
        jsonObj.put("TotalCount", okChunkMap.size());
        return jsonObj.toJSONString();
    }
    /***
     * Description: 断点续传的合并
     * @author : lijie
     * Update By lijie 2021/11/19 9:58
     */
    @SneakyThrows
    @RequestMapping(value = "/common/merge_multipart")
    @ResponseBody
    @Transactional(rollbackFor = BusinessException.class)
    public String mergeMultipart() {
        if (StrUtil.isBlank(OldFileAppContext.getContext().getUploadId())){
            return INCORRECT_PARAMETERS;
        }
        FileInfo fileInfo = getFileInfoByUploadId(OldFileAppContext.getContext().getUploadId());
        if (null==fileInfo
                || StrUtil.isBlank(fileInfo.getCreator())
                || StrUtil.isBlank(fileInfo.getFileMd5())){
            return INCORRECT_PARAMETERS;
        }
        FileMd5 fileMd5 = fileMd5Service.queryFileMd5ByFileMd5(fileInfo.getFileMd5());
        if (ObjectUtil.isEmpty(fileMd5)){
            return INCORRECT_PARAMETERS;
        }
        IFileStorageService service = FileStorageFactory.getService();
        if (FileCommonConst.UPLOAD_SUCCESS.equals(fileMd5.getUploadStatus())
            && StrUtil.isNotBlank(fileMd5.getFileBucket())
            && StrUtil.isNotBlank(fileMd5.getFilePath())
            && service.exists(fileMd5.getFileBucket(),fileMd5.getFilePath())){
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("Result", "success");
            jsonObj.put("ResultMsg", "");
            return jsonObj.toJSONString();
        }
        List<String> chunks = service
                .listObjectNames(service.getChuckBucketName(null),fileInfo.getFileMd5());
        if (CollUtil.isEmpty(chunks)){
            return INCORRECT_PARAMETERS;
        }
        chunks = chunks.stream()
                .sorted(Comparator.comparingInt(n -> Integer.parseInt(StrUtil.subBetween(n, StrUtil.SLASH, StrUtil.DOT))))
                .collect(Collectors.toList());
        fileMd5Service.ensureFilePath(fileMd5,fileInfo.getFileName());
        boolean composeObject = service.composeObject(fileInfo.getCreator(), chunks, fileMd5.getFilePath());
        JSONObject jsonObj = new JSONObject();
        if (composeObject){
            // 更新Md5值
            fileMd5.setUploadStatus(FileCommonConst.UPLOAD_SUCCESS);
            fileMd5Service.updateById(fileMd5);
            jsonObj.put("Result", "success");
            jsonObj.put("ResultMsg", "");
        }else {
            jsonObj.put("Result", "failure");
            jsonObj.put("ResultMsg", "合并失败");
        }
        return jsonObj.toJSONString();
    }
    /***
     * Description: 根据上传id查询文件信息
     * @param uploadId : 上传id
     * @return : com.persagy.dmp.file.model.FileInfo
     * @author : lijie
     * Update By 2021/12/9 20:17
     */
    private FileInfo getFileInfoByUploadId(String uploadId) {
        LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FileInfo::getBusinessId,uploadId);
        queryWrapper.eq(FileInfo::getValid,Boolean.TRUE);
        return fileService.getOne(queryWrapper,false);
    }


}
