package com.yaoai.api.controller;

import cn.dev33.satoken.stp.StpUtil;
import com.yaoai.api.dto.ai.AiTaskDTO;
import com.yaoai.api.dto.ai.StructuredVideoGenerateRequest;
import com.yaoai.api.dto.ai.UserPrompt;
import com.yaoai.common.exception.BizException;
import com.yaoai.common.exception.ErrorCode;
import com.yaoai.common.response.ApiResponse;
import com.yaoai.pipeline.dto.StructuredVideoSubmission;
import com.yaoai.pipeline.service.VideoTaskPipelineService;
import com.yaoai.security.context.TenantContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

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

@Tag(name = "AI 视频任务")
@RestController
@RequestMapping("/projects/{projectId}/video-tasks")
@RequiredArgsConstructor
public class VideoTaskController {

    private final VideoTaskPipelineService videoTaskPipelineService;

    @Operation(summary = "结构化视频生成（推荐）：左图（角色/场景/道具/风格）+ 右结构化提示词")
    @PostMapping("/generate-structured")
    public ApiResponse<AiTaskDTO> generateStructured(@PathVariable Long projectId,
                                                     @RequestBody @Valid StructuredVideoGenerateRequest req) {
        UserPrompt up = req.getUserPrompt();
        boolean hasFreeform = req.getFreeformPrompt() != null && !req.getFreeformPrompt().isBlank();
        boolean hasStructured = up != null && up.hasAnyContent();
        if (!hasFreeform && !hasStructured) {
            throw new BizException(ErrorCode.INVALID_PARAM, "请填写分镜提示词");
        }

        List<String> characterKeys = req.getCharacterImageKeys();
        boolean hasCharacter = (characterKeys != null && !characterKeys.isEmpty())
                || (req.getCharacterImageKey() != null && !req.getCharacterImageKey().isBlank());
        boolean hasScene = req.getSceneImageKey() != null && !req.getSceneImageKey().isBlank();
        boolean hasProp = req.getPropImageKeys() != null && !req.getPropImageKeys().isEmpty();
        if (!hasCharacter && !hasScene && !hasProp) {
            throw new BizException(ErrorCode.INVALID_PARAM, "请至少选择一张参考图（角色/场景/道具任一）");
        }

        Long tenantId = TenantContext.get();
        StructuredVideoSubmission submission = StructuredVideoSubmission.builder()
                .userId(StpUtil.getLoginIdAsLong())
                .episodeId(req.getEpisodeId())
                .storyboardId(req.getStoryboardId())
                .characterImageKey(req.getCharacterImageKey())
                .characterImageKeys(characterKeys)
                .sceneImageKey(req.getSceneImageKey())
                .propImageKeys(req.getPropImageKeys())
                .styleImageKey(req.getStyleImageKey())
                .characterAction(up != null ? up.getCharacterAction() : null)
                .sceneEvent(up != null ? up.getSceneEvent() : null)
                .cameraMovement(up != null ? up.getCameraMovement() : null)
                .lightingAtmosphere(up != null ? up.getLightingAtmosphere() : null)
                .videoStyle(up != null ? up.getVideoStyle() : null)
                .freeformPrompt(req.getFreeformPrompt())
                .userPromptRaw(buildUserPromptMap(up, req.getFreeformPrompt()))
                .durationSeconds(req.getDuration())
                .ratio(req.getRatio())
                .model(req.getModel())
                .generateAudio(req.getGenerateAudio())
                .build();

        return ApiResponse.success(AiTaskDTO.from(
                videoTaskPipelineService.submitStructuredVideoTask(projectId, tenantId, submission)
        ));
    }

    private static Map<String, String> buildUserPromptMap(UserPrompt up, String freeformPrompt) {
        Map<String, String> m = new LinkedHashMap<>();
        if (freeformPrompt != null && !freeformPrompt.isBlank()) {
            m.put("freeformPrompt", freeformPrompt);
        }
        if (up != null) {
            if (up.getCharacterAction() != null)    m.put("characterAction", up.getCharacterAction());
            if (up.getSceneEvent() != null)         m.put("sceneEvent", up.getSceneEvent());
            if (up.getCameraMovement() != null)     m.put("cameraMovement", up.getCameraMovement());
            if (up.getLightingAtmosphere() != null) m.put("lightingAtmosphere", up.getLightingAtmosphere());
            if (up.getVideoStyle() != null)         m.put("videoStyle", up.getVideoStyle());
        }
        return m;
    }

