package com.backendsys.modules.sdk.douyincloud.tos.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.backendsys.exception.CustException;
import com.backendsys.modules.common.config.security.utils.SecurityUtil;
import com.backendsys.modules.common.utils.CommonUtil;
import com.backendsys.modules.sdk.douyincloud.tos.service.DouyinTosService;
import com.backendsys.modules.sdk.tencentcloud.cos.entity.Progress;
import com.backendsys.modules.sse.entity.SseResponse;
import com.backendsys.modules.sse.entity.SseResponseEnum;
import com.backendsys.modules.sse.utils.SseUtil;
import com.backendsys.modules.upload.entity.SysFileResult;
import com.volcengine.tos.TOSV2;
import com.volcengine.tos.TOSV2ClientBuilder;
import com.volcengine.tos.TosClientException;
import com.volcengine.tos.TosServerException;
import com.volcengine.tos.comm.event.DataTransferListener;
import com.volcengine.tos.comm.event.DataTransferStatus;
import com.volcengine.tos.comm.event.DataTransferType;
import com.volcengine.tos.model.object.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;

// 对象接口 -> 上传对象
// https://www.volcengine.com/docs/6349/79898
@Service
public class DouyinTosServiceImpl implements DouyinTosService {

    @Autowired
    private SseUtil sseUtil;

    @Value("${spring.application.name}")
    private String APPLICATION_NAME;
    @Value("${douyin.tos.region}")
    private String REGION;
    @Value("${douyin.tos.endpoint}")
    private String ENDPOINT;
    @Value("${douyin.tos.domain}")
    private String DOMAIN;
    @Value("${douyin.tos.bucket-name}")
    private String BUCKET_NAME;
    @Value("${douyin.tos.access-key-id}")
    private String ACCESS_KEY_ID;
    @Value("${douyin.tos.secret-access-key}")
    private String SECRET_ACCESS_KEY;


    // [抖音云COS] 获取进度函数
    private DataTransferListener getDataTransferListener(String filename) {

        return new DataTransferListener() {
            // 自定义实现 DataTransferListener 的 dataTransferStatusChange 接口
            @Override
            public void dataTransferStatusChange(DataTransferStatus dataTransferStatus) {

                long once = dataTransferStatus.getRwOnceBytes();
                long current = dataTransferStatus.getConsumedBytes();
                long total = dataTransferStatus.getTotalBytes();
                String state = "";
                BigDecimal percentage = Convert.toBigDecimal(0);

                if (dataTransferStatus.getType() == DataTransferType.DATA_TRANSFER_STARTED) {
                    state = "init";
                    System.out.println("putObject started.");
                } else if (dataTransferStatus.getType() == DataTransferType.DATA_TRANSFER_RW) {
                    state = "inprogress";
                    // 计算百分比
                    percentage = NumberUtil.round(NumberUtil.mul(current, 100) / total, 2);
                    System.out.printf("putObject progress: [%d / %d] = %.02f%% \n", current, total, percentage);
                } else if (dataTransferStatus.getType() == DataTransferType.DATA_TRANSFER_FAILED) {
                    state = "completed";
                    System.out.printf("putObject failed, has sent %d bytes, total %d bytes.\n", dataTransferStatus.getConsumedBytes(), total);
                } else if (dataTransferStatus.getType() == DataTransferType.DATA_TRANSFER_SUCCEED) {
                    state = "completed";
                    System.out.printf("putObject succeed, has sent %d bytes, total %d bytes.\n", dataTransferStatus.getConsumedBytes(), total);
                }

                // [SSE] 进度回传
                Progress progress = new Progress();
                progress.setFilename(filename);
                progress.setCurrent(current);
                progress.setTotal(total);
                progress.setPercent(percentage);
                progress.setState(state);
                String dataStr = (new SseResponse(SseResponseEnum.UPLOAD, progress)).toJsonStr();
                sseUtil.send(SecurityUtil.getUserId(), dataStr);

            }
        };
    }

    /**
     * [抖音云TOS] 上传对象
     */
    @Override
    public SysFileResult putObject(MultipartFile multipartFile, String object_key) {

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

        // 生成 UUID 文件名
        String filename = CommonUtil.generateUUIDFilename(multipartFile);

        // 如果不传 object_key，则默认使用临时文件夹 (/temp/UUID文件名)
        if (StrUtil.isEmpty(object_key)) object_key = "temp/" + filename;
        System.out.println("Upload object: " + object_key);

        // ------------------------------------------------------------------------------

        // 初始化 TosClient
        TOSV2 tos = new TOSV2ClientBuilder().build(REGION, ENDPOINT, ACCESS_KEY_ID, SECRET_ACCESS_KEY);

        // TOSV2 提供的所有接口均会抛出 TosException 异常，需要使用 try-catch 进行捕获并处理。
        InputStream inputStream = null;
        try{

            // 设置上传的桶名和对象名
            inputStream = multipartFile.getInputStream();
            PutObjectInput putObjectInput = new PutObjectInput()
                .setBucket(BUCKET_NAME)
                .setKey(object_key)
                .setContent(inputStream);

            // 以下代码展示如何处理进度条
            // 在 ObjectMetaRequestOptions 中设置文件大小，可在进度条中显示 total 总长度，否则 DataTransferStatus.getTotalBytes 值为 -1。
            ObjectMetaRequestOptions options = new ObjectMetaRequestOptions().setContentLength(multipartFile.getSize());
            putObjectInput.setOptions(options);

            // 自定义实现 DataTransferListener，实现进度条功能
            DataTransferListener listener = getDataTransferListener(filename);
            putObjectInput.setDataTransferListener(listener);

            // 上传对象
            PutObjectOutput output = tos.putObject(putObjectInput);

            // 自定义返回结果实体
            SysFileResult result = new SysFileResult();
            result.setKey(object_key);
            result.setRequest_id(output.getRequestInfo().getRequestId());
            result.setE_tag(output.getEtag());
            result.setDomain(DOMAIN);
            return result;

        } catch (TosClientException e) {
            // 操作失败，捕获客户端异常，一般情况是请求参数错误，此时请求并未发送
            System.out.println("putObject failed (TosClientException)");
            throw new CustException(e.getMessage());
        } catch (TosServerException e) {
            // 操作失败，捕获服务端异常，可以获取到从服务端返回的详细错误信息
            System.out.println("putObject failed (TosServerException)");
            throw new CustException(e.getMessage());
        } catch (Throwable t) {
            // 作为兜底捕获其他异常，一般不会执行到这里
            System.out.println("putObject failed (Throwable)");
            throw new CustException(t.getMessage());
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // 忽略关闭流时的异常
                }
            }
            try {
                tos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * [抖音云COS] 删除对象
     */
    public void deleteObject(String object_key) {
        try {
            // 初始化 TosClient
            TOSV2 tos = new TOSV2ClientBuilder().build(REGION, ENDPOINT, ACCESS_KEY_ID, SECRET_ACCESS_KEY);
            DeleteObjectInput input = new DeleteObjectInput().setBucket(BUCKET_NAME).setKey(object_key);
            tos.deleteObject(input);
            System.out.println("deleteObject succeed.");
            tos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
