"""
Beispiel für die Verwendung der Low-Level API (QuestraDataCore).

Zeigt die wichtigsten Funktionen:
- Authentifizierung mit Service Account über QuestraAuthentication
- Inventories abfragen
- Namespaces erstellen
- Inventories erstellen mit Dataclasses (typsicher)
- Permissions verwalten mit Enums (typsicher)
- Dynamische Inventory-Operationen (Insert, Query, Update, Delete)
- Schema-Validierung
"""

from __future__ import annotations

from questra_authentication import QuestraAuthentication

from questra_data import (
    BoolProperty,
    ConflictAction,
    IntProperty,
    QuestraDataCore,
    StringProperty,
)


def main():
    """Hauptfunktion mit Beispielen."""

    # 1. QuestraAuthentication für Authentifizierung erstellen
    auth_client = QuestraAuthentication(
        url="https://authentik.dev.example.com",
        username="ServiceUser",
        password="secret",
    )

    # 2. QuestraDataCore mit QuestraAuthentication initialisieren
    client = QuestraDataCore(
        graphql_url="https://dev.example.com/dynamic-objects/graphql/",
        auth_client=auth_client,
    )

    print(f"Client initialisiert: {client}")
    print(f"Authentifiziert: {client.is_authenticated()}\n")

    # 2. System-Informationen abrufen
    print("=== System-Informationen ===")
    system_info = client.queries.get_system_info()
    print(f"Dyno Version: {system_info.dynamic_objects_version}")
    print(f"Datenbank Version: {system_info.database_version}")
    print(f"Speicher frei: {system_info.memory_info.free_mb} MB\n")

    # 3. Namespaces abfragen
    print("=== Namespaces abfragen ===")
    namespaces = client.queries.get_namespaces()
    for namespace in namespaces:
        print(f"  - {namespace.name}: {namespace.description or 'Keine Beschreibung'}")
    print(f"Anzahl Namespaces: {len(namespaces)}\n")

    # 4. Inventories abfragen mit Filter
    print("=== Inventories abfragen ===")
    inventories = client.queries.get_inventories(
        where={"inventoryNames": ["TestUser"]}, order={"by": "NAME"}
    )
    for inventory in inventories:
        print(f"  - {inventory.name} ({inventory.inventory_type.value})")
        print(f"    Namespace: {inventory.namespace_name}")
        print(f"    Audit: {inventory.audit_enabled}")
        if inventory.properties:
            print(f"    Properties: {len(inventory.properties)}")
    print()

    # 5. Namespace erstellen
    print("=== Namespace erstellen ===")
    try:
        result = client.mutations.create_namespace(
            namespace_name="TestNamespace",
            description="Ein Test-Namespace",
            if_exists=ConflictAction.IGNORE,
        )
        print(
            f"Namespace erstellt: {result.name}, existierte bereits: {result.existed}\n"
        )
    except Exception as e:
        print(f"Fehler beim Erstellen: {e}\n")

    # 6. Inventory erstellen (mit spezialisierten Property-Klassen - empfohlen)
    print("=== Inventory erstellen (mit Property-Klassen) ===")
    try:
        properties = [
            StringProperty(
                propertyName="Name", maxLength=255, isRequired=True, isUnique=False
            ),
            StringProperty(
                propertyName="Email", maxLength=255, isRequired=True, isUnique=True
            ),
            IntProperty(propertyName="Age", isRequired=False),
            BoolProperty(propertyName="IsActive", isRequired=True),
        ]

        result = client.mutations.create_inventory(
            inventory_name="TestUser",
            properties=properties,
            namespace_name="TestNamespace",
            description="Test-Inventory für Benutzer",
            enable_audit=True,
            if_exists=ConflictAction.IGNORE,
        )
        print(
            f"Inventory erstellt: {result.name}, existierte bereits: {result.existed}\n"
        )
    except Exception as e:
        print(f"Fehler beim Erstellen: {e}\n")

    # 7. Role erstellen
    print("=== Role erstellen ===")
    try:
        result = client.mutations.create_role(
            role_name="TestRole",
            description="Eine Test-Rolle",
            if_exists=ConflictAction.IGNORE,
        )
        print(f"Role erstellt: {result.name}, existierte bereits: {result.existed}\n")
    except Exception as e:
        print(f"Fehler beim Erstellen: {e}\n")

    # 8. Permissions gewähren (mit Enums - empfohlen)
    print("=== Permissions gewähren (mit Enums) ===")
    try:
        from questra_data import InventoryPrivilege

        client.mutations.grant_inventory_permissions(
            inventory_name="TestUser",
            role_name="TestRole",
            privileges=[
                InventoryPrivilege.SELECT,
                InventoryPrivilege.INSERT,
                InventoryPrivilege.UPDATE,
            ],
            namespace_name="TestNamespace",
        )
        print("Permissions erfolgreich gewährt\n")
        print(
            "(Hinweis: Strings für Privileges werden weiterhin unterstützt, z.B. ['SELECT', 'INSERT'])\n"
        )
    except Exception as e:
        print(f"Fehler beim Gewähren: {e}\n")

    # 9. Inventory ändern mit neuer Property (Background Job, mit Property-Klasse)
    print("=== Inventory ändern (mit Property-Klasse) ===")
    try:
        new_property = StringProperty(
            propertyName="PhoneNumber", maxLength=50, isRequired=False, isUnique=False
        )

        result = client.mutations.start_alter_inventory(
            inventory_name="TestUser",
            namespace_name="TestNamespace",
            add_properties=[new_property],
            description="Aktualisierte Beschreibung",
        )
        print(f"Background Job ID: {result.background_job_id}\n")
    except Exception as e:
        print(f"Fehler beim Ändern: {e}\n")

    # 10. Rohe GraphQL-Query ausführen
    print("=== Rohe GraphQL-Query ===")
    try:
        result = client.execute_raw("""
            query {
                _timeZones {
                    name
                    baseUtcOffset
                }
            }
        """)
        print(f"Anzahl Zeitzonen: {len(result['_timeZones'])}")
        print(f"Erste Zeitzone: {result['_timeZones'][0]['name']}\n")
    except Exception as e:
        print(f"Fehler bei roher Query: {e}\n")

    # 11. Items in Inventory erstellen (mit Schema-Validierung)
    print("=== Items in TestUser erstellen ===")
    try:
        items = client.inventory.create(
            inventory_name="TestUser",
            namespace_name="TestNamespace",
            items=[
                {
                    "Name": "John Doe",
                    "Email": "john.doe@example.com",
                    "Age": 30,
                    "IsActive": True,
                },
                {
                    "Name": "Jane Smith",
                    "Email": "jane.smith@example.com",
                    "Age": 28,
                    "IsActive": True,
                },
            ],
        )
        print(f"Items eingefügt: {len(items)}")
        for item in items:
            status = "bereits vorhanden" if item["_existed"] else "neu"
            print(f"  - ID {item['_id']}: {status}")
        print()
    except ValueError as e:
        print(f"Validierungsfehler: {e}\n")
    except Exception as e:
        print(f"Fehler beim Einfügen: {e}\n")

    # 12. Items aus Inventory abfragen
    print("=== Items aus TestUser abfragen ===")
    try:
        result = client.inventory.list(
            inventory_name="TestUser",
            namespace_name="TestNamespace",
            properties=["_id", "_rowVersion", "Name", "Email", "Age", "IsActive"],
            first=10,
        )
        print(f"Anzahl Items: {len(result['nodes'])}")
        for item in result["nodes"]:
            print(
                f"  - [{item['_id']}] {item['Name']}: {item['Email']}, "
                f"Age={item.get('Age', 'N/A')}, Active={item['IsActive']}"
            )
        print(f"Weitere Seiten: {result['pageInfo']['hasNextPage']}\n")
    except Exception as e:
        print(f"Fehler beim Abfragen: {e}\n")

    # 13. Item aktualisieren
    print("=== Item in TestUser aktualisieren ===")
    try:
        # Zuerst ein Item abrufen
        result = client.inventory.list(
            inventory_name="TestUser",
            namespace_name="TestNamespace",
            properties=["_id", "_rowVersion", "Name"],
            first=1,
        )

        if result["nodes"]:
            item = result["nodes"][0]
            print(f"Aktualisiere: {item['Name']}")

            updated = client.inventory.update(
                inventory_name="TestUser",
                namespace_name="TestNamespace",
                items=[
                    {"_id": item["_id"], "_rowVersion": item["_rowVersion"], "Age": 31}
                ],
            )
            print(f"Aktualisiert: ID {updated[0]['_id']}\n")
        else:
            print("Keine Items zum Aktualisieren gefunden\n")
    except ValueError as e:
        print(f"Validierungsfehler: {e}\n")
    except Exception as e:
        print(f"Fehler beim Aktualisieren: {e}\n")

    # 14. Item löschen
    print("=== Item aus TestUser löschen ===")
    try:
        # Zuerst ein Item abrufen
        result = client.inventory.list(
            inventory_name="TestUser",
            namespace_name="TestNamespace",
            properties=["_id", "_rowVersion"],
            first=1,
        )

        if result["nodes"]:
            item = result["nodes"][0]
            deleted = client.inventory.delete(
                inventory_name="TestUser",
                namespace_name="TestNamespace",
                items=[{"_id": item["_id"], "_rowVersion": item["_rowVersion"]}],
            )
            status = "gelöscht" if deleted[0]["_existed"] else "nicht gefunden"
            print(f"Item {deleted[0]['_id']}: {status}\n")
        else:
            print("Keine Items zum Löschen gefunden\n")
    except ValueError as e:
        print(f"Validierungsfehler: {e}\n")
    except Exception as e:
        print(f"Fehler beim Löschen: {e}\n")


def interactive_example():
    """Beispiel für interaktiven Device Code Flow."""

    # QuestraAuthentication mit interaktivem Modus erstellen
    auth_client = QuestraAuthentication(
        url="https://authentik.dev.example.com",
        interactive=True,
        oidc_discovery_paths=["/application/o/dyno"],
    )
    # Der Benutzer wird aufgefordert, sich über einen Browser zu authentifizieren
    # Die URL wird in der Konsole angezeigt

    # QuestraDataCore mit authentifiziertem QuestraAuthentication erstellen
    client = QuestraDataCore(
        graphql_url="https://dyno.dev.example.com/graphql", auth_client=auth_client
    )

    # Nach erfolgreicher Authentifizierung können Operationen ausgeführt werden
    inventories = client.queries.get_inventories()
    for inventory in inventories:
        print(f"Inventory: {inventory.name}")


if __name__ == "__main__":
    print("Questra Data - Beispiel für Verwendung\n")
    print("=" * 50)
    print()

    # Hauptbeispiele ausführen
    main()

    # Für interaktives Beispiel auskommentieren:
    # interactive_example()
