package com.yaoai.api.util;

import com.yaoai.api.dto.script.ParsedScript;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 解析短剧剧本 docx 文档，提取以下结构化区块：
 * <ul>
 *   <li>元数据：剧名、类型</li>
 *   <li>人物简介 → characters</li>
 *   <li>场景 → scenes</li>
 *   <li>短剧剧情梗概 → synopsis</li>
 *   <li>短剧剧情分集大纲 → episodeOutlines（每集概要）</li>
 *   <li>分集脚本 → episodeScripts（含镜头级别描述）</li>
 * </ul>
 *
 * <p>文档约定：加粗段落为区块分隔符，如"人物简介："、"场景："、"第N集"等。</p>
 */
public class DocScriptParser {

    // ── 内部结构 ──────────────────────────────────────────────────────────────

    private static class Para {
        String text;
        final boolean bold;
        Para(String text, boolean bold) { this.text = text; this.bold = bold; }
    }

    // ── 公开入口 ──────────────────────────────────────────────────────────────

    public static ParsedScript parse(InputStream stream) throws IOException {
        try (XWPFDocument doc = new XWPFDocument(stream)) {
            return doParse(doc);
        }
    }

    // ── 主解析逻辑 ────────────────────────────────────────────────────────────

    private static ParsedScript doParse(XWPFDocument doc) {
        List<Para> paras = extractAndMerge(doc);

        ParsedScript result = new ParsedScript();
        result.setCharacters(new ArrayList<>());
        result.setScenes(new ArrayList<>());
        result.setEpisodeOutlines(new ArrayList<>());
        result.setEpisodeScripts(new ArrayList<>());

        String section = "INIT";
        ParsedScript.EpisodeOutline currentOutline = null;
        ParsedScript.EpisodeScript currentEpScript = null;
        ParsedScript.SceneSection currentSceneSection = null;

        for (Para p : paras) {
            String t = p.text.trim();
            if (t.isEmpty()) continue;

            // ── 加粗段落：检测全局区块分隔符 ──
            if (p.bold) {
                if (t.startsWith("剧名") && containsColon(t)) {
                    result.setTitle(cleanTitle(afterColon(t)));
                    continue;
                }
                if (t.startsWith("故事类型") && containsColon(t)) {
                    result.setGenre(afterColon(t));
                    continue;
                }
                if (t.startsWith("人物简介"))            { section = "CHARS";       continue; }
                if (t.startsWith("客串角色"))            { section = "GUEST_CHARS"; continue; }
                if (t.startsWith("场景") && t.length() < 6) { section = "SCENES";      continue; }
                if (t.startsWith("短剧剧情梗概"))        { section = "SYNOPSIS";    continue; }
                if (t.startsWith("短剧剧情分集大纲"))    { section = "EP_OUTLINES"; continue; }
                if (t.startsWith("分集脚本"))            { section = "EP_SCRIPTS";  continue; }

                // 分集大纲中的"第N集："
                if ("EP_OUTLINES".equals(section) && isEpHeader(t)) {
                    if (currentOutline != null) result.getEpisodeOutlines().add(currentOutline);
                    currentOutline = new ParsedScript.EpisodeOutline();
                    currentOutline.setEpisodeNumber(epNum(t));
                    currentOutline.setContent(afterFirstColon(t));
                    continue;
                }

                // 分集脚本中的"第N集"（集的分隔符，其他加粗内容交由下方统一处理）
                if ("EP_SCRIPTS".equals(section) && isEpHeader(t)) {
                    finalizeScene(currentEpScript, currentSceneSection);
                    if (currentEpScript != null) result.getEpisodeScripts().add(currentEpScript);
                    currentEpScript = new ParsedScript.EpisodeScript();
                    currentEpScript.setEpisodeNumber(epNum(t));
                    currentEpScript.setSceneSections(new ArrayList<>());
                    currentSceneSection = null;
                    continue;
                }
            }

            // ── 正文内容，按区块处理 ──
            // 注意：EP_SCRIPTS 区块中所有段落均为加粗，因此不过滤 bold 标志，
            //       其余区块仍只处理非加粗行（加粗行已作为区块标头在上方 continue 了）。
            switch (section) {
                case "CHARS" -> { if (!p.bold) handleCharLine(t, result); }
                case "SCENES" -> { if (!p.bold) handleSceneLine(t, result); }
                case "SYNOPSIS" -> {
                    if (!p.bold) {
                        String ex = result.getSynopsis() != null ? result.getSynopsis() : "";
                        result.setSynopsis(ex.isEmpty() ? t : ex + "\n" + t);
                    }
                }
                case "EP_OUTLINES" -> {
                    if (!p.bold && currentOutline != null) {
                        String ex = currentOutline.getContent();
                        currentOutline.setContent((ex == null || ex.isEmpty()) ? t : ex + "\n" + t);
                    }
                }
                case "EP_SCRIPTS" -> {
                    // 分集脚本内容全部加粗，不过滤；仅跳过非当前集的行
                    if (currentEpScript != null) {
                        currentSceneSection = handleScriptLine(t, currentEpScript, currentSceneSection);
                    }
                }
            }
        }

        // 最后一条未收尾的数据
        if (currentOutline != null) result.getEpisodeOutlines().add(currentOutline);
        finalizeScene(currentEpScript, currentSceneSection);
        if (currentEpScript != null) result.getEpisodeScripts().add(currentEpScript);

        // 推算集数
        int maxOutlineEp = result.getEpisodeOutlines().stream()
            .mapToInt(ParsedScript.EpisodeOutline::getEpisodeNumber).max().orElse(0);
        int maxScriptEp = result.getEpisodeScripts().stream()
            .mapToInt(ParsedScript.EpisodeScript::getEpisodeNumber).max().orElse(0);
        result.setEpisodeCount(Math.max(maxOutlineEp, maxScriptEp));

        return result;
    }

