#!/usr/bin/env python3
"""
import_workgroups_kelvin.py – Bulk-Import von UCS@school-Arbeitsgruppen
über die UCS@school Kelvin REST API (offizieller, unterstützter Weg).

Voraussetzungen:
    - UCS@school Kelvin REST API App ist installiert (DC Master oder Backup)
    - Python-Paket "kelvin-rest-api-client" ist installiert:
        pip install kelvin-rest-api-client --break-system-packages
    - Ein Kelvin-fähiger Account (z.B. Administrator) mit Passwort

CSV-FORMAT: Tab-getrennt (\t), UTF-8, mit Kopfzeile:
    ou<TAB>name<TAB>description<TAB>members

"members" ist optional: eine mit Semikolon (;) getrennte Liste von
UCS@school-BENUTZERNAMEN (keine DNs! - anders als beim UDM-Weg).

Beispielzeile (Tabs hier als Leerzeichen dargestellt):
    Heisenberg   AG-Kunst   Arbeitsgruppe Kunst   t.test;m.mueller

AUFRUF:
    export KELVIN_HOST="$(hostname -f)"
    export KELVIN_USER="Administrator"
    ./import_workgroups_kelvin.py workgroups.csv              # Dry-Run
    ./import_workgroups_kelvin.py workgroups.csv --apply       # tatsächlich anlegen

Das Passwort wird interaktiv abgefragt (oder über die Umgebungsvariable
KELVIN_PASSWORD gesetzt, z.B. in einer CI-Pipeline).
"""

import argparse
import asyncio
import csv
import getpass
import logging
import os
import sys

from ucsschool.kelvin.client import (
    KelvinClientError,
    NoObject,
    Session,
    WorkGroup,
    WorkGroupResource,
)


def read_rows(csv_file: str):
    """Liest die Tab-getrennte CSV-Datei ein und liefert bereinigte Zeilen."""
    with open(csv_file, newline="", encoding="utf-8-sig") as f:
        reader = csv.DictReader(f, delimiter="\t")
        for line_no, row in enumerate(reader, start=2):  # Zeile 1 = Header
            ou = (row.get("ou") or "").strip()
            name = (row.get("name") or "").strip()
            description = (row.get("description") or "").strip()
            members_raw = (row.get("members") or "").strip()

            if not ou or not name:
                print(f"WARNUNG Zeile {line_no}: 'ou' oder 'name' fehlt, uebersprungen.", file=sys.stderr)
                continue

            members = [m.strip() for m in members_raw.split(";") if m.strip()]
            yield line_no, ou, name, description, members


async def import_workgroups(csv_file: str, apply: bool, session: Session, create_share: bool = True) -> None:
    wg_resource = WorkGroupResource(session=session)

    created, skipped, failed = 0, 0, 0

    for line_no, ou, name, description, members in read_rows(csv_file):
        label = f"Zeile {line_no}: '{name}' (Schule: {ou})"

        try:
            exists = await wg_resource.exists(name=name, school=ou)
        except KelvinClientError as exc:
            print(f"FEHLER bei Existenzpruefung {label}: {exc}", file=sys.stderr)
            failed += 1
            continue

        if exists:
            print(f"{label} - existiert bereits, uebersprungen.")
            skipped += 1
            continue

        print(f"{label} - wird angelegt (Mitglieder: {members or '-'})")

        if not apply:
            continue

        wg = WorkGroup(
            name=name,
            school=ou,
            description=description or None,
            users=members,  # leere Liste statt None - Kelvin-Client kann kein None iterieren
            create_share=create_share,
            session=session,
        )

        try:
            await wg.save()
            created += 1
        except KelvinClientError as exc:
            print(f"FEHLER beim Anlegen {label}: {exc}", file=sys.stderr)
            failed += 1

    print()
    print("=== Fertig ===")
    if apply:
        print(f"Angelegt: {created}, uebersprungen (existierten bereits): {skipped}, Fehler: {failed}")
    else:
        print(f"Dry-Run: {skipped} existieren bereits, Rest wuerde mit --apply angelegt.")
        print("Zum tatsaechlichen Anlegen erneut mit --apply aufrufen.")


async def main_async(args: argparse.Namespace) -> None:
    # Die Kelvin-Client-Bibliothek loggt bei jedem exists()-Aufruf eine harmlose
    # WARNING-Zeile, weil sie zuerst HEAD probiert (vom Server mit 405 abgelehnt,
    # da nur GET/POST/PATCH/DELETE geroutet sind) und automatisch auf GET
    # zurueckfaellt. Funktional unproblematisch, aber optisch stoerend - daher
    # hier auf ERROR angehoben, damit nur echte Probleme durchkommen.
    logging.getLogger("ucsschool.kelvin.client").setLevel(logging.ERROR)

    host = args.host or os.environ.get("KELVIN_HOST")
    username = args.username or os.environ.get("KELVIN_USER", "Administrator")
    password = os.environ.get("KELVIN_PASSWORD") or getpass.getpass(
        f"Kelvin-Passwort fuer {username}@{host}: "
    )

    if not host:
        print("Fehler: --host oder KELVIN_HOST muss gesetzt sein.", file=sys.stderr)
        sys.exit(1)

    async with Session(username, password, host, verify=args.verify) as session:
        await import_workgroups(args.csv_file, args.apply, session, create_share=not args.no_share)


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("csv_file", help="Tab-getrennte CSV-Datei mit Arbeitsgruppen")
    parser.add_argument("--apply", action="store_true", help="Aenderungen tatsaechlich ausfuehren (sonst Dry-Run)")
    parser.add_argument(
        "--no-share",
        action="store_true",
        help="Keine Samba-Freigabe automatisch anlegen (Workaround, falls das Schul-OU-Objekt "
             "serverseitig nicht ueber die UDM REST API geladen werden kann, z.B. wegen "
             "fehlerhafter Policy-Referenzen)",
    )
    parser.add_argument("--host", help="Kelvin-Host, z.B. master.ucs.local (Default: $KELVIN_HOST)")
    parser.add_argument("--username", help="Kelvin-Benutzer (Default: $KELVIN_USER oder 'Administrator')")
    parser.add_argument(
        "--verify",
        default=True,
        help="SSL-Verifizierung: True, False, oder Pfad zu einer CA-Datei (Default: True)",
    )
    args = parser.parse_args()

    if args.verify in ("false", "False"):
        args.verify = False

    print(f"=== UCS@school Kelvin Arbeitsgruppen-Import ===")
    print(f"CSV-Datei : {args.csv_file}")
    print(f"Modus     : {'APPLY - Gruppen werden angelegt' if args.apply else 'DRY-RUN - es wird nichts veraendert'}")
    print()

    asyncio.run(main_async(args))


if __name__ == "__main__":
    main()
