# -*- coding: utf-8 -*-
"""
需求长句拆分器。
将复合长句拆分为可验证的原子需求片段。
"""

import re
from typing import List


class RequirementSplitter:
    ACTION_HINTS = [
        "产生",
        "发送",
        "设置",
        "进入",
        "退出",
        "关闭",
        "开启",
        "监测",
        "判断",
        "记录",
        "上传",
        "重启",
        "恢复",
        "关断",
        "断电",
        "加电",
        "执行",
        "进行",
    ]

    CONNECTOR_HINTS = ["并", "并且", "同时", "然后", "且", "以及", "及"]
    CONDITIONAL_HINTS = ["如果", "当", "若", "在", "其中", "此时", "满足"]
    CONTEXT_PRONOUN_HINTS = ["该", "其", "上述", "此", "这些", "那些"]
    CHAIN_HINTS = ["从而", "以便", "用于", "以实现", "并据此", "进而", "从而实现"]
    ENUMERATION_HINTS = ["具体包括", "包括但不限于", "主要包括", "其中包括", "如下"]

    def __init__(self, max_sentence_len: int = 160, min_clause_len: int = 20):
        self.max_sentence_len = max_sentence_len
        self.min_clause_len = min_clause_len

    def split(self, text: str) -> List[str]:
        cleaned = self._clean(text)
        if not cleaned:
            return []

        if self._contains_strong_semantic_chain(cleaned):
            return [cleaned]

        # 先按强分隔符切分为主片段。
        base_parts = self._split_by_strong_punctuation(cleaned)

        result: List[str] = []
        for part in base_parts:
            if len(part) <= self.max_sentence_len:
                result.append(part)
                continue

            # 对超长片段进一步基于逗号和连接词拆分。
            refined = self._split_long_clause(part)
            result.extend(refined)

        result = self._merge_semantic_chain(result)
        result = self._merge_too_short(result)
        return self._deduplicate(result)

    def _contains_strong_semantic_chain(self, text: str) -> bool:
        # 条件-动作链完整时，避免强拆。
        has_conditional = any(h in text for h in ["如果", "若", "当"])
        has_result = "则" in text or "时" in text
        action_count = sum(1 for h in self.ACTION_HINTS if h in text)
        if has_conditional and has_result and action_count >= 2:
            return True
        return False

    def _clean(self, text: str) -> str:
        text = re.sub(r"\s+", " ", text or "")
        return text.strip(" ;；。")

    def _split_by_strong_punctuation(self, text: str) -> List[str]:
        chunks = re.split(r"[；;。]", text)
        return [c.strip(" ，,") for c in chunks if c and c.strip(" ，,")]

    def _split_long_clause(self, clause: str) -> List[str]:
        if self._contains_strong_semantic_chain(clause):
            return [clause]

        raw_parts = [x.strip() for x in re.split(r"[，,]", clause) if x.strip()]
        if len(raw_parts) <= 1:
            return [clause]

        assembled: List[str] = []
        current = raw_parts[0]

        for fragment in raw_parts[1:]:
            if self._should_split(current, fragment):
                assembled.append(current.strip())
                current = fragment
            else:
                current = f"{current}，{fragment}"

        if current.strip():
            assembled.append(current.strip())

        return assembled

    def _should_split(self, current: str, fragment: str) -> bool:
        if len(current) < self.min_clause_len:
            return False

        # “具体包括/其中包括”后的列举项通常是上一句延伸，不应拆分为独立需求。
        if any(h in current for h in self.ENUMERATION_HINTS):
            return False

        # 承接链条短语一般不是独立需求动作，避免切断语义链。
        if any(fragment.startswith(h) for h in self.CHAIN_HINTS):
            return False

        # 指代承接片段通常是语义延续，不应切断。
        if any(fragment.startswith(h) for h in self.CONTEXT_PRONOUN_HINTS):
            return False

        # 条件链中带“则/并/同时”的后继片段，优先保持在同一需求中。
        if self._contains_strong_semantic_chain(current + "，" + fragment):
            return False

        frag_starts_with_condition = any(fragment.startswith(h) for h in self.CONDITIONAL_HINTS)
        if frag_starts_with_condition:
            return False

        has_connector = any(fragment.startswith(h) for h in self.CONNECTOR_HINTS)
        has_action = any(h in fragment for h in self.ACTION_HINTS)
        current_has_action = any(h in current for h in self.ACTION_HINTS)

        # 并列连接词后接“控制/处理/显示”等限定短语时，优先视为同一需求。
        if has_connector and len(fragment) < self.max_sentence_len // 3 and not any(
            kw in fragment for kw in ["并输出", "并上传", "并记录", "并触发"]
        ):
            return False

        # 连接词 + 动作词，且当前片段已经包含动作，优先拆分。
        if has_connector and has_action and current_has_action:
            return True

        # 无连接词但出现新的动作片段且整体过长，也拆分。
        if has_action and current_has_action and len(current) >= self.max_sentence_len // 2:
            return True

        return False

    def _merge_semantic_chain(self, parts: List[str]) -> List[str]:
        if not parts:
            return []

        merged: List[str] = [parts[0]]
        for part in parts[1:]:
            prev = merged[-1]
            if self._should_merge(prev, part):
                merged[-1] = f"{prev}；{part}"
            else:
                merged.append(part)
        return merged

    def _should_merge(self, prev: str, current: str) -> bool:
        if any(h in prev for h in self.ENUMERATION_HINTS):
            return True

        # 指代开头：如“该报警信号...”。
        if any(current.startswith(h) for h in self.CONTEXT_PRONOUN_HINTS):
            return True

        # 报警触发后的持续条件与动作属于同一链。
        if ("报警" in prev and "持续" in current) or ("产生" in prev and "报警" in prev and "持续" in current):
            return True

        # 状态迁移 + 后续控制动作保持合并。
        if ("进入" in prev or "设置" in prev or "发送" in prev) and ("则" in current or "连续" in current):
            return True

        # 条件链分裂片段重新合并。
        if self._contains_strong_semantic_chain(prev + "，" + current):
            return True

        return False

    def _merge_too_short(self, parts: List[str]) -> List[str]:
        if not parts:
            return []

        merged: List[str] = []
        for part in parts:
            if merged and len(part) < self.min_clause_len:
                merged[-1] = f"{merged[-1]}，{part}"
            else:
                merged.append(part)
        return merged

    def _deduplicate(self, parts: List[str]) -> List[str]:
        seen = set()
        result = []
        for part in parts:
            key = re.sub(r"\s+", "", part)
            if key and key not in seen:
                seen.add(key)
                result.append(part)
        return result
