package com.yaoai.agent.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.yaoai.agent.service.AgentRunService;
import com.yaoai.agent.sse.AgentSseManager;
import com.yaoai.ai.core.model.ChatMessage;
import com.yaoai.ai.core.model.ChatRequest;
import com.yaoai.ai.core.service.LlmService;
import com.yaoai.common.context.UserContext;
import com.yaoai.common.exception.BizException;
import com.yaoai.common.exception.ErrorCode;
import com.yaoai.domain.entity.Episode;
import com.yaoai.domain.entity.GraphRun;
import com.yaoai.domain.entity.GraphStep;
import com.yaoai.domain.entity.Outline;
import com.yaoai.domain.entity.Scene;
import com.yaoai.domain.entity.Storyboard;
import com.yaoai.domain.mapper.EpisodeMapper;
import com.yaoai.domain.mapper.GraphRunMapper;
import com.yaoai.domain.mapper.GraphStepMapper;
import com.yaoai.pipeline.service.AssetGenPipelineService;
import com.yaoai.pipeline.service.OutlinePipelineService;
import com.yaoai.pipeline.service.StoryboardPipelineService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Supplier;

@Slf4j
@Service
@RequiredArgsConstructor
public class AgentRunServiceImpl implements AgentRunService {

    private final GraphRunMapper graphRunMapper;
    private final GraphStepMapper graphStepMapper;
    private final EpisodeMapper episodeMapper;
    private final AgentSseManager sseManager;
    private final LlmService llmService;
    private final OutlinePipelineService outlinePipeline;
    private final AssetGenPipelineService assetGenPipeline;
    private final StoryboardPipelineService storyboardPipeline;

    @Resource(name = "agentExecutor")
    private Executor agentExecutor;

    // ─── Step 定义 ─────────────────────────────────────────────────────────────

    record StepDef(String key, String name, String agentType) {}

    private static final List<StepDef> STEP_DEFS = List.of(
            new StepDef("decision",      "决策层分析目标",        "DECISION"),
            new StepDef("outline_gen",   "大纲生成 SubAgent",     "EXECUTOR"),
            new StepDef("episode_gen",   "分集脚本 SubAgent",     "EXECUTOR"),
            new StepDef("supervision_1", "监督层审核（剧本）",    "SUPERVISION"),
            new StepDef("asset_gen",     "角色/场景提取 SubAgent","EXECUTOR"),
            new StepDef("storyboard_gen","分镜生成 SubAgent",     "EXECUTOR"),
            new StepDef("supervision_2", "监督层终审",            "SUPERVISION")
    );

    // ─── Public API ────────────────────────────────────────────────────────────

    @Override
    public GraphRun startRun(Long projectId, Long tenantId, String goal) {
        Long userId = StpUtil.getLoginIdAsLong();
        GraphRun run = new GraphRun();
        run.setProjectId(projectId);
        run.setTenantId(tenantId);
        run.setGoal(goal);
        run.setStatus("RUNNING");
        graphRunMapper.insert(run);

        for (int i = 0; i < STEP_DEFS.size(); i++) {
            StepDef def = STEP_DEFS.get(i);
            GraphStep step = new GraphStep();
            step.setRunId(run.getId());
            step.setStepKey(def.key());
            step.setStepName(def.name());
            step.setAgentType(def.agentType());
            step.setStatus("PENDING");
            step.setSeq(i);
            graphStepMapper.insert(step);
        }

        Long runId = run.getId();
        agentExecutor.execute(() -> executeRun(runId, projectId, tenantId, userId, goal));
        return run;
    }

    @Override
    public GraphRun getRun(Long runId, Long tenantId) {
        GraphRun run = graphRunMapper.selectById(runId);
        if (run == null || !run.getTenantId().equals(tenantId)) {
            throw new BizException(ErrorCode.NOT_FOUND, "Run 不存在");
        }
        return run;
    }

    @Override
    public GraphRun getLatestRun(Long projectId, Long tenantId) {
        return graphRunMapper.findLatestByProject(projectId, tenantId).orElse(null);
    }

    @Override
    public List<GraphStep> getSteps(Long runId, Long tenantId) {
        return graphStepMapper.findByRunId(runId);
    }

    @Override
    public void pauseRun(Long runId, Long tenantId) {
        updateRunStatus(runId, "PAUSED");
        sseManager.emit(runId, "run.paused", Map.of());
    }

    @Override
    public void resumeRun(Long runId, Long tenantId) {
        updateRunStatus(runId, "RUNNING");
        sseManager.emit(runId, "run.resumed", Map.of());
    }

    // ─── Async Executor ────────────────────────────────────────────────────────

