package shop.alien.store.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiOperationSupport;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import shop.alien.entity.result.R;
import shop.alien.store.util.oss.OSSDirectUploadUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * OSS直传Controller
 * 支持：签名生成、分片上传、断点续传、上传进度查询
 *
 * @author system
 * @date 2025-01-XX
 */
@Slf4j
@Api(tags = {"OSS直传接口"})
@CrossOrigin
@RestController
@RequestMapping("/oss/direct")
@RequiredArgsConstructor
public class OSSDirectUploadController {

    private final OSSDirectUploadUtil ossDirectUploadUtil;

    @ApiOperation("生成OSS直传签名")
    @ApiOperationSupport(order = 1)
    @PostMapping("/signature")
    public R<OSSDirectUploadUtil.OSSSignatureResult> generateSignature(
            @RequestParam(required = false, defaultValue = "video/") String dir,
            @RequestParam(required = false) String fileName,
            @RequestParam(required = false) Long maxSize,
            @RequestParam(required = false) Long expireTime) {
        try {
            log.info("生成OSS直传签名: dir={}, fileName={}, maxSize={}, expireTime={}", 
                    dir, fileName, maxSize, expireTime);
            OSSDirectUploadUtil.OSSSignatureResult result = 
                    ossDirectUploadUtil.generatePostSignature(dir, fileName, maxSize, expireTime);
            return R.data(result);
        } catch (Exception e) {
            log.error("生成OSS直传签名失败: {}", e.getMessage(), e);
            return R.fail("生成签名失败: " + e.getMessage());
        }
    }

    @ApiOperation("初始化分片上传")
    @ApiOperationSupport(order = 2)
    @PostMapping("/multipart/init")
    public R<MultipartInitResult> initMultipartUpload(
            @RequestParam String ossKey) {
        try {
            log.info("初始化分片上传: ossKey={}", ossKey);
            String uploadId = ossDirectUploadUtil.initMultipartUpload(ossKey);
            MultipartInitResult result = new MultipartInitResult();
            result.setUploadId(uploadId);
            result.setOssKey(ossKey);
            return R.data(result);
        } catch (Exception e) {
            log.error("初始化分片上传失败: {}", e.getMessage(), e);
            return R.fail("初始化分片上传失败: " + e.getMessage());
        }
    }

    @ApiOperation("上传分片")
    @ApiOperationSupport(order = 3)
    @PostMapping("/multipart/upload")
    public R<PartUploadResult> uploadPart(
            @RequestParam String ossKey,
            @RequestParam String uploadId,
            @RequestParam Integer partNumber,
            @RequestBody byte[] partData) {
        try {
            log.info("上传分片: ossKey={}, uploadId={}, partNumber={}, size={}", 
                    ossKey, uploadId, partNumber, partData.length);
            String eTag = ossDirectUploadUtil.uploadPart(ossKey, uploadId, partNumber, partData);
            PartUploadResult result = new PartUploadResult();
            result.setPartNumber(partNumber);
            result.setETag(eTag);
            return R.data(result);
        } catch (Exception e) {
            log.error("上传分片失败: {}", e.getMessage(), e);
            return R.fail("上传分片失败: " + e.getMessage());
        }
    }

    @ApiOperation("完成分片上传（合并）")
    @ApiOperationSupport(order = 4)
    @PostMapping("/multipart/complete")
    public R<CompleteUploadResult> completeMultipartUpload(
            @RequestParam String ossKey,
            @RequestParam String uploadId,
            @RequestBody List<PartETagRequest> partETags) {
        try {
            log.info("完成分片上传: ossKey={}, uploadId={}, partCount={}", 
                    ossKey, uploadId, partETags.size());
            
            // 转换为OSS的PartETag列表
            List<com.aliyun.oss.model.PartETag> ossPartETags = partETags.stream()
                    .map(p -> new com.aliyun.oss.model.PartETag(p.getPartNumber(), p.getETag()))
                    .collect(Collectors.toList());
            
            String fileUrl = ossDirectUploadUtil.completeMultipartUpload(ossKey, uploadId, ossPartETags);
            
            CompleteUploadResult result = new CompleteUploadResult();
            result.setFileUrl(fileUrl);
            result.setOssKey(ossKey);
            return R.data(result);
        } catch (Exception e) {
            log.error("完成分片上传失败: {}", e.getMessage(), e);
            return R.fail("完成分片上传失败: " + e.getMessage());
        }
    }

