package com.backendsys.modules.upload.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.backendsys.exception.CustException;
import com.backendsys.modules.common.config.security.utils.HttpRequestUtil;
import com.backendsys.modules.sdk.douyincloud.tos.service.DouyinTosService;
import com.backendsys.modules.sdk.tencentcloud.cos.service.TencentCosService;
import com.backendsys.modules.system.entity.SysCommon;
import com.backendsys.modules.system.service.SysCommonService;
import com.backendsys.modules.upload.dao.SysFileDao;
import com.backendsys.modules.upload.entity.SysFile;
import com.backendsys.modules.upload.enums.StyleEnums;
import com.backendsys.modules.upload.service.SysFileMultipartService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qcloud.cos.model.*;
import com.volcengine.tos.model.object.CompleteMultipartUploadV2Output;
import com.volcengine.tos.model.object.CreateMultipartUploadOutput;
import com.volcengine.tos.model.object.UploadPartV2Output;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
public class SysFileMultipartServiceImpl implements SysFileMultipartService {

    @Value("${tencent.cos.accessible-domain}")
    private String TENCENT_ACCESSIBLE_DOMAIN;

    @Value("${douyin.tos.domain}")
    private String DOUYIN_ACCESSIBLE_DOMAIN;

    @Autowired
    private HttpRequestUtil httpRequestUtil;
    @Autowired
    private TencentCosService tencentCosService;
    @Autowired
    private DouyinTosService douyinTosService;
    @Autowired
    private SysCommonService sysCommonService;
    @Autowired
    private SysFileDao sysFileDao;


    // [方法] 设置缩略图 (参数)
    // 不同的云环境 (target)，缩略图配置也不一样
    //   -1:本地:
    //   1:腾讯云: https://cloud.tencent.com/document/product/436/113295
    //   2:阿里云:
    //   3:抖音云: https://www.volcengine.com/docs/6349/153626
    private SysFile setThumbUrl(SysFile sysFile, Integer width, Integer height, String backgroundColor) {
        // 本地上传
        if (sysFile.getTarget() == -1) {
            sysFile.setUrl_thumb(sysFile.getUrl() + "?w=" + width + "&h=" + height);
        }
        // 腾讯云 (color值通过base64加密, #f8f8f8)
        if (sysFile.getTarget() == 1) {
            System.out.println("base64 encode: " + Base64.encode(backgroundColor));
            sysFile.setUrl_thumb(sysFile.getUrl() + "?imageMogr2/thumbnail/" + width + "x" + height + "/pad/1/color/" + Base64.encode(backgroundColor));
        }
        // 抖音云
        if (sysFile.getTarget() == 3) {
            sysFile.setUrl_thumb(sysFile.getUrl() + "?x-tos-process=image/resize,w_" + width + ",h_" + height + ",m_pad,color_" + backgroundColor);
        }
        return sysFile;
    }


    // 1.初始化分块上传 (获得 upload_id, object_key)
    @Override
    public Map<String, Object> multipartUploadInit(MultipartFile multipartFile, Long category_id, Integer upload_chunk_count) {

        if (multipartFile.isEmpty()) throw new CustException("file 上传文件不能为空");
        if (upload_chunk_count == null) throw new CustException("upload_chunk_count 分块数量不能为空");

        // 获得公共配置
        List<SysCommon> sysCommonList = sysCommonService.getCommonByCategory("UPLOAD");
        AtomicReference<Integer> UPLOAD_TARGET = new AtomicReference<>();
        sysCommonList.stream().forEach(sysCommon -> {
            if (sysCommon.getTag().equals("UPLOAD_TARGET")) UPLOAD_TARGET.set(Convert.toInt(sysCommon.getValue()));
        });

        System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);

        String target_label = StyleEnums.targetToLabel(UPLOAD_TARGET.get());
        String upload_id = null;
        String object_key = null;

        // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
        if (UPLOAD_TARGET.get() == 1) {
            InitiateMultipartUploadResult uploadResult = tencentCosService.initiateMultipartUpload(multipartFile);
            upload_id = uploadResult.getUploadId();
            object_key = uploadResult.getKey();
        }
        // 3: 抖音云
        if (UPLOAD_TARGET.get() == 3) {
            CreateMultipartUploadOutput uploadResult = douyinTosService.initiateMultipartUpload(multipartFile);
            upload_id = uploadResult.getUploadID();
            object_key = uploadResult.getKey();
        }

