package com.mcwl.web.controller.resource;

import com.mcwl.common.core.domain.AjaxResult;
import com.mcwl.common.core.domain.R;
import com.mcwl.common.utils.obs.ObsUtils;
import com.mcwl.resource.domain.request.RequestFile;
import com.mcwl.resource.domain.vo.FileVo;
import com.mcwl.resource.service.impl.FileServiceImpl;
import com.mcwl.web.controller.common.OssUtil;
import com.obs.services.ObsClient;
import com.obs.services.model.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 文件/图片
 *
 * @author DaiZibo
 * @date 2025/2/10
 * @apiNote
 */

@Slf4j
@Api(tags = "上传文件")
@RestController
@RequestMapping("/file")
public class FileController {

    @Autowired
    private FileServiceImpl fileService;

    @Autowired
    private ObsUtils obsUtils;

    @Autowired
    private ObsClient obsClient;

    @Value("${huawei.obs.bucketName}")
    private String bucketName;


    /***
     *
     * 图片
     * @param file
     * @return
     */
    @ApiOperation("上传图片")
    @PostMapping("/imgUpload")
    public AjaxResult imgUpload(@RequestParam MultipartFile file) {

        String s = OssUtil.uploadMultipartFile(file);
        String fileName = file.getOriginalFilename();
        Map<String, String> map = new HashMap<>();
        map.put("fileName", fileName);
        map.put("url", s);
        return AjaxResult.success(map);
    }


    /***
     *
     * 上传文件
     * @param file
     * @return
     */
    @ApiOperation("上传文件")
    @PostMapping("/fileUpload")
    public AjaxResult fileUpload(@RequestParam MultipartFile file) {

        log.info("开始上传文件...");
        Map<String, String> map = obsUtils.uploadFile(file);

        return AjaxResult.success(map);
    }


    /***
     *
     * zip
     * @param file
     * @return
     */
    @ApiOperation(value = "zip")
    @PostMapping("/zipUrlFile")
    public AjaxResult zipUrlFile(@RequestParam MultipartFile file) {
        String s = OssUtil.uploadMultipartFile(file);
        return AjaxResult.success(s);
    }


    /***
     *
     * 下载zip
     * @param file
     * @return
     */
    @ApiOperation(value = "zip")
    @PostMapping("/zipUrl")
    public AjaxResult zipUrl(@RequestParam MultipartFile file) {
        String s = OssUtil.uploadMultipartFile(file);
        return AjaxResult.success(s);
    }


    /**
     * 根据文件名查找是否存在
     * @return
     */
    @PostMapping("/selectFileName")
    @ApiOperation(value = "根据文件名查找是否存在并返回秘钥")
    public AjaxResult selectFileName(@RequestBody FileVo fileVo){

        return fileService.selectFileName(fileVo.getName(), fileVo.getType());
    }

    @PostMapping("/selectFile")
    @ApiOperation(value = "根据文件名查找是否存在")
    public AjaxResult selectFile(@RequestBody FileVo fileVo){

        return fileService.selectFile(fileVo.getName(), fileVo.getType());
    }

    @GetMapping("/download")
    @ApiOperation(value = "读取文件内容")
    public AjaxResult download(@RequestParam String name) throws IOException {

        obsUtils.download(name);

        return AjaxResult.success();
    }



    /**
     * 启动上传任务
     */
    @ApiOperation(value = "启动上传任务")
    @GetMapping("/getUploadId")
    public R getUploadId(@RequestParam("objectKey")String objectKey) {
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectKey);
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.addUserMetadata("property", "property-value");
        metadata.setContentType("text/plain");
        request.setMetadata(metadata);
        InitiateMultipartUploadResult result = obsClient.initiateMultipartUpload(request);
        String uploadId = result.getUploadId();
        return R.ok(uploadId);
    }

    /**
     * 分片上传
     */
    @ApiOperation(value = "分片上传")
    @PostMapping("/chunk")
    public R  splitFileUpload(
            @RequestParam("objectKey")String objectKey,
            @RequestParam("file") MultipartFile file,
            @RequestParam("chunk") int chunk,
            @RequestParam("uploadId") String uploadId) throws Exception {

        long startTime = System.currentTimeMillis(); // 记录开始时间
        try {
            File file1 = multipartFileToFile(file);
            Map<String, String> map = uploadChunk(uploadId, file1, chunk, objectKey);
            log.info("上传的文件大小: {}", file.getSize());
            return R.ok(map);
        } finally {
            long duration = System.currentTimeMillis() - startTime; // 计算耗时
            log.info("方法 splitFileUpload 执行耗时: {} ms", duration);
        }
    }

    /**
     * 合并上传
     */
    @ApiOperation(value = "合并上传")
    @PostMapping("/completeUpload")
    public R completeUpload(
            @RequestParam("objectKey")String objectKey,
            @RequestParam("uploadId") String uploadId,
            @RequestBody List<Map<String,String>> mapList
    ) {
        List<PartEtag> partEtags = new ArrayList<>();
        for(Map<String,String> map: mapList ){
            PartEtag part1 = new PartEtag();
            part1.setPartNumber(Integer.valueOf(map.get("partNumber")));
            part1.seteTag(map.get("etag"));
            partEtags.add(part1);
        }
        CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest(
                bucketName, objectKey, uploadId, partEtags);
        CompleteMultipartUploadResult result = obsClient.completeMultipartUpload(request);

        return R.ok(result);
    }

    /**
     * 取消任务上传
     */
    @ApiOperation(value = "取消任务上传")
    @GetMapping("/cancelUpload")
    public R cancelUpload(
            @RequestParam("objectKey")String objectKey,
            @RequestParam("uploadId") String uploadId
    ){
        AbortMultipartUploadRequest request = new AbortMultipartUploadRequest(bucketName, objectKey, uploadId);
        obsClient.abortMultipartUpload(request);

        return R.ok();
    }

    public Map<String,String> uploadChunk(String uploadId, File file,int chunk, String objectKey){
        // Endpoint以北京四为例，其他地区请按实际情况填写。
        Map<String,String> map = new HashMap<>();
        UploadPartRequest request = new UploadPartRequest(bucketName, objectKey);
        request.setUploadId(uploadId);
        request.setPartNumber(chunk);
        request.setFile(file);
        request.setPartSize(5 * 1024 * 1024L);
        UploadPartResult result = obsClient.uploadPart(request);
        map.put("etag",result.getEtag());
        map.put("partNumber",String.valueOf(result.getPartNumber()));
        log.info("分段上传完成：{}",chunk);
        return map;
    }

    /**
     * MultipartFile 转 File
     *
     * @param file
     * @throws Exception
     */
    public static File multipartFileToFile(MultipartFile file) throws Exception {

        File toFile = null;
        if (file.equals("") || file.getSize() <= 0) {
            file = null;
        } else {
            InputStream ins = null;
            ins = file.getInputStream();
            toFile = new File(file.getOriginalFilename());
            inputStreamToFile(ins, toFile);
            ins.close();
        }
        return toFile;
    }

    //获取流文件
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 校验文件内容时候重复
     * @return
     */
    @ApiOperation(value = "校验文件hash是否重复")
    @GetMapping("/selectHash")
    public R selectHash(String hashCode,Integer type){

        return fileService.selectHash(hashCode,type);
    }

    @PostMapping("/updateFileData")
    public R updateFileData(@RequestBody RequestFile requestFile){

        log.info("加密后获取到的数据:{}",requestFile);
        return fileService.updateFileData(requestFile);
    }


}
