#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
verify_refs.py — 用 Crossref API 逐条核实 references.bib 中的文献，
找出真实 DOI 并生成核实报告。

原则：绝不编造 DOI。找不到就标记为 UNVERIFIED。
"""
import re
import json
import time
import urllib.request
import urllib.parse
from difflib import SequenceMatcher

BIB = 'references.bib'
UA = 'book-doi-verify/1.0 (mailto:verify@example.org)'


def parse_bib(path):
    """极简 bib 解析器：返回 [(key, type, fields_dict)]"""
    text = open(path, encoding='utf-8').read()
    entries = []
    # 匹配 @type{key, ... }
    for m in re.finditer(r'@(\w+)\s*\{\s*([^,]+),', text):
        etype = m.group(1).lower()
        key = m.group(2).strip()
        start = m.end()
        # 括号平衡找到结尾
        depth = 1
        i = start
        while i < len(text) and depth > 0:
            if text[i] == '{':
                depth += 1
            elif text[i] == '}':
                depth -= 1
            i += 1
        body = text[start:i-1]
        fields = {}
        for fm in re.finditer(r'(\w+)\s*=\s*\{', body):
            fname = fm.group(1).lower()
            fs = fm.end()
            d = 1
            j = fs
            while j < len(body) and d > 0:
                if body[j] == '{':
                    d += 1
                elif body[j] == '}':
                    d -= 1
                j += 1
            fields[fname] = body[fs:j-1]
        entries.append((key, etype, fields))
    return entries


def clean(s):
    s = re.sub(r'\\[a-zA-Z]+\{?', '', s)
    s = s.replace('{', '').replace('}', '').replace('\\', '')
    s = re.sub(r'\s+', ' ', s)
    return s.strip()


def crossref_query(title, author=None, year=None):
    params = {'query.bibliographic': title, 'rows': '3'}
    url = 'https://api.crossref.org/works?' + urllib.parse.urlencode(params)
    req = urllib.request.Request(url, headers={'User-Agent': UA})
    try:
        with urllib.request.urlopen(req, timeout=25) as r:
            data = json.load(r)
    except Exception as e:
        return None, f'{type(e).__name__}: {e}'
    items = data.get('message', {}).get('items', [])
    if not items:
        return None, 'no results'
    # 选择标题最相似的一条
    best, best_score = None, 0.0
    t = clean(title).lower()
    for it in items:
        cand = (it.get('title') or [''])[0]
        score = SequenceMatcher(None, t, clean(cand).lower()).ratio()
        if score > best_score:
            best, best_score = it, score
    return best, best_score


def main():
    entries = parse_bib(BIB)
    report = []
    for key, etype, f in entries:
        title = clean(f.get('title', ''))
        author = clean(f.get('author', ''))
        year = clean(f.get('year', ''))
        existing_doi = clean(f.get('doi', ''))
        if etype in ('misc',) or not title:
            report.append({'key': key, 'type': etype, 'title': title,
                           'status': 'SKIP', 'reason': 'misc/no-title',
                           'doi': existing_doi, 'score': None})
            continue
        best, score = crossref_query(title, author, year)
        if best is None:
            report.append({'key': key, 'type': etype, 'title': title,
                           'status': 'ERROR', 'reason': str(score),
                           'doi': existing_doi, 'score': None})
            continue
        found_doi = best.get('DOI', '')
        found_title = clean((best.get('title') or [''])[0])
        found_year = ''
        try:
            found_year = str(best.get('issued', {}).get('date-parts', [['']])[0][0])
        except Exception:
            pass
        status = 'VERIFIED' if score >= 0.92 else ('LIKELY' if score >= 0.80 else 'WEAK')
        report.append({'key': key, 'type': etype, 'title': title,
                       'status': status, 'score': round(score, 3),
                       'doi': found_doi, 'found_title': found_title,
                       'found_year': found_year, 'existing_doi': existing_doi})
        time.sleep(0.4)
    json.dump(report, open('doi_report.json', 'w', encoding='utf-8'),
              ensure_ascii=False, indent=2)
    # 打印摘要
    from collections import Counter
    c = Counter(r['status'] for r in report)
    print('=== 核实摘要 ===')
    for k, v in sorted(c.items()):
        print(f'{k:10s} {v}')
    print(f'总计 {len(report)} 条\n')
    print('=== 需要人工处理的条目 ===')
    for r in report:
        if r['status'] in ('WEAK', 'ERROR', 'SKIP'):
            print(f"[{r['status']:6s}] {r['key']:28s} score={r.get('score')} | {r['title'][:60]}")
            if r.get('found_title'):
                print(f"          → found: {r['found_title'][:70]} ({r.get('found_year')}) doi={r.get('doi')}")


if __name__ == '__main__':
    main()
