"""从已 tag 的 Document 抽取语义实体（结构化优先）。"""

from __future__ import annotations
import re
from fastapi_modules.fastapi_leaudit.govdoc_engine.models import Document
from fastapi_modules.fastapi_leaudit.govdoc_engine.parser.entities import SemanticEntity


# 8 个内置实体名（也用于 schema 校验冲突）
BUILTIN_ENTITY_NAMES: frozenset[str] = frozenset({
    "title", "doc_number", "recipient", "date",
    "signature", "attachments", "wenzhong", "issuer",
})


# 内置实体的 LLM 兜底 prompt 描述（Phase B 使用）
BUILTIN_LLM_DESCRIPTION: dict[str, str] = {
    "title":       "公文主标题（不含发文字号）",
    "doc_number":  "X发〔YYYY〕N号 形式的发文字号",
    "recipient":   "公文抬头的接收机关名称",
    "date":        "末尾的成文日期原文",
    "signature":   "末尾的发文机关署名",
    "attachments": "附件清单（数组，每项含 序号 与 名称）",
    "wenzhong":    "公文文种（决议/决定/通知/通报/请示/批复 等 15 种之一）",
    "issuer":      "发文机关全称",
}


# role → entity name 的 1:1 映射
_ROLE_ENTITY_MAP = {
    "title": "title",
    "doc_number": "doc_number",
    "recipient": "recipient",
    "date": "date",
    "signature": "signature",
}


_ATTACHMENT_HEAD_RE = re.compile(r"^附件\d*[:：]\s*")
_ATTACHMENT_ITEM_RE = re.compile(r"^\s*(\d+)[\.．、)]\s*(.+)$")

# 15 种法定文种（参照《党政机关公文处理工作条例》）
_WENZHONG_LIST = (
    "决议", "决定", "命令", "公报", "公告", "通告",
    "意见", "通知", "通报", "报告", "请示", "批复",
    "议案", "函", "纪要",
)
_WENZHONG_RE = re.compile("(" + "|".join(_WENZHONG_LIST) + ")$")

# 「XX关于...的YY」 → issuer = XX
_ISSUER_PREFIX_RE = re.compile(r"^(.+?)关于")


class EntityBuilder:
    """从已 tag 的 Document 抽取 8 个内置语义实体。"""

    def build(self, doc: Document) -> dict[str, SemanticEntity | None]:
        entities: dict[str, SemanticEntity | None] = {
            name: None for name in BUILTIN_ENTITY_NAMES
        }

        # ① 一对一 role → entity
        for role, name in _ROLE_ENTITY_MAP.items():
            paras = [p for p in doc.paragraphs if p.role == role]
            if not paras:
                continue
            target = paras[-1] if name == "signature" else paras[0]
            entities[name] = SemanticEntity(
                name=name,
                text=target.text.strip(),
                paragraph_indices=[target.index],
                primary_role=role,
                style=target.style,
                source="structural",
                confidence=target.role_confidence,
            )

        # ② attachments：attachment_marker + 跟随行
        entities["attachments"] = self._build_attachments(doc)

        # ③ 派生：wenzhong / issuer
        title_e = entities.get("title")
        if title_e:
            entities["wenzhong"] = self._derive_wenzhong(title_e)
            entities["issuer"] = self._derive_issuer(
                title_e, entities.get("signature")
            )
        elif entities.get("signature"):
            entities["issuer"] = self._derive_issuer(
                None, entities["signature"]
            )

        return entities

    # ---------- attachments ----------
    def _build_attachments(self, doc: Document) -> SemanticEntity | None:
        markers = [
            i for i, p in enumerate(doc.paragraphs)
            if p.role == "attachment_marker"
        ]
        if not markers:
            return None

        m = markers[0]
        items: list[dict] = []
        para_idxs: list[int] = [m]

        first = doc.paragraphs[m].text.strip()
        head = _ATTACHMENT_HEAD_RE.sub("", first)
        if head:
            mt = _ATTACHMENT_ITEM_RE.match(head)
            if mt:
                items.append(
                    {"序号": int(mt.group(1)), "名称": mt.group(2).strip()}
                )
            else:
                items.append({"序号": 1, "名称": head})

        # 后续顺序行：直到遇到非 body / unknown 的段
        for j in range(m + 1, len(doc.paragraphs)):
            p = doc.paragraphs[j]
            if p.role and p.role not in ("body", "unknown", "attachment_marker"):
                break
            t = p.text.strip()
            if not t:
                continue
            mt = _ATTACHMENT_ITEM_RE.match(t)
            if not mt:
                break
            items.append(
                {"序号": int(mt.group(1)), "名称": mt.group(2).strip()}
            )
            para_idxs.append(p.index)

        if not items:
            return None

        text = "; ".join(f"{it['序号']}. {it['名称']}" for it in items)
        return SemanticEntity(
            name="attachments",
            text=text,
            paragraph_indices=para_idxs,
            primary_role="attachment_marker",
            style=doc.paragraphs[m].style,
            extra={"items": items},
            source="structural",
            confidence=0.9,
        )

    # ---------- 派生 ----------
    def _derive_wenzhong(
        self, title: SemanticEntity
    ) -> SemanticEntity | None:
        m = _WENZHONG_RE.search(title.text)
        if not m:
            return None
        return SemanticEntity(
            name="wenzhong",
            text=m.group(1),
            paragraph_indices=list(title.paragraph_indices),
            primary_role="title",
            extra={"derived_from": "title.suffix"},
            source="derived",
            confidence=0.95,
        )

    def _derive_issuer(
        self,
        title: SemanticEntity | None,
        signature: SemanticEntity | None,
    ) -> SemanticEntity | None:
        if title:
            m = _ISSUER_PREFIX_RE.match(title.text)
            if m:
                return SemanticEntity(
                    name="issuer",
                    text=m.group(1),
                    paragraph_indices=list(title.paragraph_indices),
                    primary_role="title",
                    extra={"derived_from": "title.prefix"},
                    source="derived",
                    confidence=0.9,
                )
        if signature:
            return SemanticEntity(
                name="issuer",
                text=signature.text,
                paragraph_indices=list(signature.paragraph_indices),
                primary_role="signature",
                style=signature.style,
                extra={"derived_from": "signature"},
                source="derived",
                confidence=0.8,
            )
        return None
