#!/usr/bin/env python3
"""BGG Author Fact-Check — prüft Autoren-Zuordnungen gegen BoardGameGeek.

Extrahiert Spiel-Autor-Paare aus Artikel-HTML und verifiziert über die
BGG XML API v2, ob der genannte Autor tatsächlich Designer des Spiels ist.

Usage: python3 check_authors.py <artikel.html>
Exit 0 = alle Autoren korrekt, 1 = Fehler gefunden, 2 = API/Warning
"""
import sys, re, urllib.request, urllib.parse, xml.etree.ElementTree as ET, time

BGG_KEY_FILE = "/tmp/.bggkey"

def get_bgg_key():
    try:
        with open(BGG_KEY_FILE) as f:
            return f.read().strip()
    except FileNotFoundError:
        return None

def bgg_search(game_name):
    """Search BGG for a game, return results with exact matches first."""
    key = get_bgg_key()
    url = f"https://boardgamegeek.com/xmlapi2/search?query={urllib.parse.quote(game_name)}&type=boardgame"
    headers = {}
    if key:
        headers["Authorization"] = f"Bearer {key}"
    
    req = urllib.request.Request(url, headers=headers)
    for attempt in range(3):
        try:
            resp = urllib.request.urlopen(req, timeout=15)
            if resp.getcode() == 202:
                time.sleep(3)
                continue
            root = ET.fromstring(resp.read())
            results = []
            for item in root.findall("item"):
                results.append({
                    "id": item.attrib.get("id"),
                    "name": item.find("name").attrib.get("value", ""),
                    "year": item.find("yearpublished").attrib.get("value", "") if item.find("yearpublished") is not None else ""
                })
            # Sort: exact name matches first
            exact = [r for r in results if r["name"].lower() == game_name.lower()]
            others = [r for r in results if r["name"].lower() != game_name.lower()]
            return exact + others
        except Exception:
            time.sleep(2)
    return []

def bgg_thing(game_id):
    """Get BGG thing data including designers."""
    key = get_bgg_key()
    url = f"https://boardgamegeek.com/xmlapi2/thing?id={game_id}&stats=1"
    headers = {}
    if key:
        headers["Authorization"] = f"Bearer {key}"
    req = urllib.request.Request(url, headers=headers)
    for attempt in range(3):
        try:
            resp = urllib.request.urlopen(req, timeout=15)
            if resp.getcode() == 202:
                time.sleep(3)
                continue
            root = ET.fromstring(resp.read())
            item = root.find("item")
            if item is None:
                return None
            designers = []
            for link in item.findall("link"):
                if link.attrib.get("type") == "boardgamedesigner":
                    designers.append(link.attrib.get("value", ""))
            return {
                "name": item.find("name").attrib.get("value", "") if item.find("name") is not None else "",
                "designers": designers,
                "year": item.find("yearpublished").attrib.get("value", "") if item.find("yearpublished") is not None else ""
            }
        except Exception:
            time.sleep(2)
    return None

def extract_game_author_pairs(html):
    """Extrahiert Spiel-Autor-Paare über BGG-Links + Infoboxen im Artikel."""
    pairs = []
    
    # Finde alle Infoboxen mit BGG-Link
    # Pattern: <p><strong>GAME</strong> · AUTHOR · ...
    infobox_pattern = re.compile(
        r'<div class="infobox">\s*<p[^>]*>'
        r'<strong>([^<]+)</strong>\s*·\s*'           # Game name
        r'([^·<]+)'                                    # Author (stop at next · or <)
        r'.*?boardgamegeek\.com/boardgame/(\d+)/',     # BGG ID
        re.DOTALL
    )
    
    for m in infobox_pattern.finditer(html):
        game = m.group(1).strip()
        author_raw = m.group(2).strip()
        bgg_id = m.group(3)
        # Clean author: remove nested tags
        author = re.sub(r'<[^>]+>', '', author_raw).strip()
        pairs.append({
            "game": game,
            "claimed_author": author,
            "bgg_id": bgg_id,
            "context": m.group(0)[:200]
        })
    
    # Pattern 2: Prose claims — find "für ... <strong>GAME</strong>" and backtrack for author
    prose_pattern = re.compile(
        r'(?:gewann|erhielt|bekam|holte).*?f\xfcr\s+.*?<strong>([^<]+)</strong>',
        re.DOTALL | re.IGNORECASE
    )
    
    for m in prose_pattern.finditer(html):
        game = m.group(1).strip()
        # Skip if already found in infobox check
        if any(p["game"] == game for p in pairs):
            continue
        # Backtrack up to 500 chars to find preceding <strong>AUTHOR</strong>
        start = max(0, m.start() - 500)
        before = html[start:m.start()]
        # Find the LAST <strong> before this match (likely the author)
        author_matches = re.findall(r'<strong>([^<]+)</strong>', before)
        if author_matches:
            author = author_matches[-1].strip()
            # Search BGG for this game
            results = bgg_search(game)
            if results:
                pairs.append({
                    "game": game,
                    "claimed_author": author,
                    "bgg_id": results[0]["id"],
                    "context": f"...{author}...gewann...f\xfcr {game}..."
                })
    
    return pairs

