"""Führt die Begriffsgruppen zu `content/glossar.json` zusammen.

Das Glossar entsteht gruppenweise – Umgangsarten, Waffenarten, Erlaubniswesen
und so fort. Dieses Skript legt die Teilstücke zusammen, sortiert alphabetisch
nach deutscher Regel und setzt den Kopf mit dem tatsächlichen Gesetzesstand.

Ein Teilstück ist eine JSON-Datei, entweder mit Umschlag::

    { "eintraege": [ { "begriff": "Führen", … } ] }

oder unmittelbar als Liste::

    [ { "begriff": "Führen", … } ]

Aufruf
------
    python data-pipeline/glossar_zusammenfuehren.py <datei-oder-verzeichnis> …

Anschließend prüfen – ohne bestandene Prüfung wird nicht ausgeliefert::

    python data-pipeline/pruefe_glossar.py
"""

from __future__ import annotations

import argparse
import io
import json
import locale
import sys
from datetime import date
from pathlib import Path

WURZEL = Path(__file__).resolve().parent.parent
GESETZE = WURZEL / 'content' / 'gesetze' / 'index.json'
ZIEL = WURZEL / 'content' / 'glossar.json'

HINWEIS = (
    'Eigener redaktioneller Inhalt. Wo das Gesetz einen Begriff selbst bestimmt, '
    'gibt der Eintrag diese Bestimmung wieder; Gesetzestexte sind nach § 5 Abs. 1 '
    'UrhG gemeinfrei. Zusammenstellung und Auswahl sind nicht Teil des amtlichen '
    'Fragenkatalogs. Jede Fundstelle wird von data-pipeline/pruefe_glossar.py '
    'gegen den amtlichen Gesetzestext geprueft.'
)

#: Felder einer Fundstelle, die als Zeichenkette vorliegen müssen.
FUNDSTELLENFELDER = ('gesetz', 'norm', 'absatz', 'nummer', 'buchstabe', 'satz', 'stelle')


def teilstuecke_einlesen(orte: list[str]) -> list[dict]:
    dateien: list[Path] = []
    for ort in orte:
        pfad = Path(ort)
        if pfad.is_dir():
            dateien.extend(sorted(pfad.glob('*.json')))
        elif pfad.is_file():
            dateien.append(pfad)
        else:
            raise SystemExit(f'Nicht gefunden: {ort}')

    gesammelt: list[dict] = []
    for datei in dateien:
        if datei.resolve() == ZIEL.resolve():
            continue
        roh = json.load(io.open(datei, encoding='utf-8'))
        eintraege = roh.get('eintraege') if isinstance(roh, dict) else roh
        if not isinstance(eintraege, list):
            raise SystemExit(f'{datei}: erwartet wird eine Liste von Eintraegen.')
        gesammelt.extend(eintraege)
        print(f'  {datei.name}: {len(eintraege)} Eintraege')

    return gesammelt


def normalisieren(eintraege: list[dict]) -> int:
    """Macht aus Zahlen Zeichenketten – wie bei den Erklaerungen.

    Der Lader in der Anwendung prueft den Typ. `"absatz": 2` ist eine
    naheliegende Verwechslung, inhaltlich harmlos und wuerde dort dennoch die
    ganze Datei verwerfen.
    """
    geaendert = 0
    for eintrag in eintraege:
        for fundstelle in eintrag.get('fundstellen') or []:
            if not isinstance(fundstelle, dict):
                continue
            for feld in FUNDSTELLENFELDER:
                wert = fundstelle.get(feld)
                if isinstance(wert, bool):
                    continue
                if isinstance(wert, (int, float)):
                    fundstelle[feld] = str(wert)
                    geaendert += 1
    return geaendert


def sortierschluessel(begriff: str) -> str:
    """Deutsche Sortierung: Umlaute stehen bei ihren Grundbuchstaben."""
    ersatz = {'ä': 'a', 'ö': 'o', 'ü': 'u', 'ß': 'ss', 'Ä': 'A', 'Ö': 'O', 'Ü': 'U'}
    return ''.join(ersatz.get(z, z) for z in begriff).casefold()


def main() -> int:
    zerleger = argparse.ArgumentParser(description='Glossar zusammenfuehren.')
    zerleger.add_argument('quellen', nargs='+', help='Dateien oder Verzeichnisse')
    argumente = zerleger.parse_args()

    print('Teilstuecke:')
    eintraege = teilstuecke_einlesen(argumente.quellen)

    geaendert = normalisieren(eintraege)
    if geaendert:
        print(f'  {geaendert} Zahlangaben zu Zeichenketten gemacht.')

    # Doppelte Begriffe zusammenfuehren waere geraten; hier faellt es auf.
    gesehen: dict[str, int] = {}
    for eintrag in eintraege:
        begriff = eintrag.get('begriff')
        if isinstance(begriff, str):
            gesehen[begriff] = gesehen.get(begriff, 0) + 1
    doppelt = sorted(b for b, n in gesehen.items() if n > 1)
    if doppelt:
        print(f'\nFEHLER: mehrfach vorhanden: {", ".join(doppelt)}')
        return 1

    eintraege.sort(key=lambda e: sortierschluessel(str(e.get('begriff', ''))))

    gesetze = json.load(io.open(GESETZE, encoding='utf-8'))['gesetze']
    zitiert = {
        f.get('gesetz')
        for e in eintraege
        for f in (e.get('fundstellen') or [])
        if isinstance(f, dict)
    }

    ergebnis = {
        'meta': {
            'version': 1,
            'stand': date.today().isoformat(),
            'hinweis': HINWEIS,
            'gesetzesstand': {k: gesetze[k]['stand'] for k in gesetze if k in zitiert},
        },
        'eintraege': eintraege,
    }

    with io.open(ZIEL, 'w', encoding='utf-8', newline='\n') as datei:
        json.dump(ergebnis, datei, ensure_ascii=False, indent=1)
        datei.write('\n')

    begriffe = sum(1 for e in eintraege if e.get('art') == 'begriff')
    kuerzel = sum(1 for e in eintraege if e.get('art') == 'abkuerzung')
    print(f'\n{len(eintraege)} Eintraege ({begriffe} Begriffe, {kuerzel} Abkuerzungen)')
    print(f'geschrieben: {ZIEL.name}')
    print('\nJetzt pruefen mit: python data-pipeline/pruefe_glossar.py')
    return 0


if __name__ == '__main__':
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')  # type: ignore[union-attr]
    raise SystemExit(main())