    private void executeRun(Long runId, Long projectId, Long tenantId, Long userId, String goal) {
        log.info("Agent run started: runId={}, projectId={}", runId, projectId);
        UserContext.set(userId);
        try {
            // Step 1: Decision
            executeStep(runId, "decision", () -> {
                sseManager.emitLog(runId, "agent", "[决策层] 分析目标，规划执行步骤...");
                String plan = llmService.chat(ChatRequest.builder()
                        .messages(List.of(
                                ChatMessage.system(DECISION_SYSTEM),
                                ChatMessage.user("用户目标：" + goal)
                        )).build());
                sseManager.emitLog(runId, "agent", "[决策层] 执行计划已制定");
                return "目标分析完成。执行计划：大纲生成 → 分集脚本 → 质量审核 → 资产提取 → 分镜生成 → 终审";
            });

            checkPaused(runId);

            // Step 2: Outline
            Outline outline = (Outline) executeStep(runId, "outline_gen", () -> {
                sseManager.emitLog(runId, "agent", "[执行层] 生成大纲...");
                Outline o = outlinePipeline.generateOutline(projectId, tenantId, goal);
                sseManager.emitLog(runId, "info", "大纲已保存：" + o.getTitle());
                return o;
            });

            checkPaused(runId);

            // Step 3: Episodes
            List<Episode> episodes = (List<Episode>) executeStep(runId, "episode_gen", () -> {
                sseManager.emitLog(runId, "agent", "[执行层] 生成分集脚本...");
                List<Episode> eps = outlinePipeline.generateEpisodes(
                        outline.getId(), projectId, tenantId);
                sseManager.emitLog(runId, "info", "已生成 " + eps.size() + " 集");
                return eps;
            });

            checkPaused(runId);

            // Step 4: Supervision 1
            executeSupervisionStep(runId, "supervision_1", projectId, tenantId,
                    "剧本质量审核：大纲「" + outline.getTitle() + "」，共 " + episodes.size() + " 集");

            checkPaused(runId);

            // Step 5: Assets
            executeStep(runId, "asset_gen", () -> {
                sseManager.emitLog(runId, "agent", "[执行层] 提取角色和场景...");
                List<com.yaoai.domain.entity.Character> chars = assetGenPipeline.extractCharacters(projectId, tenantId);
                List<Scene> scenes = assetGenPipeline.extractScenes(projectId, tenantId);
                sseManager.emitLog(runId, "info",
                        "提取完成：" + chars.size() + " 个角色，" + scenes.size() + " 个场景");
                return "角色 " + chars.size() + " 个，场景 " + scenes.size() + " 个";
            });

            checkPaused(runId);

            // Step 6: Storyboards
            executeStep(runId, "storyboard_gen", () -> {
                sseManager.emitLog(runId, "agent", "[执行层] 生成分镜...");
                int total = 0;
                int promptTotal = 0;
                for (Episode ep : episodes) {
                    List<Storyboard> boards = storyboardPipeline.generateStoryboards(
                            ep.getId(), projectId, tenantId);
                    total += boards.size();
                    List<Storyboard> enriched = storyboardPipeline.populateMissingVideoPromptsByEpisode(
                            ep.getId(), tenantId);
                    long promptCount = enriched.stream()
                            .filter(sb -> sb.getVideoPrompt() != null && !sb.getVideoPrompt().isBlank())
                            .count();
                    promptTotal += (int) promptCount;
                    sseManager.emitLog(runId, "info",
                            "第 " + ep.getEpisodeNumber() + " 集视频提示词：" + promptCount + " 条已生成");
                    sseManager.emitLog(runId, "info",
                            "第 " + ep.getEpisodeNumber() + " 集分镜：" + boards.size() + " 个镜头");
                }
                return "共生成 " + total + " 个分镜，并补全 " + promptTotal + " 条视频提示词";
            });

            checkPaused(runId);

            // Step 7: Final Supervision
            executeSupervisionStep(runId, "supervision_2", projectId, tenantId,
                    "终审：完整制作流程质量评估");

            // Done
            updateRunStatus(runId, "DONE");
            sseManager.emit(runId, "run.done", Map.of("runId", runId));
            log.info("Agent run completed: runId={}", runId);

        } catch (PausedException e) {
            log.info("Agent run paused by user: runId={}", runId);
        } catch (Exception e) {
            log.error("Agent run failed: runId={}", runId, e);
            updateRunStatus(runId, "FAILED");
            sseManager.emit(runId, "run.failed", Map.of("error", e.getMessage()));
        } finally {
            UserContext.clear();
        }
    }