    @ApiOperation("取消分片上传")
    @ApiOperationSupport(order = 5)
    @PostMapping("/multipart/abort")
    public R<String> abortMultipartUpload(
            @RequestParam String ossKey,
            @RequestParam String uploadId) {
        try {
            log.info("取消分片上传: ossKey={}, uploadId={}", ossKey, uploadId);
            ossDirectUploadUtil.abortMultipartUpload(ossKey, uploadId);
            return R.data("取消成功");
        } catch (Exception e) {
            log.error("取消分片上传失败: {}", e.getMessage(), e);
            return R.fail("取消失败: " + e.getMessage());
        }
    }

    @ApiOperation("查询已上传的分片列表（用于断点续传）")
    @ApiOperationSupport(order = 6)
    @GetMapping("/multipart/list")
    public R<List<PartInfo>> listParts(
            @RequestParam String ossKey,
            @RequestParam String uploadId) {
        try {
            log.info("查询已上传分片: ossKey={}, uploadId={}", ossKey, uploadId);
            List<com.aliyun.oss.model.PartSummary> parts = 
                    ossDirectUploadUtil.listParts(ossKey, uploadId);
            
            List<PartInfo> partInfos = parts.stream()
                    .map(p -> {
                        PartInfo info = new PartInfo();
                        info.setPartNumber(p.getPartNumber());
                        info.setETag(p.getETag());
                        info.setSize(p.getSize());
                        return info;
                    })
                    .collect(Collectors.toList());
            
            return R.data(partInfos);
        } catch (Exception e) {
            log.error("查询已上传分片失败: {}", e.getMessage(), e);
            return R.fail("查询失败: " + e.getMessage());
        }
    }

    @ApiOperation("OSS上传回调验证")
    @ApiOperationSupport(order = 7)
    @PostMapping("/callback")
    public R<String> callback(
            @RequestHeader(value = "Authorization", required = false) String authorization,
            @RequestParam(required = false) String pubKeyUrl,
            @RequestBody(required = false) String callbackBody) {
        try {
            log.info("OSS回调验证: authorization={}", authorization);
            
            if (authorization == null || pubKeyUrl == null || callbackBody == null) {
                return R.fail("回调参数不完整");
            }
            
            boolean verified = ossDirectUploadUtil.verifyCallback(authorization, pubKeyUrl, callbackBody);
            if (verified) {
                // 解析回调Body，获取上传结果
                // callbackBody格式通常是JSON，包含bucket、object、etag等信息
                log.info("OSS回调验证通过: callbackBody={}", callbackBody);
                return R.data("验证成功");
            } else {
                log.warn("OSS回调验证失败");
                return R.fail("验证失败");
            }
        } catch (Exception e) {
            log.error("OSS回调验证异常: {}", e.getMessage(), e);
            return R.fail("验证异常: " + e.getMessage());
        }
    }

    // ========== 内部类 ==========

    @Data
    public static class MultipartInitResult {
        private String uploadId;
        private String ossKey;
    }

    @Data
    public static class PartUploadResult {
        private Integer partNumber;
        private String eTag;
    }

    @Data
    public static class CompleteUploadResult {
        private String fileUrl;
        private String ossKey;
    }

    @Data
    public static class PartETagRequest {
        private Integer partNumber;
        private String eTag;
    }

    @Data
    public static class PartInfo {
        private Integer partNumber;
        private String eTag;
        private Long size;
    }
}
