# 要点に付けさせた引用が、原文に本当にあるかを照合する。
#
# 使い方:
#   python tools/quote_check.py 原文.txt 要点.md
#   python tools/quote_check.py 原文.txt 要点.md --min 20
#   python tools/quote_check.py 規程.txt 要点.md --article
#
# 要点のファイルからは「」で囲まれた文字列を引用として拾う。
# PDF から取り出した原文は改行と空白が混ざるので、両方から空白を除いて比べる。
# 短い引用は偶然一致するので、既定では20文字未満を「短すぎる」として分ける。
# --article を付けると、原文を「第N条」で切って、引用がどの条にあったかも出す。
# 要点の側で引用の直前に書かれた条番号と食い違えば、条の取り違えとして印を付ける。
import re
import sys
import unicodedata

try:
    sys.stdout.reconfigure(encoding="utf-8")
except Exception:
    pass

NUM = r"[0-9]+|[〇一二三四五六七八九十百千]+"
ART_RE = re.compile(r"第\s*(" + NUM + r")\s*条")
HEAD_RE = re.compile(r"^\s*第\s*(" + NUM + r")\s*条")
KANSU = {"〇": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5,
         "六": 6, "七": 7, "八": 8, "九": 9}


def art_no(s: str) -> str:
    """第一条も第1条も同じ番号として扱う"""
    if s.isdigit():
        return str(int(s))
    total, cur = 0, 0
    for ch in s:
        if ch in KANSU:
            cur = KANSU[ch]
        elif ch == "十":
            total += (cur or 1) * 10
            cur = 0
        elif ch == "百":
            total += (cur or 1) * 100
            cur = 0
        elif ch == "千":
            total += (cur or 1) * 1000
            cur = 0
    return str(total + cur)


def norm(s: str) -> str:
    return re.sub(r"\s+", "", unicodedata.normalize("NFKC", s))


def quotes_in(text: str) -> list[tuple[str, str]]:
    """「」で囲まれた文字列を引用として拾う。直前に出てくる条番号も一緒に返す"""
    out = []
    for m in re.finditer(r"「([^「」]{2,})」", text):
        before = text[max(0, m.start() - 60):m.start()]
        arts = [art_no(a) for a in ART_RE.findall(before)]
        out.append((m.group(1).strip(), arts[-1] if arts else ""))
    return out


def split_articles(src: str) -> list[tuple[str, str]]:
    """原文を「第N条」で切る。戻り値は（条番号, 正規化した本文）

    条の見出しは行の先頭に来る。文の途中の「第N条」は他の条への参照なので拾わない。
    目次にも行頭の「第N条」が並ぶが、そちらは1行で終わるので、同じ条番号が
    2回以上出てきたら長いほうを本文とみなす。
    """
    blocks: dict[str, list[str]] = {}
    cur = None
    for line in src.split("\n"):
        m = HEAD_RE.match(line)
        if m:
            cur = art_no(m.group(1))
            blocks.setdefault(cur, []).append("")
        if cur:
            blocks[cur][-1] += line + "\n"
    out = [(num, norm(max(bodies, key=len))) for num, bodies in blocks.items()]
    return sorted(out, key=lambda kv: int(kv[0]))


def main() -> int:
    args = [a for a in sys.argv[1:] if not a.startswith("--")]
    if len(args) < 2:
        print("python tools/quote_check.py 原文.txt 要点.md [--min 20] [--article]")
        return 1
    minlen = 20
    if "--min" in sys.argv:
        minlen = int(sys.argv[sys.argv.index("--min") + 1])
    by_article = "--article" in sys.argv

    src = open(args[0], encoding="utf-8").read()
    note = open(args[1], encoding="utf-8").read()
    nsrc = norm(src)
    qs = quotes_in(note)
    if not qs:
        print("引用が1つもない。依頼文で引用を義務づけたか確認する")
        return 1
    arts = split_articles(src) if by_article else []

    ok, short, miss, wrong = [], [], [], []
    for q, said in qs:
        nq = norm(q)
        if nq not in nsrc:
            miss.append((q, said, ""))
        elif len(nq) < minlen:
            short.append((q, said, ""))
        elif by_article:
            hits = [num for num, body in arts if nq in body]
            found = hits[0] if hits else ""
            if said and found and said != found:
                wrong.append((q, said, found))
            else:
                ok.append((q, said, found))
        else:
            ok.append((q, said, ""))

    print(f"原文 {len(src):,}字（空白を除くと {len(nsrc):,}字） / 引用 {len(qs)}件")
    if by_article:
        print(f"  条に切れた数          {len(arts)}条")
    print(f"  原文にあった          {len(ok)}件")
    print(f"  短すぎる（{minlen}字未満）  {len(short)}件")
    print(f"  原文になかった        {len(miss)}件")
    if by_article:
        print(f"  条番号が違う          {len(wrong)}件")
    for q, _s, _f in miss:
        print(f"    × {q[:60]}")
    for q, said, found in wrong:
        print(f"    ! 第{said}条と書いてあるが、原文では第{found}条 / {q[:40]}")
    for q, _s, _f in short:
        print(f"    △ {q[:60]}")
    return 1 if (miss or wrong) else 0


if __name__ == "__main__":
    sys.exit(main())