    // ── 预处理：提取段落 + 合并续行 ──────────────────────────────────────────

    /**
     * 提取所有段落，并将"续行"（以"，"或"）"开头，或前行有未闭合括号）合并到上一行。
     * 注意：分集脚本区块中所有段落均为加粗，续行（"，乔月"、"）"）同样加粗，
     * 因此不能以 bold 属性过滤续行——无论加粗与否都做续行判断。
     *   "1-1室内（婆婆" / "，乔月" / "）" → "1-1室内（婆婆，乔月）"
     */
    private static List<Para> extractAndMerge(XWPFDocument doc) {
        List<Para> result = new ArrayList<>();
        for (XWPFParagraph p : doc.getParagraphs()) {
            String text = p.getText().trim();
            if (text.isEmpty()) continue;

            boolean bold = p.getRuns().stream().anyMatch(r -> Boolean.TRUE.equals(r.isBold()));

            // 续行合并：无论当前行是否加粗都判断（分集脚本中续行也是加粗的）
            if (!result.isEmpty()) {
                Para prev = result.get(result.size() - 1);
                if (isContinuation(text, prev.text)) {
                    prev.text = prev.text + text;
                    continue;
                }
            }
            result.add(new Para(text, bold));
        }
        return result;
    }

    private static boolean isContinuation(String cur, String prev) {
        if (cur.startsWith("，") || cur.equals("）")) return true;
        // 上一行有未闭合的中文括号
        long open  = prev.chars().filter(c -> c == '（').count();
        long close = prev.chars().filter(c -> c == '）').count();
        return open > close;
    }

    // ── 各区块内容处理 ────────────────────────────────────────────────────────

    private static void handleCharLine(String t, ParsedScript result) {
        int colonIdx = colonIdx(t);
        if (colonIdx > 0 && colonIdx < 12) {
            String name = t.substring(0, colonIdx).trim();
            String desc = t.substring(colonIdx + 1).trim();
            ParsedScript.Character ch = new ParsedScript.Character();
            ch.setName(name);
            ch.setDescription(desc);
            ch.setRoleType(inferRoleType(name, desc));
            ch.setGender(inferGender(name, desc));
            result.getCharacters().add(ch);
        }
    }

    private static void handleSceneLine(String t, ParsedScript result) {
        // "1、小区居民房（主场景）（加强家）" or "1.xxx"
        if (!t.matches("^\\d+[、.].*")) return;
        String nameRaw = t.replaceFirst("^\\d+[、.]\\s*", "");
        String baseName = nameRaw.contains("（") ? nameRaw.substring(0, nameRaw.indexOf('（')).trim() : nameRaw.trim();
        ParsedScript.Scene scene = new ParsedScript.Scene();
        scene.setName(baseName.isEmpty() ? nameRaw : baseName);
        scene.setDescription(nameRaw);
        scene.setSceneType(inferSceneType(nameRaw));
        result.getScenes().add(scene);
    }

