"""
Beispiel für die Verwendung der High-Level API von Questra Data.

Die High-Level API abstrahiert die Komplexität von GraphQL und REST
und bietet eine benutzerfreundliche Schnittstelle für häufige Operationen.

Der Benutzer muss nicht wissen, ob intern GraphQL oder REST verwendet wird.
"""

from __future__ import annotations

import os
from datetime import datetime, timedelta

from questra_authentication import QuestraAuthentication

from questra_data import Quality, QuestraData, TimeSeriesValue


def main():
    """Hauptfunktion mit High-Level API Beispielen."""

    # ===== Client initialisieren =====
    print("=" * 60)
    print("High-Level API - Client initialisieren")
    print("=" * 60)

    # 1. QuestraAuthentication erstellen
    auth_client = QuestraAuthentication(
        url="https://authentik.dev.techstack.s2o.dev",
        username=os.getenv("DYNO_USERNAME", "ServiceUser"),
        password=os.getenv("DYNO_PASSWORD", "secret"),
    )

    # 2. QuestraData mit QuestraAuthentication initialisieren
    client = QuestraData(
        graphql_url="https://dev.techstack.s2o.dev/dynamic-objects-v2/graphql",
        auth_client=auth_client,
    )

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

    # ===== System-Informationen =====
    print("=" * 60)
    print("System-Informationen abrufen")
    print("=" * 60)

    info = client.get_system_info()
    print(f"Dyno Version: {info.dynamic_objects_version}")
    print(f"Datenbank: {info.database_version}")
    print()

    # ===== Namespaces und Inventories auflisten =====
    print("=" * 60)
    print("Namespaces und Inventories auflisten")
    print("=" * 60)

    namespaces = client.list_namespaces()
    print(f"Anzahl Namespaces: {len(namespaces)}")
    for ns in namespaces[:5]:  # Nur erste 5
        print(f"  - {ns.name}: {ns.description or 'Keine Beschreibung'}")
    print()

    inventories = client.list_inventories(namespace_name="TestNamespace")
    print(f"Inventories in 'TestNamespace': {len(inventories)}")
    for inv in inventories[:5]:  # Nur erste 5
        print(f"  - {inv.name} ({inv.inventory_type.value})")
    print()

    # Roles auflisten
    roles = client.list_roles()
    print(f"Anzahl Roles: {len(roles)}")
    for role in roles[:5]:  # Nur erste 5
        print(f"  - {role.name}: {role.description or 'Keine Beschreibung'}")
    print()

    # Units auflisten
    units = client.list_units()
    print(f"Anzahl Units: {len(units)}")
    for unit in units[:10]:  # Nur erste 10
        print(f"  - {unit.symbol}: {unit.aggregation}")
    print()

    # Zeitzonen auflisten
    time_zones = client.list_time_zones()
    print(f"Anzahl Zeitzonen: {len(time_zones)}")
    for tz in time_zones[:5]:  # Nur erste 5
        print(f"  - {tz.name}: UTC{tz.base_utc_offset}")
    print()

    # ===== Namespace und Inventory erstellen =====
    print("=" * 60)
    print("Namespace und Inventory erstellen")
    print("=" * 60)

    # Namespace erstellen (wird ignoriert wenn schon existiert)
    ns_result = client.create_namespace(
        name="Energie", description="Namespace für Energie-Management"
    )
    print(
        f"Namespace erstellt: {ns_result.name}, existierte bereits: {ns_result.existed}"
    )

    # Inventory für Stromzähler erstellen (mit spezialisierten Property-Klassen - empfohlen)
    from questra_data import StringProperty, TimeSeriesProperty, TimeUnit

    properties = [
        StringProperty(
            propertyName="stromzaehlernummer",
            maxLength=50,
            isRequired=True,
            isUnique=True,
        ),
        StringProperty(propertyName="hersteller", maxLength=100, isRequired=False),
        TimeSeriesProperty(
            propertyName="messwerte_Energie",
            timeUnit=TimeUnit.HOUR,
            multiplier=1,
            unit="kWh",
            isRequired=False,
        ),
    ]

    inv_result = client.create_inventory(
        name="Stromzaehler",
        namespace_name="Energie",
        properties=properties,
        description="Inventory für Stromzähler mit Messwerten",
    )
    print(
        f"Inventory erstellt: {inv_result.name}, existierte bereits: {inv_result.existed}"
    )

    # Beispiel: Inventory mit Relationen erstellen
    # Erst Gebäude-Inventory erstellen
    from questra_data import InventoryRelation, RelationType

    building_properties = [
        StringProperty(
            propertyName="gebaeudename", maxLength=100, isRequired=True, isUnique=True
        )
    ]

    building_result = client.create_inventory(
        name="Gebaeude",
        namespace_name="Energie",
        properties=building_properties,
        description="Gebäude-Inventory",
    )
    print(
        f"Gebäude-Inventory erstellt: {building_result.name}, existierte bereits: {building_result.existed}"
    )

    # Dann Raum-Inventory mit Relation zu Gebäude erstellen
    room_properties = [
        StringProperty(propertyName="raumnummer", maxLength=50, isRequired=True)
    ]

    relations = [
        InventoryRelation(
            propertyName="Gebaeude",
            relationType=RelationType.ONE_TO_MANY,
            parentInventoryName="Gebaeude",
            parentPropertyName="Raeume",
        )
    ]

    room_result = client.create_inventory(
        name="Raeume",
        namespace_name="Energie",
        properties=room_properties,
        relations=relations,
        description="Räume-Inventory mit Gebäude-Relation",
    )
    print(
        f"Raum-Inventory mit Relation erstellt: {room_result.name}, existierte bereits: {room_result.existed}"
    )
    print()

    # ===== Inventory löschen =====
    print("=" * 60)
    print("Inventory löschen")
    print("=" * 60)

    # Erstelle Test-Inventory zum Löschen
    test_props = [
        StringProperty(propertyName="testfeld", maxLength=50, isRequired=False)
    ]

    test_inv_result = client.create_inventory(
        name="TestInventoryToDelete",
        namespace_name="Energie",
        properties=test_props,
        description="Test-Inventory zum Löschen",
    )
    print(f"Test-Inventory erstellt: {test_inv_result.name}")

    # Inventory löschen
    delete_result = client.delete_inventory(
        inventory_name="TestInventoryToDelete", namespace_name="Energie"
    )
    print(
        f"Inventory gelöscht: {delete_result.name}, existierte: {delete_result.existed}"
    )
    print()

    # ===== Inventory Items laden und speichern =====
    print("=" * 60)
    print("Inventory Items laden und speichern")
    print("=" * 60)

    # Items erstellen
    # WICHTIG: Bei Properties vom DataType.TIMESERIES wird automatisch eine Zeitreihe angelegt!
    # Dazu muss die Property im Item als None angegeben werden
    items = [
        {
            "stromzaehlernummer": "SZ-12345",
            "hersteller": "Siemens",
            "messwerte_Energie": None,  # Zeitreihe wird automatisch angelegt
        },
        {
            "stromzaehlernummer": "SZ-67890",
            "hersteller": "ABB",
            "messwerte_Energie": None,  # Zeitreihe wird automatisch angelegt
        },
    ]

    created_items = client.create_items(
        inventory_name="Stromzaehler", namespace_name="Energie", items=items
    )
    print(
        f"{len(created_items)} Stromzähler erstellt (mit automatischer TimeSeries-Erstellung)"
    )
    for item in created_items:
        status = "bereits vorhanden" if item.get("_existed") else "neu"
        print(f"  - ID: {item['_id']}, {status}")
    print()

    # Items auflisten
    loaded_items = client.list_items(
        inventory_name="Stromzaehler",
        namespace_name="Energie",
        properties=["_id", "_rowVersion", "stromzaehlernummer", "hersteller"],
        limit=10,
    )
    print(f"{len(loaded_items)} Stromzähler aufgelistet:")
    for item in loaded_items:
        print(f"  - {item['stromzaehlernummer']} ({item['hersteller']})")
    print()

    # Mit Filter auflisten
    filtered_items = client.list_items(
        inventory_name="Stromzaehler",
        namespace_name="Energie",
        properties=["_id", "stromzaehlernummer", "hersteller"],
        where={"stromzaehlernummer": {"eq": "SZ-12345"}},
        limit=10,
    )
    print(f"Gefilterte Stromzähler: {len(filtered_items)}")
    for item in filtered_items:
        print(f"  - {item['stromzaehlernummer']} ({item['hersteller']})")
    print()

    # ===== Zeitreihen-Werte laden (HIGH-LEVEL!) =====
    print("=" * 60)
    print("Zeitreihen-Werte laden (High-Level)")
    print("=" * 60)

    # Dies ist der große Vorteil: Ein einziger Aufruf!
    # Der Benutzer muss nicht:
    # 1. Items mit TimeSeries-Property laden
    # 2. TimeSeries-IDs extrahieren
    # 3. REST-Call für Zeitreihen-Daten machen
    # Das macht alles die High-Level API automatisch!

    try:
        result = client.list_timeseries_values(
            inventory_name="Stromzaehler",
            namespace_name="Energie",
            timeseries_properties="messwerte_Energie",
            from_time=datetime(2025, 1, 1),
            to_time=datetime(2025, 12, 31),
        )

        print(f"Zeitreihen-Werte für {len(result)} Stromzähler aufgelistet:")
        for item_id, data in result.items():
            item = data["item"]
            values = data["values"]
            print(f"\n  Stromzähler: {item.get('stromzaehlernummer', 'N/A')}")
            print(f"  TimeSeries ID: {data['timeseries_id']}")
            print(f"  Anzahl Werte: {len(values)}")
            if values:
                print(f"  Erster Wert: {values[0].time} = {values[0].value}")
                print(f"  Letzter Wert: {values[-1].time} = {values[-1].value}")

    except Exception as e:
        print(f"Hinweis: {e}")
        print(
            "(Keine Zeitreihen-Daten vorhanden - das ist normal wenn noch keine Daten eingefügt wurden)"
        )
    print()

    # ===== Zeitreihen-Werte speichern (HIGH-LEVEL!) =====
    print("=" * 60)
    print("Zeitreihen-Werte speichern (High-Level)")
    print("=" * 60)

    # Erstelle Beispielwerte
    try:
        # Zuerst Items auflisten um IDs zu bekommen
        items = client.list_items(
            inventory_name="Stromzaehler",
            namespace_name="Energie",
            properties=["_id", "stromzaehlernummer"],
            limit=2,
        )

        if items:
            # Erstelle Zeitreihen-Werte für die ersten 2 Stromzähler
            now = datetime.now()
            item_values = {}

            for i, item in enumerate(items[:2]):
                values = [
                    TimeSeriesValue(
                        time=now - timedelta(hours=j),
                        value=100.0 + i * 10 + j * 0.5,
                        quality=Quality.VALID,
                    )
                    for j in range(5)
                ]
                item_values[item["_id"]] = values

            # Ein einziger Aufruf zum Speichern!
            # Der Benutzer muss nicht:
            # 1. Items mit TimeSeries-Property laden
            # 2. TimeSeries-IDs extrahieren
            # 3. SetTimeSeriesDataInput erstellen
            # 4. REST-Call machen
            # Das macht alles die High-Level API automatisch!

            from questra_data import TimeUnit

            # Option 1: Bulk-Methode für mehrere Items
            client.save_timeseries_values_bulk(
                inventory_name="Stromzaehler",
                namespace_name="Energie",
                timeseries_properties="messwerte_Energie",
                item_values=item_values,
                time_unit=TimeUnit.HOUR,
                multiplier=1,
                unit="kWh",
            )

            print(
                f"Zeitreihen-Werte für {len(item_values)} Stromzähler gespeichert (Bulk)"
            )
            for item_id, values in item_values.items():
                print(f"  - Item ID {item_id}: {len(values)} Werte")

            # Option 2: Einfache Methode für ein einzelnes Item
            # (Für den häufigsten Use-Case)
            first_item_id = list(item_values.keys())[0]
            first_values = item_values[first_item_id]

            client.save_timeseries_values(
                inventory_name="Stromzaehler",
                namespace_name="Energie",
                timeseries_property="messwerte_Energie",
                item_id=first_item_id,
                values=first_values,
                time_unit=TimeUnit.HOUR,
                multiplier=1,
                unit="kWh",
            )

            print("\nZeitreihen-Werte für einen Stromzähler gespeichert (Einfach)")
            print(f"  - Item ID {first_item_id}: {len(first_values)} Werte")

    except Exception as e:
        print(f"Hinweis: {e}")
        print(
            "(Fehler beim Speichern - möglicherweise müssen TimeSeries erst erstellt werden)"
        )
    print()

    # ===== Low-Level API Zugriff (für fortgeschrittene Operationen) =====
    print("=" * 60)
    print("Low-Level API Zugriff")
    print("=" * 60)

    # Für spezielle Operationen kann der Low-Level Client verwendet werden
    result = client.lowlevel.execute_raw("""
        query {
            _timeZones(first: 5) {
                name
                baseUtcOffset
            }
        }
    """)

    print("Erste 5 Zeitzonen (via Low-Level API):")
    for tz in result["_timeZones"]:
        print(f"  - {tz['name']}: {tz['baseUtcOffset']}")
    print()

    # ===== Pandas DataFrame Beispiele (Optional) =====
    try:
        import pandas as pd

        PANDAS_AVAILABLE = True
    except ImportError:
        PANDAS_AVAILABLE = False

    if PANDAS_AVAILABLE:
        print("=" * 60)
        print("Pandas DataFrame Integration")
        print("=" * 60)

        # Zeitreihen als DataFrame
        print("Zeitreihen-DataFrame:")
        ts_result = client.list_timeseries_values(
            inventory_name="Stromzaehler",
            namespace_name="Energie",
            timeseries_properties="messwerte_Energie",
            from_time=datetime.now() - timedelta(days=7),
            to_time=datetime.now(),
            properties=["stromzaehlernummer"],
            limit=5,
        )
        df_timeseries = ts_result.to_df(properties=["stromzaehlernummer"])
        print(df_timeseries.head())
        print()

        # Inventory Items als DataFrame
        print("Inventory Items als DataFrame:")
        items = client.list_items(
            inventory_name="Stromzaehler",
            namespace_name="Energie",
            properties=["_id", "_rowVersion", "stromzaehlernummer", "hersteller"],
            limit=10,
        )
        df_items = items.to_df()

        print(f"DataFrame mit {len(df_items)} Items:")
        if not df_items.empty:
            print(df_items)
        print()

        # Inventories als DataFrame
        print("Inventories als DataFrame:")
        inventories = client.list_inventories(namespace_name="Energie")
        df_inventories = inventories.to_df()

        print(f"DataFrame mit {len(df_inventories)} Inventories:")
        if not df_inventories.empty:
            print(df_inventories[["name", "namespace_name", "inventory_type"]].head())
        print()

        # Roles als DataFrame
        print("Roles als DataFrame:")
        roles = client.list_roles()
        df_roles = roles.to_df()

        print(f"DataFrame mit {len(df_roles)} Roles:")
        if not df_roles.empty:
            print(df_roles[["name", "description", "is_system"]].head(10))

            # System vs. Custom Roles
            print(f"\nSystem-Roles: {len(df_roles[df_roles['is_system'] == True])}")
            print(f"Custom-Roles: {len(df_roles[df_roles['is_system'] == False])}")
        print()

        # Units als DataFrame
        print("Units als DataFrame:")
        units = client.list_units()
        df_units = units.to_df()

        print(f"DataFrame mit {len(df_units)} Units:")
        if not df_units.empty:
            print(df_units.head(15))

            # Aggregationsarten gruppieren
            print("\nUnits nach Aggregationsart:")
            print(df_units.groupby("aggregation").size())
        print()

        # Zeitzonen als DataFrame
        print("Zeitzonen als DataFrame:")
        time_zones = client.list_time_zones()
        df_tz = time_zones.to_df()

        print(f"DataFrame mit {len(df_tz)} Zeitzonen:")
        if not df_tz.empty:
            print(df_tz.head(10))

            # Zeitzonen mit Sommerzeit
            print(
                f"\nZeitzonen mit Sommerzeit: {len(df_tz[df_tz['supports_daylight_saving_time'] == True])}"
            )

            # Europäische Zeitzonen
            europe_tz = df_tz[df_tz["name"].str.contains("Europe")]
            print(f"Europäische Zeitzonen: {len(europe_tz)}")
            if not europe_tz.empty:
                print(europe_tz.head())
        print()

    else:
        print("=" * 60)
        print("Pandas nicht installiert")
        print("=" * 60)
        print("Für DataFrame-Unterstützung installieren mit:")
        print("  pip install questra-data[pandas]")
        print()

    print("=" * 60)
    print("High-Level API Beispiele abgeschlossen!")
    print("=" * 60)


