"""
register_match.py — Lädt das NotebookLM-Register und bietet Lookup-Helfer.

Semantisches Matching ist Aufgabe des aufrufenden Agenten.
Diese Datei stellt nur Daten bereit, nicht die Entscheidungslogik.

Pfad ist Pflicht-Parameter — kein impliziter Vault-Default.
Der Aufrufer übergibt typischerweise `cfg.paths.register_path`.

Vertrag (eingefroren ab v0.11.0):
- `load_register(register_path: Path)` liefert eine **aktiv-gefilterte**
  Liste von Notebook-Dicts. Notebooks mit Status `veraltet` oder
  `archiviert` sind bereits raus. `is_allowed_notebook(notebook_id,
  notebooks)` erwartet diese aktive Liste als zweites Argument.
"""

import re
from pathlib import Path


_FILTERED_STATUSES = {"veraltet", "archiviert"}


def load_register(register_path: Path) -> list[dict]:
    """Liest das NotebookLM-Register und gibt eine Liste aktiver Notebooks zurück.

    register_path: Pfad zur Register-Markdown-Datei (Tabellen-Format).

    Pro Notebook ein Dict:
    {
        "title": "...",
        "id": "<uuid>",
        "themes": [...],
        "description": "...",
        "status": "aktiv"
    }

    Filter: status in ["veraltet", "archiviert"] wird rausgefiltert.
    Die Rückgabe ist garantiert eine aktiv-gefilterte Liste — wer alle
    Notebooks inklusive archivierter braucht, muss diese Funktion erweitern.
    """
    register_path = Path(register_path)
    text = register_path.read_text(encoding="utf-8")

    notebooks = []
    for line in text.splitlines():
        # Tabellen-Datenzeilen: | Titel | ID | Themen | Quellen | Beschreibung | Status |
        # Trennzeilen (|---|---|...) überspringen.
        # Header-Zeilen werden nicht über Spalten-Namen erkannt, sondern indirekt
        # über das UUID-Format unten — Header-Zellen haben kein UUID-Format,
        # also fallen sie automatisch raus.
        if not line.startswith("|") or re.match(r"^\|[-| ]+\|$", line):
            continue

        cells = [c.strip() for c in line.split("|")]
        # cells[0] ist leer (vor erstem |), cells[-1] ist leer (nach letztem |)
        cells = [c for c in cells if c != ""]

        if len(cells) < 2:
            continue

        title = cells[0]
        raw_id = cells[1]

        # ID aus Backtick-Wrapper extrahieren, falls vorhanden
        id_match = re.search(r"`([a-f0-9-]{36})`", raw_id)
        notebook_id = id_match.group(1) if id_match else raw_id.strip("`").strip()

        # UUID-Format prüfen (grober Check)
        if not re.match(r"^[a-f0-9-]{36}$", notebook_id):
            continue

        themes_raw = cells[2] if len(cells) > 2 else ""
        description = cells[4] if len(cells) > 4 else ""
        status = cells[5].strip() if len(cells) > 5 else "aktiv"

        # Leerer Status gilt als aktiv
        if not status:
            status = "aktiv"

        if status in _FILTERED_STATUSES:
            continue

        themes = [t.strip() for t in themes_raw.split(",") if t.strip()]

        notebooks.append({
            "title": title,
            "id": notebook_id,
            "themes": themes,
            "description": description,
            "status": status,
        })

    return notebooks


def is_allowed_notebook(notebook_id: str, notebooks: list[dict]) -> bool:
    """Prüft, ob ein Notebook in der aktiven Register-Liste enthalten ist.

    Erwartet die aktiv-gefilterte Liste aus `load_register()` — Notebooks
    mit Status `veraltet`/`archiviert` sind dort bereits raus, daher reicht
    der reine ID-Check.
    """
    return any(nb["id"] == notebook_id for nb in notebooks)


def match(query_text: str, notebooks: list[dict]) -> list[tuple[dict, float]]:
    """Leichter Vorfilter basierend auf Themen-Stichwort-Match (case-insensitive
    Substring-Treffer im Feld `themes` oder `title`).

    Returned: Liste von (notebook, score) Tupeln, sortiert absteigend nach score.
    Score: Anzahl Themen-Treffer, normalisiert auf [0, 1] (Treffer / max-mögliche-Treffer).

    Das finale semantische Matching macht der LLM-Agent im SKILL.md, NICHT hier.
    Dieser Vorfilter ist nur ein Cheap-Shortcut, der offensichtliche Treffer anzeigt.
    """
    if not notebooks or not query_text.strip():
        return []

    query_words = [w.lower() for w in query_text.split() if w.strip()]
    if not query_words:
        return []

    scored = []
    for nb in notebooks:
        searchable_terms = nb["themes"] + [nb["title"]]
        searchable_lower = [t.lower() for t in searchable_terms]

        hits = sum(
            1
            for word in query_words
            if any(word in term for term in searchable_lower)
        )

        max_hits = len(query_words)
        score = hits / max_hits if max_hits > 0 else 0.0

        if score > 0:
            scored.append((nb, score))

    scored.sort(key=lambda x: x[1], reverse=True)
    return scored
