"""基于位置 + 文字模式 + 字体样式的段落角色识别。"""

from __future__ import annotations
import re
from fastapi_modules.fastapi_leaudit.govdoc_engine.models import Document, Paragraph, Role


HEADING_1_RE = re.compile(r"^[一二三四五六七八九十百]+、")
HEADING_2_RE = re.compile(r"^（[一二三四五六七八九十]+）")
HEADING_3_RE = re.compile(r"^\d+[\.．]")
HEADING_4_RE = re.compile(r"^（\d+）")
DOC_NUMBER_RE = re.compile(r"[一-龥]+[〔\[]\d{4}[〕\]]第?\d+号")
DATE_RE = re.compile(
    r"^\d{4}年\d{1,2}月\d{1,2}日$"
    r"|^[一二三四五六七八九十○〇零]+年[一二三四五六七八九十○〇零]+月[一二三四五六七八九十○〇零]+日$"
)
ATTACHMENT_RE = re.compile(r"^附件[:：1-9]")
NO_TEXT_RE = re.compile(r"^[\(（]\s*此页无正文\s*[\)）]")
RECIPIENT_TAIL_RE = re.compile(r"[：:]\s*$")
RECIPIENT_HINTS = (
    "局", "委", "府", "厅", "办", "公司", "各", "处室",
    "委员会", "署", "院", "部", "司", "处",
)
RECIPIENT_BLOCKLIST = (
    "现将", "兹", "经研究", "为做好", "为深入", "为进一步",
    "根据", "如下", "汇报", "通知如下", "请示如下",
)


class RuleBasedTagger:
    def tag(self, doc: Document) -> None:
        n = len(doc.paragraphs)
        for i, p in enumerate(doc.paragraphs):
            role, conf = self._classify(p, i, n, doc)
            p.role = role
            p.role_confidence = conf

    def _classify(
        self, p: Paragraph, idx: int, total: int, doc: Document
    ) -> tuple[Role, float]:
        text = p.text.strip()

        if not text:
            return ("unknown", 0.5)

        if NO_TEXT_RE.match(text):
            return ("no_text_marker", 1.0)

        if ATTACHMENT_RE.match(text):
            return ("attachment_marker", 0.95)

        if DATE_RE.match(text):
            return ("date", 0.9)

        if DOC_NUMBER_RE.search(text) and idx <= 5:
            return ("doc_number", 0.95)

        if idx == 0 or (
            idx <= 2
            and p.style.alignment == "center"
            and (p.style.font_size_pt or 0) >= 18
        ):
            return ("title", 0.95)

        font = (p.style.font_eastasia or "").strip()
        size = p.style.font_size_pt or 0

        if self._is_attachment_title(p, idx, doc):
            return ("attachment_title", 0.9)

        if HEADING_1_RE.match(text):
            conf = 0.95 if "黑体" in font else 0.7
            return ("heading_1", conf)

        if HEADING_2_RE.match(text):
            conf = 0.95 if "楷体" in font else 0.7
            return ("heading_2", conf)

        if HEADING_3_RE.match(text):
            conf = 0.9 if "仿宋" in font else 0.65
            return ("heading_3", conf)

        if HEADING_4_RE.match(text):
            return ("heading_4", 0.85)

        if (
            idx <= 6
            and 3 <= len(text) <= 50
            and RECIPIENT_TAIL_RE.search(text)
            and any(kw in text for kw in RECIPIENT_HINTS)
            and not any(kw in text for kw in RECIPIENT_BLOCKLIST)
        ):
            return ("recipient", 0.9)

        if total - idx <= 3 and 5 <= len(text) <= 30 and any(
            kw in text
            for kw in ["局", "公司", "委员会", "人民政府", "办公厅", "办公室"]
        ):
            return ("signature", 0.7)

        if size >= 14 or font:
            return ("body", 0.85)

        return ("unknown", 0.4)

    @staticmethod
    def _is_attachment_title(p: Paragraph, idx: int, doc: Document) -> bool:
        """识别附件正文首页标题，避免按普通正文套用 GW-F-004。"""
        if idx <= 0:
            return False
        text = p.text.strip()
        font = (p.style.font_eastasia or "").strip()
        if (
            p.style.alignment != "center"
            or (p.style.font_size_pt or 0) < 18
            or "小标宋" not in font
        ):
            return False

        marker_index = None
        marker_text = ""
        for prev in reversed(doc.paragraphs[:idx]):
            if prev.role == "attachment_marker" or ATTACHMENT_RE.match(prev.text.strip()):
                marker_index = prev.index
                marker_text = prev.text.strip()
                break
        if marker_index is None or idx - marker_index > 12:
            return False

        attachment_name = re.sub(r"^附件\d*[:：]\s*", "", marker_text).strip()
        attachment_name = re.sub(r"^\d+[\.．、)]\s*", "", attachment_name).strip()
        return not attachment_name or text == attachment_name or text in attachment_name or attachment_name in text