    /**
     * 处理分集脚本正文行，返回（可能更新的）currentSceneSection。
     */
    private static ParsedScript.SceneSection handleScriptLine(
            String t,
            ParsedScript.EpisodeScript epScript,
            ParsedScript.SceneSection currentScene) {

        if (t.startsWith("场地") && containsColon(t)) {
            epScript.setLocation(afterColon(t));
            return currentScene;
        }
        if (t.startsWith("人物") && containsColon(t)) {
            epScript.setCharacterNames(afterColon(t));
            return currentScene;
        }
        if (isSectionMarker(t)) {
            // 新场景段：如 "1-1室内（婆婆，乔月）"
            if (currentScene != null && !currentScene.getShots().isEmpty()) {
                epScript.getSceneSections().add(currentScene);
            }
            String stripped = stripAnnotation(t);
            ParsedScript.SceneSection ss = new ParsedScript.SceneSection();
            ss.setSectionId(sectionId(stripped));
            ss.setCharacterNames(parenContent(stripped));
            ss.setShots(new ArrayList<>());
            return ss;
        }
        if (isShotLine(t)) {
            if (currentScene == null) {
                // 无明确场景标头时创建默认段
                currentScene = new ParsedScript.SceneSection();
                currentScene.setSectionId("1");
                currentScene.setShots(new ArrayList<>());
            }
            ParsedScript.Shot shot = new ParsedScript.Shot();
            shot.setShotNumber(shotNum(t));
            shot.setDescription(afterFirstColon(t));
            currentScene.getShots().add(shot);
        }
        return currentScene;
    }

    private static void finalizeScene(ParsedScript.EpisodeScript epScript, ParsedScript.SceneSection scene) {
        if (epScript != null && scene != null && !scene.getShots().isEmpty()) {
            epScript.getSceneSections().add(scene);
        }
    }

    // ── 字符串工具 ────────────────────────────────────────────────────────────

    private static boolean isEpHeader(String t) {
        return t.matches("^第\\d{1,2}集.*");
    }

    private static int epNum(String t) {
        Matcher m = Pattern.compile("第(\\d{1,2})集").matcher(t);
        return m.find() ? Integer.parseInt(m.group(1)) : 0;
    }

    private static boolean isSectionMarker(String t) {
        return stripAnnotation(t).trim().matches("^\\d+-\\d+.*");
    }

    private static boolean isShotLine(String t) {
        return t.matches("^镜头\\d+[：:].+");
    }

    private static int shotNum(String t) {
        Matcher m = Pattern.compile("^镜头(\\d+)").matcher(t);
        return m.find() ? Integer.parseInt(m.group(1)) : 0;
    }

    private static String sectionId(String t) {
        Matcher m = Pattern.compile("^(\\d+-\\d+)").matcher(t.trim());
        return m.find() ? m.group(1) : t.substring(0, Math.min(6, t.length()));
    }

    /** 去掉【...】前缀注释，如"【转场】2-2..."→"2-2..." */
    private static String stripAnnotation(String t) {
        return t.replaceAll("^【[^】]*】\\s*", "");
    }

    /** 提取第一个（...）内的内容 */
    private static String parenContent(String t) {
        int s = t.indexOf('（'), e = t.lastIndexOf('）');
        return (s >= 0 && e > s) ? t.substring(s + 1, e).trim() : "";
    }

    private static boolean containsColon(String t) {
        return t.indexOf('：') >= 0 || t.indexOf(':') >= 0;
    }

    private static int colonIdx(String t) {
        for (int i = 0; i < t.length(); i++) {
            char c = t.charAt(i);
            if (c == '：' || c == ':') return i;
        }
        return -1;
    }

    private static String afterColon(String t) {
        int idx = colonIdx(t);
        return idx >= 0 ? t.substring(idx + 1).trim() : t.trim();
    }

    private static String afterFirstColon(String t) {
        return afterColon(t);
    }

    private static String cleanTitle(String t) {
        return t.replaceAll("[《》]", "").trim();
    }

    // ── 推断逻辑 ──────────────────────────────────────────────────────────────

    private static String inferRoleType(String name, String desc) {
        String c = name + desc;
        if (c.contains("女主") || c.contains("大女主"))              return "female_lead";
        if (c.contains("男主"))                                       return "male_lead";
        if (c.contains("婆婆") || c.contains("公公") ||
            (c.contains("恶") && (c.contains("相") || c.contains("相")))) return "antagonist";
        if (c.contains("渣男") || c.contains("丈夫") || c.contains("老公")) return "male_lead";
        return "supporting";
    }

    private static String inferGender(String name, String desc) {
        String c = name + desc;
        if (c.contains("男") || c.contains("公公") || c.contains("丈夫") || c.contains("老公")) return "male";
        return "female";
    }

    private static String inferSceneType(String name) {
        if (name.contains("户外") || name.contains("街道") || name.contains("市场")
            || name.contains("法院") || name.contains("公园")) return "outdoor";
        return "indoor";
    }
}
