package com.yaoai.api.controller;

import com.yaoai.api.dto.ai.StoryboardDTO;
import com.yaoai.api.dto.ai.ShotAssetDTO;
import com.yaoai.common.exception.BizException;
import com.yaoai.common.exception.ErrorCode;
import com.yaoai.common.response.ApiResponse;
import com.yaoai.domain.entity.Storyboard;
import com.yaoai.domain.mapper.StoryboardMapper;
import com.yaoai.pipeline.service.ShotAssetService;
import com.yaoai.pipeline.service.StoryboardPipelineService;
import com.yaoai.security.context.TenantContext;
import com.yaoai.storage.service.TosService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

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

@Tag(name = "分镜工作台")
@RestController
@RequestMapping("/projects/{projectId}")
@RequiredArgsConstructor
public class StoryboardController {

    private final StoryboardPipelineService storyboardService;
    private final ShotAssetService shotAssetService;
    private final StoryboardMapper storyboardMapper;
    private final TosService tosService;

    @Operation(summary = "AI 批量生成分集分镜")
    @PostMapping("/episodes/{episodeId}/storyboards/generate")
    public ApiResponse<List<StoryboardDTO>> generate(@PathVariable Long projectId,
                                                      @PathVariable Long episodeId) {
        Long tenantId = TenantContext.get();
        storyboardService.generateStoryboards(episodeId, projectId, tenantId);
        return ApiResponse.success(
                storyboardService.populateMissingVideoPromptsByEpisode(episodeId, tenantId)
                        .stream().map(StoryboardDTO::from).collect(Collectors.toList())
        );
    }

    @Operation(summary = "获取分集分镜列表")
    @GetMapping("/episodes/{episodeId}/storyboards")
    public ApiResponse<List<StoryboardDTO>> listByEpisode(@PathVariable Long projectId,
                                                           @PathVariable Long episodeId) {
        Long tenantId = TenantContext.get();
        return ApiResponse.success(
                storyboardService.listByEpisode(episodeId, tenantId)
                        .stream().map(StoryboardDTO::from).collect(Collectors.toList())
        );
    }

    @Operation(summary = "获取项目全部分镜")
    @GetMapping("/storyboards")
    public ApiResponse<List<StoryboardDTO>> listByProject(@PathVariable Long projectId) {
        Long tenantId = TenantContext.get();
        return ApiResponse.success(
                storyboardService.listByProject(projectId, tenantId)
                        .stream().map(StoryboardDTO::from).collect(Collectors.toList())
        );
    }

    @Operation(summary = "新建单个分镜")
    @PostMapping("/episodes/{episodeId}/storyboards")
    public ApiResponse<StoryboardDTO> create(@PathVariable Long projectId,
                                              @PathVariable Long episodeId,
                                              @RequestBody Storyboard req) {
        Long tenantId = TenantContext.get();
        req.setProjectId(projectId);
        req.setEpisodeId(episodeId);
        req.setTenantId(tenantId);
        return ApiResponse.success(StoryboardDTO.from(storyboardService.create(req)));
    }

    @Operation(summary = "更新分镜")
    @PutMapping("/storyboards/{storyboardId}")
    public ApiResponse<StoryboardDTO> update(@PathVariable Long projectId,
                                              @PathVariable Long storyboardId,
                                              @RequestBody Storyboard req) {
        Long tenantId = TenantContext.get();
        return ApiResponse.success(StoryboardDTO.from(storyboardService.update(storyboardId, tenantId, req)));
    }