def normalize_name(name):
    """Normalize author name for comparison."""
    return re.sub(r'\s+', ' ', name.strip().lower())

def main():
    if len(sys.argv) < 2:
        print("Usage: check_authors.py <artikel.html>")
        sys.exit(1)
    
    with open(sys.argv[1]) as f:
        html = f.read()
    
    # Extract body
    body_match = re.search(r'<article>(.*?)</article>', html, re.DOTALL)
    body = body_match.group(1) if body_match else html
    
    pairs = extract_game_author_pairs(body)
    
    if not pairs:
        print("ℹ️  Keine Spiel-Autor-Paare zur Prüfung gefunden.")
        sys.exit(0)
    
    errors = 0
    warnings = 0
    
    for pair in pairs:
        game = pair["game"]
        print(f"\n🔍 Prüfe: »{game}«")
        
        # Use BGG ID directly if available, otherwise search
        bgg_id = pair.get("bgg_id")
        if bgg_id:
            thing = bgg_thing(bgg_id)
        else:
            results = bgg_search(game)
            if not results:
                print(f"   ⚠️  BGG nicht gefunden für »{game}«")
                warnings += 1
                continue
            thing = bgg_thing(results[0]["id"])
        
        if not thing:
            print(f"   ⚠️  BGG-Details nicht abrufbar (ID {bgg_id or '?'})")
            warnings += 1
            continue
        
        # Verify game name matches (avoid false positives from bad search results)
        bgg_name = thing["name"].lower()
        claimed_lower = game.lower()
        if bgg_name != claimed_lower and claimed_lower not in bgg_name and bgg_name not in claimed_lower:
            print(f"   ⚠️  BGG fand »{thing['name']}« statt »{game}« — Spiel vermutlich zu neu für BGG")
            warnings += 1
            continue
        
        designers = [normalize_name(d) for d in thing["designers"]]
        claimed = normalize_name(pair.get("claimed_author", ""))
        
        print(f"   BGG: {thing['name']} ({thing['year']})")
        print(f"   Designer: {', '.join(thing['designers']) if thing['designers'] else 'KEINE'}")
        
        if claimed and claimed not in " ".join(designers):
            # Try partial match (last name)
            claimed_parts = claimed.split()
            found = False
            for d in designers:
                if any(part in d for part in claimed_parts if len(part) > 2):
                    found = True
                    break
            if not found:
                print(f"   ❌ FAKTENFEHLER: »{pair.get('claimed_author', '?')}« ist NICHT Designer von »{thing['name']}«!")
                errors += 1
            else:
                print(f"   ✅ Teil-Match für »{pair.get('claimed_author', '?')}«")
        elif claimed:
            print(f"   ✅ Autor bestätigt")
        else:
            print(f"   ℹ️  Kein expliziter Autor-Anspruch zum Abgleichen")
    
    print(f"\n{'='*50}")
    if errors == 0 and warnings == 0:
        print("✅ ALLE AUTOREN-ZUORDNUNGEN KORREKT")
        sys.exit(0)
    elif errors == 0:
        print(f"⚠️  {warnings} Warnung(en) — KEINE Faktenfehler")
        sys.exit(0)
    else:
        print(f"❌ {errors} FAKTENFEHLER in Autoren-Zuordnungen!")
        sys.exit(1)

if __name__ == "__main__":
    main()