    /**
     * @deprecated 由 {@code POST /generate-structured} 替代；保留 1 个版本周期供前端回滚。
     * 旧路径：prompt 内嵌 @图N + image_keys 顺序绑定。
     */
    @Deprecated
    @Operation(summary = "[DEPRECATED] 文生图→图生视频（异步，自然语言 @图N 方式）")
    @PostMapping("/generate")
    public ApiResponse<AiTaskDTO> generateFromText(@PathVariable Long projectId,
                                                    @RequestBody Map<String, Object> body) {
        String prompt = (String) body.get("prompt");
        if (prompt == null || prompt.isBlank()) {
            throw new BizException(ErrorCode.INVALID_PARAM, "prompt 不能为空");
        }
        String videoPrompt = body.containsKey("video_prompt") ? (String) body.get("video_prompt") : prompt;
        String episodeIdStr = (String) body.get("episode_id");
        Long episodeId = episodeIdStr != null ? Long.parseLong(episodeIdStr) : null;
        String storyboardIdStr = (String) body.get("storyboard_id");
        Long storyboardId = storyboardIdStr != null ? Long.parseLong(storyboardIdStr) : null;

        // image_keys: 按 @图N 顺序排列的 TOS key 列表，非空时跳过 Seedream
        @SuppressWarnings("unchecked")
        List<String> preImageKeys = body.containsKey("image_keys")
                ? (List<String>) body.get("image_keys")
                : Collections.emptyList();

        // duration: 视频时长（秒），默认 5
        int duration = 5;
        Object durationObj = body.get("duration");
        if (durationObj instanceof Number) {
            duration = ((Number) durationObj).intValue();
        }

        Long tenantId = TenantContext.get();
        return ApiResponse.success(AiTaskDTO.from(
                videoTaskPipelineService.submitTextToVideoAsync(projectId, tenantId, StpUtil.getLoginIdAsLong(), episodeId, storyboardId,
                        prompt, videoPrompt, preImageKeys, duration)
        ));
    }

    @Operation(summary = "提交图生视频任务（已有图片 TOS key）")
    @PostMapping
    public ApiResponse<AiTaskDTO> submit(@PathVariable Long projectId,
                                         @RequestBody Map<String, String> body) {
        String imageKey = body.get("image_key");
        if (imageKey == null || imageKey.isBlank()) {
            throw new BizException(ErrorCode.INVALID_PARAM, "image_key 不能为空");
        }
        String prompt = body.get("prompt");
        String episodeIdStr = body.get("episode_id");
        Long episodeId = episodeIdStr != null ? Long.parseLong(episodeIdStr) : null;
        String storyboardIdStr = body.get("storyboard_id");
        Long storyboardId = storyboardIdStr != null ? Long.parseLong(storyboardIdStr) : null;

        Long tenantId = TenantContext.get();
        return ApiResponse.success(AiTaskDTO.from(
                videoTaskPipelineService.submitVideoTask(projectId, tenantId, episodeId, storyboardId, imageKey, prompt)
        ));
    }

    @Operation(summary = "轮询任务状态")
    @GetMapping("/{taskId}")
    public ApiResponse<AiTaskDTO> poll(@PathVariable Long projectId,
                                       @PathVariable Long taskId) {
        Long tenantId = TenantContext.get();
        return ApiResponse.success(AiTaskDTO.from(
                videoTaskPipelineService.pollTaskStatus(taskId, tenantId)
        ));
    }

    @Operation(summary = "删除视频任务")
    @DeleteMapping("/{taskId}")
    public ApiResponse<Void> delete(@PathVariable Long projectId,
                                    @PathVariable Long taskId) {
        Long tenantId = TenantContext.get();
        videoTaskPipelineService.deleteTask(taskId, tenantId);
        return ApiResponse.success(null);
    }

    @Operation(summary = "获取项目视频任务列表")
    @GetMapping
    public ApiResponse<List<AiTaskDTO>> list(@PathVariable Long projectId) {
        Long tenantId = TenantContext.get();
        return ApiResponse.success(
                videoTaskPipelineService.listByProject(projectId, tenantId)
                        .stream().map(AiTaskDTO::from).collect(Collectors.toList())
        );
    }
}