    private Object executeStep(Long runId, String stepKey, Supplier<Object> action) {
        GraphStep step = findStep(runId, stepKey);
        long startMs = System.currentTimeMillis();

        step.setStatus("RUNNING");
        step.setStartedAt(LocalDateTime.now());
        graphStepMapper.updateById(step);
        sseManager.emit(runId, "step.start", Map.of("stepKey", stepKey));

        try {
            Object result = action.get();
            long durationMs = System.currentTimeMillis() - startMs;

            step.setStatus("DONE");
            step.setDoneAt(LocalDateTime.now());
            step.setDurationMs(durationMs);
            step.setOutput(toOutputString(result));
            graphStepMapper.updateById(step);

            sseManager.emit(runId, "step.done", Map.of(
                    "stepKey", stepKey,
                    "output", step.getOutput(),
                    "durationMs", durationMs
            ));
            return result;
        } catch (Exception e) {
            step.setStatus("FAILED");
            step.setDoneAt(LocalDateTime.now());
            step.setDurationMs(System.currentTimeMillis() - startMs);
            step.setOutput("失败：" + e.getMessage());
            graphStepMapper.updateById(step);

            sseManager.emit(runId, "step.failed", Map.of(
                    "stepKey", stepKey,
                    "error", e.getMessage()
            ));
            throw e;
        }
    }

    private void executeSupervisionStep(Long runId, String stepKey, Long projectId, Long tenantId, String context) {
        GraphStep step = findStep(runId, stepKey);
        long startMs = System.currentTimeMillis();

        step.setStatus("RUNNING");
        step.setStartedAt(LocalDateTime.now());
        graphStepMapper.updateById(step);
        sseManager.emit(runId, "step.start", Map.of("stepKey", stepKey));
        sseManager.emitLog(runId, "agent", "[监督层] 正在审核...");

        try {
            String reviewResult = llmService.chat(ChatRequest.builder()
                    .messages(List.of(
                            ChatMessage.system(SUPERVISION_SYSTEM),
                            ChatMessage.user(context)
                    )).build());

            String score = extractScore(reviewResult);
            long durationMs = System.currentTimeMillis() - startMs;

            step.setStatus("DONE");
            step.setScore(score);
            step.setScoreDetail(reviewResult);
            step.setDoneAt(LocalDateTime.now());
            step.setDurationMs(durationMs);
            graphStepMapper.updateById(step);

            sseManager.emit(runId, "step.done", Map.of(
                    "stepKey", stepKey,
                    "score", score,
                    "scoreDetail", reviewResult,
                    "durationMs", durationMs
            ));
            sseManager.emitLog(runId, "agent", "[监督层] 评分：" + score);
        } catch (Exception e) {
            step.setStatus("FAILED");
            step.setDoneAt(LocalDateTime.now());
            graphStepMapper.updateById(step);
            sseManager.emit(runId, "step.failed", Map.of("stepKey", stepKey, "error", e.getMessage()));
            throw e;
        }
    }

    // ─── Helpers ───────────────────────────────────────────────────────────────

    private GraphStep findStep(Long runId, String stepKey) {
        return graphStepMapper.findByRunId(runId).stream()
                .filter(s -> s.getStepKey().equals(stepKey))
                .findFirst()
                .orElseThrow(() -> new BizException(ErrorCode.NOT_FOUND, "Step not found: " + stepKey));
    }

    private void updateRunStatus(Long runId, String status) {
        GraphRun run = new GraphRun();
        run.setId(runId);
        run.setStatus(status);
        graphRunMapper.updateById(run);
    }

    private void checkPaused(Long runId) {
        GraphRun run = graphRunMapper.selectById(runId);
        if (run != null && "PAUSED".equals(run.getStatus())) {
            throw new PausedException();
        }
    }

    private String extractScore(String reviewText) {
        if (reviewText == null) return "B";
        String upper = reviewText.toUpperCase();
        if (upper.contains("评分：A") || upper.contains("SCORE: A") || upper.startsWith("A")) return "A";
        if (upper.contains("评分：B") || upper.contains("SCORE: B") || upper.startsWith("B")) return "B";
        if (upper.contains("评分：C") || upper.contains("SCORE: C") || upper.startsWith("C")) return "C";
        if (upper.contains("评分：D") || upper.contains("SCORE: D") || upper.startsWith("D")) return "D";
        return "B";
    }

    private String toOutputString(Object result) {
        if (result == null) return "";
        if (result instanceof String s) return s;
        if (result instanceof Outline o) return "大纲《" + o.getTitle() + "》共 " + o.getEpisodeCount() + " 集";
        if (result instanceof List<?> list) return "生成 " + list.size() + " 条记录";
        return result.toString();
    }

    private static class PausedException extends RuntimeException {}

    // ─── Prompts ───────────────────────────────────────────────────────────────

    private static final String DECISION_SYSTEM = """
            你是一位AI动漫短剧制作导演。分析用户的制作目标，输出简洁的执行计划（100字以内）。
            执行顺序固定为：大纲→分集→角色场景→分镜。只需确认理解目标即可，不要执行。
            """;

    private static final String SUPERVISION_SYSTEM = """
            你是一位专业的影视内容质量审核专家。对提交的内容进行质量评审，
            按照以下标准给出评分并说明理由（150字以内）：
            - 评分 A：内容优质，可直接进入下一阶段
            - 评分 B：内容良好，有少量改进空间
            - 评分 C：内容一般，建议修改后继续
            - 评分 D：内容较差，建议重做

            回复格式：先写"评分：X"，再写简短评价。
            """;
}
