#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""check_refs.py — 检查 LaTeX 书稿的引用完整性与英文残留。"""
import glob
import os
import re

ROOT = os.path.dirname(os.path.abspath(__file__))

CITE_RE = re.compile(r"\\cite[a-z]*\{([^}]*)\}")
BIBKEY_RE = re.compile(r"@\w+\{([^,]+),")

# 允许出现的英文/缩写白名单
WHITELIST = {
    "pdf", "doi", "gpa", "dsm", "icd", "rem", "timss", "pisa", "oecd", "ai",
    "khanmigo", "supermemo", "duolingo", "bloom", "kestin", "et", "al",
    "world", "bank", "tutor", "copilot", "oreopoulos", "low", "self",
    "exploitation", "effort", "preserving", "cognitive", "offloading",
    "spacing", "effect", "credential", "inflation", "shadow", "education",
    "total", "institution", "public", "transcript", "hidden", "strategy",
    "tactic", "mastery", "learning", "identity", "role", "confusion",
    "variable", "reward", "involution", "grit", "panopticon", "alexithymia",
    "neuroadaptation", "self-concealment", "covering", "reactance",
    "expressive", "writing", "of", "and", "the", "a", "in", "on", "for",
    "vs", "not", "to", "with", "as", "by", "at", "is", "it",
}

STRIP_CMDS = re.compile(
    r"\\(?:cite[a-z]*|ref|label|eqref|pageref|includegraphics|bibliography"
    r"|bibliographystyle|usepackage|documentclass|input|pgfplotsset"
    r"|usetikzlibrary|newtcolorbox|definecolor|addcontentsline|setlength"
    r"|renewcommand|newcommand|hypersetup|tcbuselibrary|setmainfont"
    r"|geometry|documentclass)\b(?:\[[^\]]*\])?(?:\{[^{}]*\})*"
)
STRIP_ENV = re.compile(r"\\begin\{[^}]*\}|\\end\{[^}]*\}|\\[a-zA-Z@]+\*?(?:\[[^\]]*\])?")


def main():
    bib_path = os.path.join(ROOT, "references.bib")
    bib = open(bib_path, encoding="utf-8").read()
    keys = set(BIBKEY_RE.findall(bib))

    files = sorted(glob.glob(os.path.join(ROOT, "chapters", "*.tex")))
    files.append(os.path.join(ROOT, "appendix", "appendix.tex"))

    used = {}
    for f in files:
        if not os.path.exists(f):
            continue
        t = open(f, encoding="utf-8").read()
        for m in CITE_RE.findall(t):
            for k in m.split(","):
                k = k.strip()
                if k:
                    used.setdefault(k, []).append(os.path.basename(f))

    missing = sorted(k for k in used if k not in keys)
    unused = sorted(k for k in keys if k not in used)

    print("bib 条目数: %d" % len(keys))
    print("被引用键数: %d" % len(used))
    print("\n!! 正文引用但 bib 缺失 (%d):" % len(missing))
    for k in missing:
        print("    %-28s %s" % (k, ",".join(sorted(set(used[k])))))
    print("\n!! bib 定义但从未引用 (%d):" % len(unused))
    for k in unused:
        print("    %s" % k)

    print("\n==== 正文残留英文 ====")
    for f in files:
        if not os.path.exists(f):
            continue
        t = open(f, encoding="utf-8").read()
        t = re.sub(r"(?<!\\)%.*", "", t)
        t = STRIP_CMDS.sub(" ", t)
        t = STRIP_ENV.sub(" ", t)
        hits = []
        for m in re.finditer(r"[A-Za-z][A-Za-z\-]{2,40}", t):
            s = m.group()
            if s.lower() in WHITELIST:
                continue
            hits.append(s)
        if hits:
            print("  %s:" % os.path.basename(f))
            for h in sorted(set(hits)):
                print("      %s" % h)


if __name__ == "__main__":
    main()
