package com.yd.oss.api.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result;
import com.yd.oss.api.service.ApiChunkedUploadContextService;
import com.yd.oss.service.config.OssClientFactory;
import com.yd.oss.service.dao.ChunkedUploadContextMapper;
import com.yd.oss.service.model.ChunkedUploadContext;
import com.yd.oss.service.model.OssProvider;
import com.yd.oss.service.service.IOssProviderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;


@Slf4j
@Service
public class ApiChunkedUploadContextServiceImpl implements ApiChunkedUploadContextService {

    @Resource
    private ChunkedUploadContextMapper contextMapper;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private OssClientFactory ossClientFactory;

    @Resource
    private IOssProviderService ossProviderService;

    /**
     * 上传单个分片
     * @param taskId
     * @param chunkIndex
     * @param chunk
     * @param projectBizId
     * @param source
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void uploadChunk(String taskId, Integer chunkIndex, MultipartFile chunk, String projectBizId,String source) {
        try {
            log.debug("上传分片: taskId={}, chunkIndex={}, size={}", taskId, chunkIndex, chunk.getSize());

            // 根据项目ID获取服务商信息
            OssProvider provider = ossProviderService.getProviderByProjectId(projectBizId);
            if (provider == null) {
                log.error("未找到项目对应的OSS服务商，projectBizId={}", projectBizId);
                throw new BusinessException("未找到对应的OSS服务商配置");
            }
            //创建OSS客户端
            OSS ossClient = ossClientFactory.createOssClient(provider);

            // 1. 从数据库查询上下文，若不存在则新建
            ChunkedUploadContext context = contextMapper.selectOne(
                    new LambdaQueryWrapper<ChunkedUploadContext>()
                            .eq(ChunkedUploadContext::getTaskId, taskId)
            );

            if (context == null) {
                //首次上传，创建新上下文（初始化 OSS 分片上传）
                context = new ChunkedUploadContext();
                context.setTaskId(taskId);

                // 生成 OSS 对象 Key 结构：sharding+分片来源+年+月+日+xxx.webm
                String objectKey = String.format("sharding/"+source+"/%tY/%tm/%s_%d.webm",
                        new Date(), new Date(), taskId, System.currentTimeMillis());
                context.setObjectKey(objectKey);

                // 调用 OSS 初始化分片上传
                InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(provider.getBucketName(), objectKey);
                InitiateMultipartUploadResult initResult = ossClient.initiateMultipartUpload(initRequest);
                //OSS 分片上传 ID（由 OSS 初始化时返回）
                context.setUploadId(initResult.getUploadId());

                // 初始化分片列表为空
                context.setPartEtagsJson("[]");
                context.setStatus(1); // 状态：上传中

                // 插入数据库
                contextMapper.insert(context);
                log.info("初始化分片上传并入库: taskId={}, uploadId={}, objectKey={}",
                        taskId, context.getUploadId(), objectKey);
            }

            // 2. 当前分片序号转 OSS partNumber（从 1 开始）
            int partNumber = chunkIndex + 1;

            // 3. 上传分片到 OSS
            UploadPartRequest uploadPartRequest = new UploadPartRequest();
            uploadPartRequest.setBucketName(provider.getBucketName());
            uploadPartRequest.setKey(context.getObjectKey());
            uploadPartRequest.setUploadId(context.getUploadId());
            uploadPartRequest.setPartNumber(partNumber);
            uploadPartRequest.setInputStream(chunk.getInputStream());
            uploadPartRequest.setPartSize(chunk.getSize());
            UploadPartResult uploadResult = ossClient.uploadPart(uploadPartRequest);

            // 4. 构建新的 PartETag
            PartETag partETag = new PartETag(uploadResult.getPartNumber(), uploadResult.getETag());

            // 5. 从数据库读取现有的 ETag 列表（JSON -> List）
            String json = context.getPartEtagsJson();
            List<PartETag> partETags = objectMapper.readValue(json,
                    new TypeReference<List<PartETag>>() {});

            // 6. 添加新 ETag（按 partNumber 升序排序）
            partETags.add(partETag);
            partETags.sort(Comparator.comparingInt(PartETag::getPartNumber));

            // 7. 序列化为 JSON 并更新数据库
            context.setPartEtagsJson(objectMapper.writeValueAsString(partETags));
            context.setUpdateTime(LocalDateTime.now());
            contextMapper.updateById(context);

            log.debug("分片上传成功: taskId={}, partNumber={}, ETag={}", taskId, partNumber, uploadResult.getETag());

        } catch (Exception e) {
            log.error("分片上传失败", e);
            throw new BusinessException("分片上传失败: " + e.getMessage());
        }
    }

    /**
     * 完成分片上传（合并）
     * @param taskId
     * @param projectBizId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Map<String, Object>> finishChunks(String taskId,String projectBizId) {
        log.info("完成分片上传并合并文件: taskId={}", taskId);

        // 1. 从数据库查询上下文
        ChunkedUploadContext context = contextMapper.selectOne(
                new LambdaQueryWrapper<ChunkedUploadContext>()
                        .eq(ChunkedUploadContext::getTaskId, taskId)
        );
        if (context == null) {
            throw new BusinessException("未找到分片上传上下文，请检查 taskId 是否正确");
        }

        // 2. 解析分片列表
        String json = context.getPartEtagsJson();
        List<PartETag> partETags;
        try {
            partETags = objectMapper.readValue(json, new TypeReference<List<PartETag>>() {});
        } catch (Exception e) {
            log.error("解析分片 ETag 列表失败", e);
            throw new BusinessException("数据异常，无法合并");
        }
        if (partETags.isEmpty()) {
            throw new BusinessException("没有分片数据，无法合并");
        }

        // 根据项目ID获取服务商信息
        OssProvider provider = ossProviderService.getProviderByProjectId(projectBizId);
        if (provider == null) {
            log.error("未找到项目对应的OSS服务商，projectBizId={}", projectBizId);
            throw new BusinessException("未找到对应的OSS服务商配置");
        }
        //创建OSS客户端
        OSS ossClient = ossClientFactory.createOssClient(provider);

        // 3. 调用 OSS 完成分片合并
        CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(
                provider.getBucketName(),
                context.getObjectKey(),
                context.getUploadId(),
                partETags
        );
        CompleteMultipartUploadResult completeResult = ossClient.completeMultipartUpload(completeRequest);

        // 4. 构建文件访问 URL
        String fileUrl = String.format("https://%s.%s/%s",
                provider.getBucketName(),
                provider.getEndpoint().replace("https://", ""),
                context.getObjectKey()
        );

        // 5. 获取文件大小
        ObjectMetadata metadata = ossClient.getObjectMetadata(provider.getBucketName(), context.getObjectKey());
        long fileSize = metadata.getContentLength();

        // 6. 更新录制任务表（状态变为已录制）
//        LambdaQueryWrapper<RecordingTask> wrapper = new LambdaQueryWrapper<>();
//        wrapper.eq(RecordingTask::getTaskId, taskId);
//        RecordingTask task = iRecordingTaskService.getOne(wrapper);
//        if (task == null) {
//            throw new BusinessException("录制任务不存在，taskId=" + taskId);
//        }
//        task.setFileUrl(fileUrl);
//        task.setFileSize(fileSize);
//        task.setStatus("3"); // 已录制
//        task.setFileFormat("webm");
//        task.setStopTime(LocalDateTime.now());
//        iRecordingTaskService.updateById(task);

        // 7. 更新上下文状态为“已完成”
        context.setStatus(2); // 已完成
        contextMapper.updateById(context);

        // 8. 返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("taskId", taskId);
        result.put("fileUrl", fileUrl);
        result.put("fileKey", context.getObjectKey());
        result.put("fileSize", fileSize);

        return Result.success(result);
    }
}