    @Operation(summary = "上传分镜首尾帧参考图")
    @PostMapping(value = "/storyboards/{storyboardId}/frame-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ApiResponse<StoryboardDTO> uploadFrameImage(@PathVariable Long projectId,
                                                       @PathVariable Long storyboardId,
                                                       @RequestParam("frameType") String frameType,
                                                       @RequestPart("file") MultipartFile file) throws IOException {
        Long tenantId = TenantContext.get();
        Storyboard storyboard = storyboardMapper.selectById(storyboardId);
        if (storyboard == null
                || !tenantId.equals(storyboard.getTenantId())
                || !projectId.equals(storyboard.getProjectId())) {
            throw new BizException(ErrorCode.NOT_FOUND, "分镜不存在");
        }
        if (!"first".equals(frameType) && !"last".equals(frameType)) {
            throw new BizException(ErrorCode.INVALID_PARAM, "frameType 仅支持 first / last");
        }
        if (file == null || file.isEmpty()) {
            throw new BizException(ErrorCode.INVALID_PARAM, "图片文件不能为空");
        }
        String contentType = file.getContentType() != null ? file.getContentType() : "application/octet-stream";
        if (!contentType.startsWith("image/")) {
            throw new BizException(ErrorCode.INVALID_PARAM, "请上传图片文件");
        }

        String key = TosService.buildKey(tenantId, projectId, "storyboard-frames/" + frameType, file.getOriginalFilename());
        tosService.upload(key, file.getInputStream(), file.getSize(), contentType);
        String url = tosService.publicUrl(key);

        Storyboard patch = new Storyboard();
        patch.setId(storyboardId);
        if ("first".equals(frameType)) {
            patch.setFirstFrameImageKey(key);
            patch.setFirstFrameImageUrl(url);
        } else {
            patch.setLastFrameImageKey(key);
            patch.setLastFrameImageUrl(url);
        }
        storyboardMapper.updateById(patch);
        return ApiResponse.success(StoryboardDTO.from(storyboardMapper.selectById(storyboardId)));
    }

    @Operation(summary = "删除分镜")
    @DeleteMapping("/storyboards/{storyboardId}")
    public ApiResponse<Void> delete(@PathVariable Long projectId,
                                    @PathVariable Long storyboardId) {
        Long tenantId = TenantContext.get();
        storyboardService.delete(storyboardId, tenantId);
        return ApiResponse.success();
    }

    @Operation(summary = "批量排序（传入有序 ID 列表）")
    @PutMapping("/storyboards/reorder")
    public ApiResponse<Void> reorder(@PathVariable Long projectId,
                                     @RequestBody Map<String, List<Long>> body) {
        Long tenantId = TenantContext.get();
        storyboardService.reorder(body.get("ids"), tenantId);
        return ApiResponse.success();
    }

    @Operation(summary = "AI 生成分镜视频描述提示词")
    @PostMapping("/storyboards/{storyboardId}/generate-prompt")
    public ApiResponse<Map<String, String>> generatePrompt(@PathVariable Long projectId,
                                                           @PathVariable Long storyboardId) {
        Long tenantId = TenantContext.get();
        Map<String, String> body = Map.of("prompt", storyboardService.generatePrompt(storyboardId, tenantId));
        return ApiResponse.success(body);
    }

    @Operation(summary = "获取分镜素材版本列表")
    @GetMapping("/storyboards/{storyboardId}/assets")
    public ApiResponse<List<ShotAssetDTO>> listAssets(@PathVariable Long projectId,
                                                       @PathVariable Long storyboardId) {
        Long tenantId = TenantContext.get();
        return ApiResponse.success(
                shotAssetService.listByStoryboard(storyboardId, tenantId)
                        .stream().map(ShotAssetDTO::from).collect(Collectors.toList())
        );
    }

    @Operation(summary = "激活某个分镜素材版本")
    @PostMapping("/storyboards/{storyboardId}/assets/{assetId}/activate")
    public ApiResponse<ShotAssetDTO> activateAsset(@PathVariable Long projectId,
                                                   @PathVariable Long storyboardId,
                                                   @PathVariable Long assetId) {
        Long tenantId = TenantContext.get();
        return ApiResponse.success(ShotAssetDTO.from(
                shotAssetService.activateAsset(assetId, storyboardId, tenantId)
        ));
    }
}