def simple_example():
    """
    Sehr einfaches Beispiel, das den Hauptvorteil der High-Level API zeigt.
    """
    print("\n\n")
    print("=" * 60)
    print("EINFACHES BEISPIEL - Vorher vs. Nachher")
    print("=" * 60)

    # Initialisierung
    auth_client = QuestraAuthentication(
        url="https://authentik.dev.techstack.s2o.dev",
        username=os.getenv("DYNO_USERNAME", "ServiceUser"),
        password=os.getenv("DYNO_PASSWORD", "secret"),
    )

    client = QuestraData(
        graphql_url="https://dev.techstack.s2o.dev/dynamic-objects-v2/graphql",
        auth_client=auth_client,
    )

    print("\n>>> VORHER (Low-Level API - 3 Schritte) <<<\n")
    print("""
    # Schritt 1: Items mit TimeSeries-Property laden
    result = client.inventory.list(
        inventory_name="Stromzaehler",
        namespace_name="Energie",
        properties=["_id", "messwerte_Energie.id"]
    )

    # Schritt 2: TimeSeries-IDs extrahieren
    timeseries_ids = [
        item["messwerte_Energie"]["id"]
        for item in result["nodes"]
        if item.get("messwerte_Energie")
    ]

    # Schritt 3: Zeitreihen-Daten laden
    data = client.timeseries.get_data(
        time_series_ids=timeseries_ids,
        from_time=datetime(2025, 1, 1),
        to_time=datetime(2025, 12, 31)
    )
    """)

    print("\n>>> NACHHER (High-Level API - 1 Schritt!) <<<\n")
    print("""
    # Nur ein einziger Aufruf!
    result = client.list_timeseries_values(
        inventory_name="Stromzaehler",
        namespace_name="Energie",
        timeseries_property="messwerte_Energie",
        from_time=datetime(2025, 1, 1),
        to_time=datetime(2025, 12, 31)
    )
    """)

    print("\n>>> ERGEBNIS <<<\n")
    print("✓ Der Benutzer muss nicht verstehen, wie GraphQL und REST zusammenspielen")
    print(
        "✓ Der Benutzer muss nicht wissen, dass TimeSeries über Properties referenziert werden"
    )
    print(
        "✓ Der Benutzer bekommt ein strukturiertes Dictionary mit allen relevanten Daten"
    )
    print("✓ Der Code ist viel kürzer und lesbarer")
    print()


if __name__ == "__main__":
    print("Questra Data - High-Level API Beispiele\n")

    # Hauptbeispiele
    main()

    # Einfaches Vergleichsbeispiel
    simple_example()