        // 如果没有获得以上参数，则抛出报错
        if (StrUtil.isEmpty(upload_id) || StrUtil.isEmpty(object_key)) {
            throw new CustException("upload_id 或 object_key 不能为空");
        }

        // [新增] 上传文件记录
        SysFile sysFileEntity = new SysFile();
        sysFileEntity.setCategory_id(category_id);
        sysFileEntity.setUser_id(httpRequestUtil.getUserId());
        sysFileEntity.setUpload_id(upload_id);
        sysFileEntity.setUpload_chunk_count(upload_chunk_count);
        sysFileEntity.setUpload_chunk_index(0);
        sysFileEntity.setName(FileNameUtil.getName(object_key));
        sysFileEntity.setObject_key(object_key);
        sysFileEntity.setTarget(UPLOAD_TARGET.get());
        sysFileEntity.setContent_type(multipartFile.getContentType());
        sysFileEntity.setSize(multipartFile.getSize());
        sysFileDao.insert(sysFileEntity);

        Map<String, Object> resp = new LinkedHashMap<>();
        resp.put("target", UPLOAD_TARGET.get());
        resp.put("target_label", target_label);
        resp.put("upload_chunk_count", upload_chunk_count);
        resp.put("upload_id", upload_id);
        resp.put("object_key", object_key);
        return resp;
    }

    // 2.上传分块
    // - 抖音云单个分块大小不能小于4MB
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> multipartUpload(MultipartFile multipartFile, String upload_id, Integer upload_chunk_index) {

        if (multipartFile.isEmpty()) throw new CustException("file 上传文件不能为空");
        if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");
        if (upload_chunk_index == null) throw new CustException("upload_chunk_index 不能为空");

        // [Get] 获得初始化分块信息
        SysFile sysFileEntity = sysFileDao.selectOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUpload_id, upload_id));
        if (sysFileEntity == null) throw new CustException("分块记录不存在");
        String object_key = sysFileEntity.getObject_key();
        Integer upload_chunk_size = sysFileEntity.getUpload_chunk_count();
        if (upload_chunk_index > upload_chunk_size) throw new CustException("分块索引(index)不能大于分块数量(count)");

        if (upload_chunk_index < upload_chunk_size) {
            // 分片编号从 1 开始，最大为 10000。除最后一个分片以外，其他分片大小最小为 4MiB
            if (multipartFile.getSize() < 4 * 1024 * 1024) {
                throw new CustException("分块文件大小不能小于 4MB");
            }
        }

        // 获得公共配置
        List<SysCommon> sysCommonList = sysCommonService.getCommonByCategory("UPLOAD");
        AtomicReference<Integer> UPLOAD_TARGET = new AtomicReference<>();
        sysCommonList.stream().forEach(sysCommon -> {
            if (sysCommon.getTag().equals("UPLOAD_TARGET")) UPLOAD_TARGET.set(Convert.toInt(sysCommon.getValue()));
        });
        System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);


        String part_etag = null;

        // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
        if (UPLOAD_TARGET.get() == 1) {
            UploadPartResult partResult = tencentCosService.uploadPart(multipartFile, upload_id, object_key, upload_chunk_index);
            part_etag = partResult.getPartETag().getETag();
        }
        // 3: 抖音云
        if (UPLOAD_TARGET.get() == 3) {
            UploadPartV2Output partResult = douyinTosService.uploadPart(multipartFile, upload_id, object_key, upload_chunk_index);
            part_etag = partResult.getEtag().replace("\"", "");
        }

        // [Update] 更新分块文件信息
        sysFileEntity.setUpload_chunk_index(upload_chunk_index);
        sysFileDao.updateById(sysFileEntity);

        Map<String, Object> resp = new LinkedHashMap<>();
        resp.put("upload_chunk_index", upload_chunk_index);
        resp.put("upload_id", upload_id);
        resp.put("object_key", object_key);
        resp.put("part_etag", part_etag);
        return resp;
    }

    // 完成分块上传
    @Override
    public Map<String, Object> multipartUploadComplete(String upload_id) {

        if (StrUtil.isEmpty(upload_id)) throw new CustException("upload_id 不能为空");

        // [Get] 查询文件分块记录
        SysFile sysFileEntity = sysFileDao.selectOne(new LambdaQueryWrapper<SysFile>().eq(SysFile::getUpload_id, upload_id));
        if (sysFileEntity == null) throw new CustException("upload_id 不存在");

        // 判断分块索引和分块数量是否一致
        if (sysFileEntity.getUpload_chunk_index() != sysFileEntity.getUpload_chunk_count()) {
            throw new CustException("分块索引和分块数量不一致");
        }


        // 获得公共配置
        List<SysCommon> sysCommonList = sysCommonService.getCommonByCategory("UPLOAD");
        AtomicReference<Integer> UPLOAD_TARGET = new AtomicReference<>();
        AtomicReference<Integer> UPLOAD_THUMB_SIZE = new AtomicReference<>();
        sysCommonList.stream().forEach(sysCommon -> {
            if (sysCommon.getTag().equals("UPLOAD_TARGET")) UPLOAD_TARGET.set(Convert.toInt(sysCommon.getValue()));
            if (sysCommon.getTag().equals("UPLOAD_THUMB_SIZE")) UPLOAD_THUMB_SIZE.set(Convert.toInt(sysCommon.getValue()));
        });
        System.out.println("[系统配置] 上传目标(-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云): " + UPLOAD_TARGET);
        System.out.println("[系统配置] 图片的缩略图宽高尺寸 (px): " + UPLOAD_THUMB_SIZE);


        // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
        if (UPLOAD_TARGET.get() == 1) {

            // 查询分块集合 (etag)
            Map<String, Object> partList = listParts(upload_id, sysFileEntity.getObject_key());
            PartListing partListing = (PartListing) partList.get("listParts");
            List<PartSummary> partSummaryList = partListing.getParts();
            List<PartETag> etags = partSummaryList.stream()
                .map(partSummary -> new PartETag(partSummary.getPartNumber(), partSummary.getETag()))
                .collect(Collectors.toList());

            // 如果分块数量对不上，最好还是重新上传
            if (sysFileEntity.getUpload_chunk_count() != etags.size()) throw new CustException("分块索引异常，请重新上传");

            // [腾讯云] 合并分块
            CompleteMultipartUploadResult completeResult = tencentCosService.completeMultipartUpload(upload_id, sysFileEntity.getObject_key(), etags);
            if (completeResult == null) throw new CustException("分块合并失败");

            // 拼接图片路径
            sysFileEntity.setUrl(TENCENT_ACCESSIBLE_DOMAIN + "/" + completeResult.getKey());
            sysFileEntity.setRequest_id(completeResult.getRequestId());
        }

        // 3: 抖音云
        if (UPLOAD_TARGET.get() == 3) {

            CompleteMultipartUploadV2Output completeResult = douyinTosService.completeMultipartUpload(upload_id, sysFileEntity.getObject_key());
            if (completeResult == null) throw new CustException("分块合并失败");

            // 拼接图片路径
            sysFileEntity.setUrl(DOUYIN_ACCESSIBLE_DOMAIN + "/" + completeResult.getKey());
            sysFileEntity.setRequest_id(completeResult.getRequestInfo().getRequestId());

        }

        // 如果是图片类型，就设置封面
        /*
            图片Icon (png / jpg / jpeg / gif / bmp / webp)
            视频 Icon (mp4 / avi / mov / flv / 3gp / mpeg / wmv)
            Word Icon (doc / docx)
            Excel Icon (xls / xlsx)
            PDF Icon (pdf)
            其他 Icon
         */
        setThumbUrl(sysFileEntity, UPLOAD_THUMB_SIZE.get(), UPLOAD_THUMB_SIZE.get(), StyleEnums.THUMB_BACKGROUND.getValue());

        // [db] 更新分块记录
        sysFileDao.updateCompleteFile(sysFileEntity);

        Map<String, Object> resp = new LinkedHashMap<>();
        resp.put("upload_id", upload_id);
        resp.put("request_id", sysFileEntity.getRequest_id());
        resp.put("object_key", sysFileEntity.getObject_key());
        resp.put("url", sysFileEntity.getUrl());
        return resp;

    }

    // 查询分块上传情况
    @Override
    public Map<String, Object> listParts(String upload_id, String object_key) {

        Integer UPLOAD_TARGET = Convert.toInt(sysCommonService.getCommonByTag("UPLOAD_TARGET"));
        // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
        if (UPLOAD_TARGET == 1) {
            return Map.of("listParts", tencentCosService.listParts(upload_id, object_key));
        }
        if (UPLOAD_TARGET == 3) {
            return Map.of("listParts", douyinTosService.listParts(upload_id, object_key));
        }

        return null;
    }

}
