"""
High-Level API für Questra Data.

Bietet eine vereinfachte Schnittstelle für häufige Operationen,
ohne dass der Benutzer die internen Details (GraphQL vs REST) kennen muss.
"""

from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING, Any

from loguru import logger
from questra_authentication import QuestraAuthentication

from .client import QuestraDataCore
from .managers import CatalogManager
from .models import (
    Aggregation,
    ConflictAction,
    DataType,
    Inventory,
    NamedItemResult,
    Namespace,
    Quality,
    QuotationBehavior,
    Role,
    SystemInfo,
    TimeUnit,
    TimeZone,
    Unit,
)
from .models.inputs import (
    BoolProperty,
    DateProperty,
    DateTimeOffsetProperty,
    DateTimeProperty,
    DecimalProperty,
    FileProperty,
    GuidProperty,
    IntProperty,
    InventoryProperty,
    InventoryRelation,
    LongProperty,
    StringProperty,
    TimeProperty,
    TimeSeriesProperty,
)
from .models.rest import SetTimeSeriesDataInput, TimeSeriesValue
from .results import QueryResult, TimeSeriesResult

# Optionale pandas Integration
if TYPE_CHECKING:
    import pandas as pd

try:
    import pandas as pd

    _PANDAS_AVAILABLE = True
except ImportError:
    _PANDAS_AVAILABLE = False


class QuestraData:
    """
    High-Level Client für vereinfachte API-Nutzung.

    Abstrahiert die Komplexität von GraphQL und REST und bietet
    eine benutzerfreundliche API für häufige Operationen.

    Examples:
        ```python
        from questra_authentication import QuestraAuthentication
        from questra_data import QuestraData

        auth_client = QuestraAuthentication(
            url="https://authentik.dev.example.com",
            username="ServiceUser",
            password="secret",
        )

        client = QuestraData(
            graphql_url="https://dev.example.com/dynamic-objects/graphql/",
            auth_client=auth_client,
        )

        # Zeitreihen-Werte auflisten
        values = client.list_timeseries_values(
            inventory_name="Stromzaehler",
            namespace_name="Energie",
            timeseries_properties="messwerte_Energie",
            from_time=datetime(2024, 1, 1),
            to_time=datetime(2024, 1, 31),
        )
        ```
    """

    def __init__(
        self,
        graphql_url: str,
        auth_client: QuestraAuthentication,
        rest_base_url: str | None = None,
    ):
        """
        Initialisiert den High-Level Client.

        Args:
            graphql_url: URL des GraphQL-Endpunkts
                (z.B. "https://dev.example.com/graphql/")
            auth_client: Konfigurierter und authentifizierter QuestraAuthentication
            rest_base_url: Basis-URL für REST-API (optional, wird aus graphql_url
                abgeleitet wenn nicht angegeben)

        Raises:
            ValueError: Wenn auth_client nicht authentifiziert ist

        Examples:
            ```python
            from questra_authentication import QuestraAuthentication
            from questra_data import QuestraData

            auth_client = QuestraAuthentication(
                url="https://authentik.dev.example.com",
                username="ServiceUser",
                password="secret",
            )

            client = QuestraData(
                graphql_url="https://dev.example.com/graphql/", auth_client=auth_client
            )
            ```
        """
        logger.info("Initializing QuestraData", graphql_url=graphql_url)

        # Erstelle QuestraDataCore Client (Low-Level API)
        self._client = QuestraDataCore(
            graphql_url=graphql_url,
            auth_client=auth_client,
            rest_base_url=rest_base_url,
        )

        # Initialisiere interne Manager (für Code-Organisation)
        self._catalog_manager = CatalogManager(self._client)

        logger.info("QuestraData initialized successfully")

    def _normalize_properties(
        self,
        properties: list[
            InventoryProperty
            | StringProperty
            | IntProperty
            | LongProperty
            | DecimalProperty
            | BoolProperty
            | DateTimeProperty
            | DateTimeOffsetProperty
            | DateProperty
            | TimeProperty
            | GuidProperty
            | FileProperty
            | TimeSeriesProperty
        ],
    ) -> list[InventoryProperty]:
        """
        Konvertiert spezialisierte Property-Klassen zu InventoryProperty.

        Args:
            properties: Liste von Property-Objekten (InventoryProperty oder spezialisierte Klassen)

        Returns:
            Liste von InventoryProperty-Objekten
        """
        normalized: list[InventoryProperty] = []
        for prop in properties:
            if isinstance(prop, InventoryProperty):
                normalized.append(prop)
            else:
                # Spezialisierte Property-Klassen haben alle to_inventory_property()
                normalized.append(prop.to_inventory_property())  # type: ignore[union-attr]
        return normalized

    # ===== Zeitreihen-Operationen =====

    def list_timeseries_values(
        self,
        inventory_name: str,
        timeseries_properties: str | list[str],
        from_time: datetime,
        to_time: datetime,
        namespace_name: str | None = None,
        properties: list[str] | None = None,
        where: dict[str, Any] | None = None,
        aggregation: Aggregation | None = None,
        time_unit: TimeUnit | None = None,
        multiplier: int | None = None,
        exclude_qualities: list[Quality] | None = None,
        time_zone: str | None = None,
    ) -> TimeSeriesResult:
        """
        Listet Zeitreihen-Werte für Items aus einem Inventory auf.

        Diese Methode kombiniert automatisch:
        1. Abfrage der Inventory Items mit TimeSeries-properties
        2. Extraktion der TimeSeries-IDs
        3. Laden der Zeitreihen-Werte inkl. Einheiten

        Args:
            inventory_name: Name des Inventorys (z.B. "Stromzaehler")
            namespace_name: Name des Namespaces (optional)
            timeseries_properties: Name des TimeSeries-properties oder Liste von Field-Namen
            from_time: Start des Zeitbereichs
            to_time: Ende des Zeitbereichs
            properties: Liste normaler properties die zusätzlich abgefragt werden (optional)
            where: Filter für Inventory Items (optional). Beispiele:
                {"Name": {"eq": "John"}}, {"Age": {"gt": 18}},
                {"and": [{"Name": {"eq": "John"}}, {"Age": {"gt": 18}}]}
            aggregation: Aggregationsart (optional)
            time_unit: Zeiteinheit für Intervall-Aggregation (optional)
            multiplier: Multiplikator für Intervall (optional, Standard: 1)
            exclude_qualities: Liste von Quality-Werten die ausgeschlossen werden sollen.
                Standard (None): [Quality.MISSING] wird ausgeschlossen
                Beispiele:
                [Quality.MISSING, Quality.FAULTY] - schließt fehlende und fehlerhafte Werte aus
                [] - keine Qualitätsfilterung (alle Werte inkl. MISSING)
            time_zone: Zeitzone für Ausgabe (optional)

        Returns:
            TimeSeriesResult: Dictionary-ähnliches Objekt mit Item-ID als Schlüssel und verschachtelter Struktur.

                ```python
                {
                    item_id: {
                        "item": {...},
                        "timeseries": {
                            "property_name": {
                                "timeseries_id": int,
                                "values": [TimeSeriesValue, ...],
                                "unit": str | None,
                                "interval": Interval,
                                "time_zone": str | None,
                            }
                        },
                    }
                }
                ```

                Hinweis: Struktur ist IMMER gleich, auch bei nur einem Field!

        Examples:
            ```python
            # Direkte Nutzung wie Dictionary
            result = client.list_timeseries_values(
                inventory_name="Stromzaehler",
                timeseries_properties="messwerte_Energie",  # oder ["messwerte_Energie"]
                from_time=datetime(2024, 1, 1),
                to_time=datetime(2024, 1, 31),
                namespace_name="Energie",
                properties=["stromzaehlernummer", "standort"],  # Normale properties
            )

            # Struktur ist immer gleich!
            for item_id, data in result.items():
                item = data["item"]
                print(f"Stromzähler {item['stromzaehlernummer']} ({item['standort']}):")
                for property_name, field_data in data["timeseries"].items():
                    print(f"  {property_name} [{field_data['unit']}]:")
                    for value in field_data["values"]:
                        print(f"    {value.time}: {value.value}")

            # Optional: DataFrame-Konvertierung
            df = result.to_df()
            df = result.to_df(properties=["stromzaehlernummer", "standort"])

            # Mehrere Timeseries-properties - gleiche Struktur:
            result = client.list_timeseries_values(
                inventory_name="Sensoren",
                timeseries_properties=[
                    "messwerte_temperatur",
                    "messwerte_luftfeuchtigkeit",
                ],
                from_time=datetime(2024, 1, 1),
                to_time=datetime(2024, 1, 31),
                namespace_name="Gebäude",
                properties=["sensornummer", "raum"],  # Normale properties
            )

            for item_id, data in result.items():
                item = data["item"]
                print(f"Sensor {item['sensornummer']} in Raum {item['raum']}:")
                for property_name, field_data in data["timeseries"].items():
                    print(f"  {property_name} [{field_data['unit']}]:")
                    for value in field_data["values"]:
                        print(f"    {value.time}: {value.value}")
            ```
        """
        # Normalisiere zu Liste
        ts_properties = (
            [timeseries_properties]
            if isinstance(timeseries_properties, str)
            else timeseries_properties
        )

        logger.info(
            "Listing timeseries values from inventory",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            timeseries_properties=ts_properties,
            properties=properties,
        )

        # Schritt 1: Lade Inventory Items mit TimeSeries-properties und normalen properties
        query_properties = ["_id", "_rowVersion"]

        # Sammle TimeSeries-Subfelder (z.B. "messwerte_temperatur.timeZone")
        ts_subfields = {ts_prop: set() for ts_prop in ts_properties}

        # Füge normale properties hinzu und sammle TimeSeries-Subfelder
        if properties:
            for prop in properties:
                # Prüfe ob es eine verschachtelte TimeSeries-Property ist
                is_ts_subfield = False
                for ts_prop in ts_properties:
                    if prop.startswith(f"{ts_prop}."):
                        # TimeSeries-Subfeld gefunden (z.B. "messwerte_temperatur.timeZone")
                        subfield = prop.split(".", 1)[1]
                        ts_subfields[ts_prop].add(subfield)
                        is_ts_subfield = True
                        break

                if not is_ts_subfield:
                    # Normale Property
                    query_properties.append(prop)

        # Füge TimeSeries-Properties mit ihren Subfeldern hinzu
        for ts_prop in ts_properties:
            # Immer .id abfragen (für Zeitreihen-Daten)
            ts_subfields[ts_prop].add("id")

            # Füge alle Subfelder als verschachtelte Properties hinzu
            for subfield in ts_subfields[ts_prop]:
                query_properties.append(f"{ts_prop}.{subfield}")

        result = self._client.inventory.list(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            properties=query_properties,
            where=where,
            first=1000,  # TODO: Pagination implementieren falls mehr Items
        )

        items = result.get("nodes", [])
        logger.debug(f"Found {len(items)} items with TimeSeries properties")

        if not items:
            logger.warning(f"No items found in {inventory_name}")
            return {}

        # Schritt 2: Extrahiere TimeSeries-IDs und erstelle Mapping
        # Format: ts_id -> (item_id, property_name)
        ts_to_item_mapping = {}
        timeseries_ids = []

        for item in items:
            item_id = item["_id"]
            for field in ts_properties:
                ts_data = item.get(field)
                if ts_data and isinstance(ts_data, dict) and "id" in ts_data:
                    ts_id = ts_data["id"]
                    ts_to_item_mapping[ts_id] = {
                        "item_id": item_id,
                        "property_name": field,
                    }
                    timeseries_ids.append(ts_id)

        if not timeseries_ids:
            logger.warning("No TimeSeries IDs found in items")
            return {}

        logger.debug(f"Extracted {len(timeseries_ids)} TimeSeries IDs")

        # Schritt 3: Lade Zeitreihen-Werte
        # Default: nur MISSING ausschließen
        if exclude_qualities is None:
            exclude_qualities = [Quality.MISSING]

        ts_result = self._client.timeseries.get_data(
            time_series_ids=timeseries_ids,
            from_time=from_time,
            to_time=to_time,
            time_unit=time_unit,
            multiplier=multiplier if time_unit else None,
            aggregation=aggregation,
            time_zone=time_zone,
            exclude_qualities=exclude_qualities,
        )

        # Schritt 4: Kombiniere Ergebnisse
        # Einheitliche Struktur: IMMER mit "timeseries" dict
        item_index = {item["_id"]: item for item in items}
        result_dict = {}

        for ts_data in ts_result.data:
            ts_id = ts_data.time_series_id
            if ts_id not in ts_to_item_mapping:
                continue

            mapping = ts_to_item_mapping[ts_id]
            item_id = mapping["item_id"]
            property_name = mapping["property_name"]

            # Initialisiere Item-Eintrag falls nötig
            if item_id not in result_dict:
                result_dict[item_id] = {
                    "item": item_index[item_id],
                    "timeseries": {},
                }

            # Speichere TimeSeries-Daten IMMER unter timeseries[property_name]
            result_dict[item_id]["timeseries"][property_name] = {
                "timeseries_id": ts_id,
                "values": ts_data.values,
                "unit": ts_data.unit,
                "interval": ts_data.interval,
                "time_zone": ts_data.time_zone,
            }

        logger.info(f"Listed timeseries values for {len(result_dict)} items")
        return TimeSeriesResult(result_dict, self)

    def _convert_timeseries_to_dataframe(
        self,
        result: dict[str, dict],
        include_metadata: bool = True,
        properties: list[str] | None = None,
    ) -> pd.DataFrame:
        """
        Konvertiert Zeitreihen-Result zu pandas DataFrame im Pivot-Format.

        Erstellt immer ein Pivot-Format mit MultiIndex-Spalten:
        - Erste Ebene: Timeseries-Field-Name
        - Zweite Ebene: Einheit (wird zusammengefasst wenn alle Timeseries-properties gleiche Unit haben)
        - Weitere Ebenen: Normale properties (wenn angegeben)
        - Letzte Ebene: Item-ID (zur Unterscheidung bei mehreren Items)
        - Jede Zeile repräsentiert einen Zeitpunkt
        - Jede Spalte repräsentiert eine Zeitreihe (Timeseries-Field + normale properties + Item-Kombination)

        Args:
            result: Ergebnis von list_timeseries_values()
            include_metadata: Item-Metadaten als Spalten einbeziehen (wird ignoriert im Pivot-Format)
            properties: Liste normaler properties die als Spalten-Ebenen hinzugefügt werden

        Returns:
            pd.DataFrame: DataFrame mit time als Index und MultiIndex-Spalten:

                - Wenn properties=None: (Timeseries-Field [Unit], Item-ID) oder (Timeseries-Field, Unit, Item-ID)
                - Wenn properties angegeben: (Timeseries-Field [Unit], field1, field2, ..., Item-ID) oder
                  (Timeseries-Field, Unit, field1, field2, ..., Item-ID)
        """
        if not _PANDAS_AVAILABLE:
            raise ImportError("pandas is required for DataFrame conversion")

        # Sammle alle Daten und Item-Informationen
        # {(property, unit, item_id): {time: value}}
        data_dict = {}
        # {item_id: {property_name: field_value}}
        item_properties_dict = {}

        def get_nested_value(d: dict, path: str):
            """Extrahiert Wert aus verschachteltem Dict via Punkt-Notation."""
            keys = path.split(".")
            value = d
            for key in keys:
                if isinstance(value, dict):
                    value = value.get(key)
                else:
                    return None
            return value

        for item_id, data in result.items():
            # Sammle normale properties für dieses Item
            if properties:
                item_properties_dict[item_id] = {}
                for field in properties:
                    # Unterstütze verschachtelte Properties (z.B. "messwerte_temperatur.timeZone")
                    item_properties_dict[item_id][field] = get_nested_value(
                        data["item"], field
                    )

            # Einheitliche Struktur: IMMER data["timeseries"]
            for property_name, ts_data in data["timeseries"].items():
                values = ts_data["values"]
                unit = ts_data.get("unit") or ""

                key = (property_name, unit, item_id)
                data_dict[key] = {
                    ts_value.time: float(ts_value.value) for ts_value in values
                }

        if not data_dict:
            # Leerer DataFrame
            return pd.DataFrame()  # type: ignore

        # Erstelle DataFrame aus dict
        # Sammle Spalten als Liste von Tuples (property_name, unit, item_id)
        columns_list = []
        series_list = []

        for (property_name, unit, item_id), time_values in data_dict.items():
            columns_list.append((property_name, unit, item_id))
            series_list.append(pd.Series(time_values))  # type: ignore

        if not columns_list:
            return pd.DataFrame()  # type: ignore

        # Erstelle DataFrame mit MultiIndex-Spalten
        df = pd.DataFrame(dict(enumerate(series_list)))  # type: ignore
        df.index.name = "time"
        df = df.sort_index()

        # Prüfe ob alle Units pro Timeseries-Field gleich sind
        # Wenn ja, können wir die Unit-Ebene vereinfachen
        field_units = {}
        for ts_field, unit, item in columns_list:
            if ts_field not in field_units:
                field_units[ts_field] = set()
            field_units[ts_field].add(unit)

        # Wenn alle Timeseries-properties nur eine einzige Unit haben, vereinfache
        all_single_unit = all(len(units) == 1 for units in field_units.values())

        # Erstelle MultiIndex mit normalen properties als Spalten-Ebenen
        if properties and item_properties_dict:
            # Erweitere columns_list um field-Werte
            # Struktur: (ts_field, unit, field1_value, field2_value, ..., item_id)
            extended_columns = []
            for ts_field, unit, item_id in columns_list:
                # Hole field-Werte für dieses Item
                field_values = []
                if item_id in item_properties_dict:
                    for field in properties:
                        field_values.append(item_properties_dict[item_id].get(field))
                else:
                    field_values = [None] * len(properties)

                # Baue Spalten-Tuple
                if all_single_unit:
                    # (ts_field [unit], field1, field2, ..., item_id)
                    ts_field_with_unit = f"{ts_field} [{unit}]" if unit else ts_field
                    extended_columns.append(
                        (ts_field_with_unit, *field_values, item_id)
                    )
                else:
                    # (ts_field, unit, field1, field2, ..., item_id)
                    extended_columns.append((ts_field, unit, *field_values, item_id))

            # Erstelle Namen für die MultiIndex-Ebenen
            if all_single_unit:
                column_names = ["Timeseries-Field [Unit]"] + properties + ["Item-ID"]
            else:
                column_names = ["Timeseries-Field", "Unit"] + properties + ["Item-ID"]

            df.columns = pd.MultiIndex.from_tuples(  # type: ignore
                extended_columns, names=column_names
            )
        else:
            # Keine normalen properties: wie vorher
            if all_single_unit:
                # Vereinfachter MultiIndex: Timeseries-Field und Item-ID
                # Unit wird in der ersten Ebene mit Field zusammengefasst
                simplified_columns = []
                for ts_field, unit, item in columns_list:
                    ts_field_with_unit = f"{ts_field} [{unit}]" if unit else ts_field
                    simplified_columns.append((ts_field_with_unit, item))

                df.columns = pd.MultiIndex.from_tuples(  # type: ignore
                    simplified_columns, names=["Timeseries-Field [Unit]", "Item-ID"]
                )
            else:
                # Voller MultiIndex: Timeseries-Field, Unit, Item-ID
                df.columns = pd.MultiIndex.from_tuples(  # type: ignore
                    columns_list, names=["Timeseries-Field", "Unit", "Item-ID"]
                )

        return df

    def save_timeseries_values(
        self,
        inventory_name: str,
        timeseries_property: str,
        item_id: str,
        values: list[TimeSeriesValue],
        namespace_name: str | None = None,
        time_unit: TimeUnit | None = None,
        multiplier: int = 1,
        unit: str | None = None,
        time_zone: str | None = None,
    ) -> None:
        """
        Speichert Zeitreihen-Werte für EINE Zeitreihe (1 Item, 1 Property).

        Diese vereinfachte Methode ist für den häufigsten Use-Case optimiert:
        Speichern von Werten für ein einzelnes Item und eine einzelne TimeSeries-Property.

        Für Bulk-Operationen (mehrere Items/Properties) siehe `save_timeseries_values_bulk`.

        Args:
            inventory_name: Name des Inventorys
            timeseries_property: Name des TimeSeries-Properties
            item_id: ID des Items
            values: Liste von TimeSeriesValue-Objekten
            namespace_name: Name des Namespaces (optional)
            time_unit: Zeiteinheit für Intervall (optional)
            multiplier: Multiplikator für Intervall (Standard: 1)
            unit: Einheit der Werte (optional)
            time_zone: Zeitzone (optional)

        Examples:
            ```python
            from datetime import datetime
            from questra_data import TimeSeriesValue

            # Einfaches Beispiel: Temperaturwerte für einen Sensor
            client.save_timeseries_values(
                inventory_name="Sensoren",
                timeseries_property="messwerte_temperatur",
                item_id="638301262349418496",
                values=[
                    TimeSeriesValue(time=datetime(2024, 6, 20, 12, 0), value=22.5),
                    TimeSeriesValue(time=datetime(2024, 6, 21, 12, 0), value=23.0),
                    TimeSeriesValue(time=datetime(2024, 6, 22, 12, 0), value=21.8),
                ],
            )

            # Mit Intervall und Einheit
            client.save_timeseries_values(
                inventory_name="Stromzaehler",
                timeseries_property="messwerte_Energie",
                item_id="123456",
                values=[TimeSeriesValue(time=datetime(2024, 6, 20, 0, 0), value=100.5)],
                time_unit=TimeUnit.HOUR,
                multiplier=1,
                unit="kWh",
            )
            ```
        """
        logger.info(
            "Saving timeseries values for single item",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            timeseries_property=timeseries_property,
            item_id=item_id,
            value_count=len(values),
        )

        # Delegiere an Bulk-Methode
        self.save_timeseries_values_bulk(
            inventory_name=inventory_name,
            timeseries_properties=timeseries_property,
            item_values={item_id: values},
            namespace_name=namespace_name,
            time_unit=time_unit,
            multiplier=multiplier,
            unit=unit,
            time_zone=time_zone,
        )

    def save_timeseries_values_bulk(
        self,
        inventory_name: str,
        timeseries_properties: str | list[str],
        item_values: dict[str, list[TimeSeriesValue]]
        | dict[str, dict[str, list[TimeSeriesValue]]],
        namespace_name: str | None = None,
        time_unit: TimeUnit | None = None,
        multiplier: int = 1,
        unit: str | None = None,
        time_zone: str | None = None,
    ) -> None:
        """
        Speichert Zeitreihen-Werte für MEHRERE Items und/oder Properties (Bulk-Operation).

        Diese Methode kombiniert automatisch:
        1. Abfrage der Inventory Items mit TimeSeries-properties
        2. Extraktion der TimeSeries-IDs
        3. Speichern der Zeitreihen-Werte

        Für einfache Fälle (1 Item, 1 Property) siehe `save_timeseries_values`.

        Args:
            inventory_name: Name des Inventorys
            namespace_name: Name des Namespaces (optional)
            timeseries_properties: Name des TimeSeries-properties oder Liste von Field-Namen
            item_values: Dictionary mit Item-ID als Schlüssel und:
                - Bei einzelnem Field: Liste von TimeSeriesValue
                - Bei mehreren properties: Dictionary {property_name: [TimeSeriesValue, ...]}
            time_unit: Zeiteinheit für Intervall (optional)
            multiplier: Multiplikator für Intervall (Standard: 1)
            unit: Einheit der Werte (optional)
            time_zone: Zeitzone (optional)

        Raises:
            ValueError: Wenn die Struktur von item_values nicht zum Modus passt:
                - Multi-Field-Modus (timeseries_properties ist Liste): Erwartet dict[str, dict[str, list[TimeSeriesValue]]]
                - Single-Field-Modus (timeseries_properties ist String): Erwartet dict[str, list[TimeSeriesValue]]

        Examples:
            ```python
            # Mehrere Items, ein Property:
            item_values = {
                "638301262349418496": [
                    TimeSeriesValue(time=datetime(2024, 6, 20, 12, 0), value=22.5),
                    TimeSeriesValue(time=datetime(2024, 6, 21, 12, 0), value=23.0),
                ],
                "638301262349418497": [
                    TimeSeriesValue(time=datetime(2024, 6, 20, 12, 0), value=21.7),
                    TimeSeriesValue(time=datetime(2024, 6, 21, 12, 0), value=22.1),
                ],
            }
            client.save_timeseries_values_bulk(
                inventory_name="Stromzaehler",
                timeseries_properties="messwerte_Energie",  # ← String!
                item_values=item_values
            )

            # Mehrere Items, mehrere Properties:
            ```python
            item_values = {
                "638301262349418496": {
                    "messwerte_temperatur": [
                        TimeSeriesValue(time=datetime(2024, 6, 20, 12, 0), value=22.5),
                    ],
                    "messwerte_druck": [
                        TimeSeriesValue(time=datetime(2024, 6, 20, 12, 0), value=1013.25),
                    ],
                },
                "638301262349418497": {
                    "messwerte_temperatur": [
                        TimeSeriesValue(time=datetime(2024, 6, 20, 12, 0), value=21.7),
                    ],
                    "messwerte_druck": [
                        TimeSeriesValue(time=datetime(2024, 6, 20, 12, 0), value=1012.5),
                    ],
                },
            }
            client.save_timeseries_values_bulk(
                inventory_name="Sensoren",
                timeseries_properties=["messwerte_temperatur", "messwerte_druck"],  # ← Liste!
                item_values=item_values
            )
            ```
        """
        # Normalisiere timeseries_properties zu Liste
        ts_properties = (
            [timeseries_properties]
            if isinstance(timeseries_properties, str)
            else timeseries_properties
        )

        logger.info(
            "Saving timeseries values for inventory items",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            item_count=len(item_values),
            timeseries_properties=ts_properties,
        )

        if not item_values:
            logger.warning("No item values provided")
            return

        # Schritt 1: Lade Inventory Items mit TimeSeries-properties
        # Hinweis: _id verwendet lowercase Filter (eq, in) statt _eq, _in
        item_ids = list(item_values.keys())
        where = {"_id": {"in": item_ids}}

        # Erstelle properties Liste mit allen Timeseries-properties
        properties = ["_id"]
        for field in ts_properties:
            properties.append(f"{field}.id")

        result = self._client.inventory.list(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            properties=properties,
            where=where,
            first=len(item_ids),
        )

        items = result.get("nodes", [])
        logger.debug(f"Found {len(items)} items")

        # Schritt 2: Erstelle SetTimeSeriesDataInput für alle Timeseries-properties
        from .models import Interval

        data_inputs = []

        # Prüfe ob wir mit mehreren Timeseries-properties arbeiten
        is_multi_field = len(ts_properties) > 1

        for item in items:
            item_id = item["_id"]
            item_value_data = item_values.get(item_id)

            if not item_value_data:
                continue

            # Verarbeite jedes Timeseries-Field
            for field in ts_properties:
                ts_data = item.get(field)

                if not ts_data or not isinstance(ts_data, dict) or "id" not in ts_data:
                    logger.warning(
                        f"Item {item_id} has no valid TimeSeries field '{field}'"
                    )
                    continue

                ts_id = ts_data["id"]

                # Hole Werte basierend auf Modus (single vs. multi field)
                if is_multi_field:
                    # Multi-Field-Modus: item_value_data ist Dict[str, list[TimeSeriesValue]]
                    if not isinstance(item_value_data, dict):
                        expected_fields = ", ".join(
                            f'"{p}": [...]' for p in ts_properties
                        )
                        raise ValueError(
                            f"Item {item_id}: Multi-field mode requires dict structure.\n"
                            f"Expected: {{{expected_fields}}}\n"
                            f"Got {type(item_value_data).__name__}: {item_value_data!r}"
                        )
                    values = item_value_data.get(field, [])
                else:
                    # Single-Field-Modus: item_value_data ist list[TimeSeriesValue]
                    if isinstance(item_value_data, dict):
                        hint_props = ", ".join(f'"{k}"' for k in item_value_data.keys())
                        raise ValueError(
                            f"Item {item_id}: Single-field mode requires list structure.\n"
                            f"Expected: [TimeSeriesValue(...), ...]\n"
                            f"Got dict with keys: {list(item_value_data.keys())}\n"
                            f"Hint: For multiple properties, pass them as list: "
                            f"timeseries_properties=[{hint_props}]"
                        )
                    values = item_value_data

                if values:
                    data_input = SetTimeSeriesDataInput(
                        time_series_id=ts_id,
                        values=values,
                        interval=Interval(time_unit=time_unit, multiplier=multiplier)
                        if time_unit
                        else None,
                        unit=unit,
                        time_zone=time_zone,
                    )
                    data_inputs.append(data_input)

        # Schritt 3: Speichere Werte via REST API
        if data_inputs:
            self._client.timeseries.set_data(data_inputs)
            total_values = sum(len(di.values) for di in data_inputs)
            logger.info(
                f"Saved {total_values} values for {len(data_inputs)} TimeSeries"
            )
        else:
            logger.warning("No valid data inputs created")

    # ===== Quotation-Operationen =====

    def list_quotation_timestamps(
        self,
        inventory_name: str,
        timeseries_property: str,
        from_time: datetime,
        to_time: datetime,
        namespace_name: str | None = None,
        where: dict[str, Any] | None = None,
        aggregated: bool = False,
    ) -> dict[str, list[datetime]] | list[datetime]:
        """
        Listet verfügbare Notierungs-Zeitstempel für Zeitreihen auf.

        Diese Methode ermöglicht Discovery: Welche Forecast-Versionen, Budget-Revisionen
        oder Preis-Notierungen existieren für einen Zeitraum?

        Args:
            inventory_name: Name des Inventorys
            timeseries_property: Name des TimeSeries-Properties (muss enableQuotation=True haben)
            from_time: Start des Zeitbereichs für Datenpunkte
            to_time: Ende des Zeitbereichs für Datenpunkte
            namespace_name: Name des Namespaces (optional)
            where: Filter für Inventory Items (optional)
            aggregated: Bei True werden Quotations über alle Items aggregiert (Standard: False)

        Returns:
            - aggregated=False: dict[str, list[datetime]] - Mapping Item-ID (String) -> Notierungs-Zeitstempel
            - aggregated=True: list[datetime] - Eindeutige Notierungs-Zeitstempel über alle Items

        Examples:
            ```python
            # Szenario 1: Festes Raster (Stromhandel)
            # Welche Monatsprodukt-Notierungen existieren für Q1?
            quotations = client.list_quotation_timestamps(
                inventory_name="PowerProducts",
                timeseries_property="quarter_hourly_prices",
                from_time=datetime(2025, 1, 1),
                to_time=datetime(2025, 3, 31),
                namespace_name="Trading",
            )
            # Result: {item_id: [datetime(2025,1,1), datetime(2025,2,1), datetime(2025,3,1)]}

            # Szenario 2: Freies Raster (Prognosen)
            # Welche Forecast-Versionen wurden erstellt?
            quotations = client.list_quotation_timestamps(
                inventory_name="EnergyForecasts",
                timeseries_property="predicted_load",
                from_time=datetime(2025, 1, 10),
                to_time=datetime(2025, 1, 17),
                aggregated=True,  # Über alle Regionen hinweg
            )
            # Result: [datetime(2025,1,8,6,0), datetime(2025,1,9,6,0), datetime(2025,1,10,6,0)]
            ```
        """
        logger.info(
            "Listing quotation timestamps",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            timeseries_property=timeseries_property,
            aggregated=aggregated,
        )

        # Schritt 1: Lade Inventory Items mit TimeSeries-Property
        properties = ["_id", f"{timeseries_property}.id"]

        result = self._client.inventory.list(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            properties=properties,
            where=where,
            first=1000,  # TODO: Pagination implementieren falls mehr Items
        )

        items = result.get("nodes", [])
        logger.debug(f"Found {len(items)} items with TimeSeries property")

        if not items:
            logger.warning(f"No items found in {inventory_name}")
            return [] if aggregated else {}

        # Schritt 2: Extrahiere TimeSeries-IDs
        timeseries_ids = []
        ts_to_item_mapping = {}

        for item in items:
            item_id = item["_id"]
            ts_data = item.get(timeseries_property)
            if ts_data and isinstance(ts_data, dict) and "id" in ts_data:
                ts_id = ts_data["id"]
                timeseries_ids.append(ts_id)
                ts_to_item_mapping[ts_id] = item_id

        if not timeseries_ids:
            logger.warning("No TimeSeries IDs found in items")
            return [] if aggregated else {}

        logger.debug(f"Extracted {len(timeseries_ids)} TimeSeries IDs")

        # Schritt 3: Lade Notierungen
        quotations_result = self._client.timeseries.get_quotations(
            time_series_ids=timeseries_ids,
            from_time=from_time,
            to_time=to_time,
            aggregated=aggregated,
        )

        # Schritt 4: Verarbeite Ergebnisse
        if aggregated:
            # Aggregiert: Sammle alle eindeutigen Notierungs-Zeitstempel
            all_timestamps = set()
            for quotations in quotations_result.items:
                for quot_value in quotations.values:
                    all_timestamps.add(quot_value.time)

            result_list = sorted(list(all_timestamps))
            logger.info(
                f"Found {len(result_list)} unique quotation timestamps (aggregated)"
            )
            return result_list
        else:
            # Pro Item: Mapping Item-ID -> Notierungs-Zeitstempel
            item_quotations = {}
            for quotations in quotations_result.items:
                ts_id = quotations.time_series_id
                if ts_id in ts_to_item_mapping:
                    item_id = ts_to_item_mapping[ts_id]
                    timestamps = [quot_value.time for quot_value in quotations.values]
                    item_quotations[item_id] = sorted(timestamps)

            logger.info(f"Found quotations for {len(item_quotations)} items")
            return item_quotations

    def list_quotation_values(
        self,
        inventory_name: str,
        timeseries_property: str,
        from_time: datetime,
        to_time: datetime,
        quotation_time: datetime | None = None,
        quotation_behavior: QuotationBehavior = QuotationBehavior.LATEST,
        quotation_exactly_at: bool = False,
        namespace_name: str | None = None,
        where: dict[str, Any] | None = None,
        properties: list[str] | None = None,
        aggregation: Aggregation | None = None,
        time_unit: TimeUnit | None = None,
        multiplier: int | None = None,
        exclude_qualities: list[Quality] | None = None,
        time_zone: str | None = None,
    ) -> dict[str, dict]:
        """
        Liest Zeitreihen-Werte für eine spezifische Notierung.

        Args:
            inventory_name: Name des Inventorys
            timeseries_property: Name des TimeSeries-Properties
            from_time: Start des Zeitbereichs
            to_time: Ende des Zeitbereichs
            quotation_time: Spezifischer Notierungs-Zeitstempel (optional)
                - None: Verwendet quotation_behavior
            quotation_behavior: Verhalten bei fehlender quotation_time:
                - LATEST: Neueste Notierung im Zeitraum
                - LATEST_NO_FUTURE: Neueste Notierung <= jetzt
                - LATEST_EXACTLY_AT: Höchste Notierung genau im Zeitraum
            quotation_exactly_at: Bei quotation_time:
                - True: Nur exakt diese Notierung
                - False: Neueste Notierung <= quotation_time
            namespace_name: Name des Namespaces (optional)
            where: Filter für Inventory Items (optional)
            properties: Zusätzliche Item-Properties (optional)
            aggregation: Aggregationsart (optional)
            time_unit: Zeiteinheit für Intervall-Aggregation (optional)
            multiplier: Multiplikator für Intervall (optional)
            exclude_qualities: Liste von Quality-Werten zum Ausschließen (optional)
            time_zone: Zeitzone für Ausgabe (optional)

        Returns:
            dict[str, dict]: Struktur wie list_timeseries_values() erweitert um quotation_time:
                ```python
                {
                    item_id: {  # item_id ist String (LongNumberString)
                        "item": {...},
                        "quotation_time": datetime,  # Tatsächlich verwendete Notierung
                        "timeseries": {
                            property_name: {
                                "timeseries_id": int,
                                "values": [TimeSeriesValue, ...],
                                "unit": str | None,
                                "interval": Interval,
                                "time_zone": str | None,
                            }
                        },
                    }
                }
                ```

        Examples:
            ```python
            # Szenario 1: Festes Raster - Monatsprodukt Februar lesen
            data = client.list_quotation_values(
                inventory_name="PowerProducts",
                timeseries_property="quarter_hourly_prices",
                from_time=datetime(2025, 2, 1),
                to_time=datetime(2025, 2, 28, 23, 45),
                quotation_time=datetime(2025, 2, 1),  # Monatsprodukt-Notierung
                quotation_exactly_at=True,
                namespace_name="Trading",
            )

            # Szenario 2: Freies Raster - Neuester Forecast vor Ereignis
            data = client.list_quotation_values(
                inventory_name="EnergyForecasts",
                timeseries_property="predicted_load",
                from_time=datetime(2025, 1, 10),
                to_time=datetime(2025, 1, 17),
                quotation_time=datetime(2025, 1, 9, 18, 0),  # Forecast von 18 Uhr
                quotation_exactly_at=False,  # Falls 18 Uhr fehlt, nimm den davor
                namespace_name="Operations",
            )

            # Szenario 3: Immer neueste Notierung
            data = client.list_quotation_values(
                inventory_name="Budgets",
                timeseries_property="monthly_budget",
                from_time=datetime(2024, 1, 1),
                to_time=datetime(2024, 12, 31),
                quotation_behavior=QuotationBehavior.LATEST,  # Aktuelle Budget-Revision
            )
            ```
        """
        logger.info(
            "Getting quotation data",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            timeseries_property=timeseries_property,
            quotation_time=quotation_time,
            quotation_behavior=quotation_behavior,
        )

        # Schritt 1: Lade Inventory Items mit TimeSeries-Property
        query_properties = ["_id", "_rowVersion"]

        # Füge TimeSeries-Property hinzu
        query_properties.append(f"{timeseries_property}.id")

        # Füge zusätzliche Properties hinzu
        if properties:
            for prop in properties:
                if prop not in query_properties:
                    query_properties.append(prop)

        result = self._client.inventory.list(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            properties=query_properties,
            where=where,
            first=1000,  # TODO: Pagination implementieren falls mehr Items
        )

        items = result.get("nodes", [])
        logger.debug(f"Found {len(items)} items with TimeSeries property")

        if not items:
            logger.warning(f"No items found in {inventory_name}")
            return {}

        # Schritt 2: Extrahiere TimeSeries-IDs
        timeseries_ids = []
        ts_to_item_mapping = {}

        for item in items:
            item_id = item["_id"]
            ts_data = item.get(timeseries_property)
            if ts_data and isinstance(ts_data, dict) and "id" in ts_data:
                ts_id = ts_data["id"]
                ts_to_item_mapping[ts_id] = item_id
                timeseries_ids.append(ts_id)

        if not timeseries_ids:
            logger.warning("No TimeSeries IDs found in items")
            return {}

        logger.debug(f"Extracted {len(timeseries_ids)} TimeSeries IDs")

        # Schritt 3: Lade Zeitreihen-Werte mit Quotation
        # Default: nur MISSING ausschließen
        if exclude_qualities is None:
            exclude_qualities = [Quality.MISSING]

        ts_result = self._client.timeseries.get_data(
            time_series_ids=timeseries_ids,
            from_time=from_time,
            to_time=to_time,
            time_unit=time_unit,
            multiplier=multiplier if time_unit else None,
            aggregation=aggregation,
            time_zone=time_zone,
            quotation_time=quotation_time,
            quotation_exactly_at=quotation_exactly_at,
            quotation_behavior=quotation_behavior,
            exclude_qualities=exclude_qualities,
        )

        # Schritt 4: Kombiniere Ergebnisse
        item_index = {item["_id"]: item for item in items}
        result_dict = {}

        for ts_data in ts_result.data:
            ts_id = ts_data.time_series_id
            if ts_id not in ts_to_item_mapping:
                continue

            item_id = ts_to_item_mapping[ts_id]

            # Initialisiere Item-Eintrag falls nötig
            if item_id not in result_dict:
                result_dict[item_id] = {
                    "item": item_index[item_id],
                    "quotation_time": None,  # Wird bei Bedarf gesetzt
                    "timeseries": {},
                }

            # Speichere TimeSeries-Daten
            result_dict[item_id]["timeseries"][timeseries_property] = {
                "timeseries_id": ts_id,
                "values": ts_data.values,
                "unit": ts_data.unit,
                "interval": ts_data.interval,
                "time_zone": ts_data.time_zone,
            }

            # TODO: quotation_time aus Response extrahieren wenn verfügbar
            # Aktuell liefert die API die tatsächlich verwendete Quotation nicht zurück

        logger.info(f"Loaded quotation data for {len(result_dict)} items")
        return result_dict

    def save_quotation_values(
        self,
        inventory_name: str,
        timeseries_property: str,
        item_id: str,
        values: list[TimeSeriesValue],
        quotation_time: datetime,
        namespace_name: str | None = None,
        time_unit: TimeUnit | None = None,
        multiplier: int = 1,
        unit: str | None = None,
        time_zone: str | None = None,
    ) -> None:
        """
        Speichert Zeitreihen-Werte mit Notierungs-Zeitstempel.

        Notierungs-Zeitpunkt (quotation_time) ist PFLICHT - er repräsentiert den fachlichen Versions-Zeitstempel
        (wann wurde dieser Forecast erstellt, welche Budget-Revision, etc.).

        Args:
            inventory_name: Name des Inventorys
            timeseries_property: Name des TimeSeries-Properties (muss enableQuotation=True haben)
            item_id: ID des Items
            values: Liste von TimeSeriesValue-Objekten
            quotation_time: Notierungs-Zeitstempel (PFLICHT!)
                - Festes Raster: Monatsbeginn, Quartalsbeginn, etc.
                - Freies Raster: Forecast-Erstellungs-Zeitpunkt
            namespace_name: Name des Namespaces (optional)
            time_unit: Zeiteinheit für Intervall (optional)
            multiplier: Multiplikator für Intervall (Standard: 1)
            unit: Einheit der Werte (optional)
            time_zone: Zeitzone (optional)

        Raises:
            ValueError: Wenn TimeSeries nicht mit enableQuotation=True erstellt wurde

        Examples:
            ```python
            # Szenario 1: Festes Raster - Monatsprodukt-Notierung März speichern
            client.save_quotation_values(
                inventory_name="PowerProducts",
                timeseries_property="quarter_hourly_prices",
                item_id="638301262349418496",
                quotation_time=datetime(2025, 3, 1),  # Monatsprodukt-Notierung
                values=[
                    TimeSeriesValue(time=datetime(2025, 3, 1, 0, 0), value=45.20),
                    TimeSeriesValue(time=datetime(2025, 3, 1, 0, 15), value=44.80),
                    # ... alle 15-Minuten-Werte für März
                ],
                unit="EUR/MWh",
            )

            # Szenario 2: Freies Raster - Täglicher Forecast Update
            from datetime import datetime

            forecast_created_at = datetime.now()  # Jetzt!

            client.save_quotation_values(
                inventory_name="EnergyForecasts",
                timeseries_property="predicted_load",
                item_id="123",
                quotation_time=forecast_created_at,  # Forecast-Erstellungszeitpunkt
                values=[
                    TimeSeriesValue(time=datetime(2025, 1, 11, 0, 0), value=150.5),
                    TimeSeriesValue(time=datetime(2025, 1, 11, 1, 0), value=148.2),
                ],
                unit="MW",
            )
            ```
        """
        logger.info(
            "Saving quotation data for single item",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            timeseries_property=timeseries_property,
            item_id=item_id,
            quotation_time=quotation_time,
            value_count=len(values),
        )

        # Schritt 1: Lade Item mit TimeSeries-Property
        where = {"_id": {"eq": item_id}}
        properties = ["_id", f"{timeseries_property}.id"]

        result = self._client.inventory.list(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            properties=properties,
            where=where,
            first=1,
        )

        items = result.get("nodes", [])
        if not items:
            raise ValueError(f"Item with ID {item_id} not found in {inventory_name}")

        item = items[0]
        ts_data = item.get(timeseries_property)

        if not ts_data or not isinstance(ts_data, dict) or "id" not in ts_data:
            raise ValueError(
                f"Item {item_id} has no valid TimeSeries property '{timeseries_property}'"
            )

        ts_id = ts_data["id"]
        logger.debug(f"Found TimeSeries ID {ts_id} for item {item_id}")

        # Schritt 2: Erstelle SetTimeSeriesDataInput mit Notierung
        from .models import Interval

        data_input = SetTimeSeriesDataInput(
            time_series_id=ts_id,
            values=values,
            interval=Interval(time_unit=time_unit, multiplier=multiplier)
            if time_unit
            else None,
            unit=unit,
            time_zone=time_zone,
            quotation_time=quotation_time,  # ← Notierungs-Zeitstempel!
        )

        # Schritt 3: Speichere Werte via REST API
        self._client.timeseries.set_data([data_input])
        logger.info(
            f"Saved {len(values)} values with quotation_time={quotation_time} for TimeSeries {ts_id}"
        )

    def compare_quotations(
        self,
        inventory_name: str,
        timeseries_property: str,
        item_id: str,
        quotation_times: list[datetime],
        from_time: datetime,
        to_time: datetime,
        namespace_name: str | None = None,
        aggregation: Aggregation | None = None,
        time_unit: TimeUnit | None = None,
        multiplier: int | None = None,
    ) -> pd.DataFrame:
        """
        Vergleicht mehrere Notierungs-Versionen in einem DataFrame.

        Nützlich für:
        - Forecast-Accuracy-Analyse (wie hat sich der Forecast entwickelt?)
        - Budget-Revisions-Vergleich (ursprünglicher vs. finaler Plan)
        - Preis-Entwicklung über Zeit (verschiedene Notierungen)

        Note:
            Benötigt pandas: `pip install questra-data[pandas]`

        Args:
            inventory_name: Name des Inventorys
            timeseries_property: Name des TimeSeries-Properties
            item_id: ID des Items
            quotation_times: Liste von Notierungs-Zeitstempeln zum Vergleichen
            from_time: Start des Zeitbereichs
            to_time: Ende des Zeitbereichs
            namespace_name: Name des Namespaces (optional)
            aggregation: Aggregationsart (optional)
            time_unit: Zeiteinheit für Intervall-Aggregation (optional)
            multiplier: Multiplikator für Intervall (optional)

        Returns:
            pd.DataFrame: MultiIndex-Spalten mit Notierungs-Zeitstempeln:
                - Index: time (Zeitstempel der Datenpunkte)
                - Spalten: MultiIndex (quotation_time, metric)
                    - metric: "value", "quality"

        Raises:
            ImportError: Wenn pandas nicht installiert ist
            ValueError: Wenn keine Notierungs-Zeitstempel angegeben

        Examples:
            ```python
            # Szenario 1: Festes Raster - Vergleiche 3 Monatsprodukt-Notierungen
            df = client.compare_quotations(
                inventory_name="PowerProducts",
                timeseries_property="quarter_hourly_prices",
                item_id="638301262349418496",
                quotation_times=[
                    datetime(2025, 1, 1),
                    datetime(2025, 2, 1),
                    datetime(2025, 3, 1),
                ],
                from_time=datetime(2025, 1, 1),
                to_time=datetime(2025, 3, 31, 23, 45),
            )

            # DataFrame-Struktur:
            #                          2025-01-01 00:00:00  2025-02-01 00:00:00  2025-03-01 00:00:00
            # time                     value    quality     value    quality     value    quality
            # 2025-01-01 00:00:00      45.20    VALID       NaN      NaN         NaN      NaN
            # 2025-01-01 00:15:00      44.80    VALID       NaN      NaN         NaN      NaN
            # 2025-02-01 00:00:00      NaN      NaN         47.30    VALID       NaN      NaN

            # Analyse: Preisentwicklung über Monate
            monthly_avg = df.xs("value", level=1, axis=1).resample("M").mean()

            # Szenario 2: Freies Raster - Forecast-Accuracy
            df = client.compare_quotations(
                inventory_name="EnergyForecasts",
                timeseries_property="predicted_load",
                item_id="123",
                quotation_times=[
                    datetime(2025, 1, 8, 6, 0),  # 2 Tage vorher
                    datetime(2025, 1, 9, 6, 0),  # 1 Tag vorher
                    datetime(2025, 1, 10, 6, 0),  # Am Tag selbst
                ],
                from_time=datetime(2025, 1, 10),
                to_time=datetime(2025, 1, 10, 23, 59),
            )

            # Analyse: Wie haben sich Forecasts entwickelt?
            forecast_values = df.xs("value", level=1, axis=1)
            forecast_diff = forecast_values.diff(
                axis=1
            )  # Änderungen zwischen Forecasts

            # Plotten
            forecast_values.plot(title="Forecast Evolution")
            ```
        """
        if not _PANDAS_AVAILABLE:
            raise ImportError(
                "pandas is required for DataFrame output. "
                "Install with: pip install questra-data[pandas]"
            )

        if not quotation_times:
            raise ValueError("At least one quotation_time is required")

        logger.info(
            "Comparing quotations",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            timeseries_property=timeseries_property,
            item_id=item_id,
            quotation_count=len(quotation_times),
        )

        # Lade Daten für jede Notierung
        all_data = {}
        for quot_time in quotation_times:
            logger.debug(f"Loading data for quotation {quot_time}")
            data = self.list_quotation_values(
                inventory_name=inventory_name,
                timeseries_property=timeseries_property,
                from_time=from_time,
                to_time=to_time,
                quotation_time=quot_time,
                quotation_exactly_at=True,  # Exakt diese Notierung
                namespace_name=namespace_name,
                where={"_id": {"eq": item_id}},
                aggregation=aggregation,
                time_unit=time_unit,
                multiplier=multiplier,
            )

            # Extrahiere Werte für dieses Item
            if item_id in data:
                item_data = data[item_id]
                if timeseries_property in item_data["timeseries"]:
                    ts_values = item_data["timeseries"][timeseries_property]["values"]
                    all_data[quot_time] = ts_values
                else:
                    logger.warning(
                        f"No data for quotation {quot_time} and property {timeseries_property}"
                    )
                    all_data[quot_time] = []
            else:
                logger.warning(f"No data for quotation {quot_time} and item {item_id}")
                all_data[quot_time] = []

        # Erstelle DataFrame mit MultiIndex-Spalten
        # Format: (quotation_time, "value") und (quotation_time, "quality")
        data_dict = {}

        for quot_time, values in all_data.items():
            # Spalte für values
            value_series = pd.Series(  # type: ignore
                {val.time: float(val.value) for val in values},
                name=(quot_time, "value"),
            )
            data_dict[(quot_time, "value")] = value_series

            # Spalte für quality
            quality_series = pd.Series(  # type: ignore
                {
                    val.time: val.quality.value if val.quality else None
                    for val in values
                },
                name=(quot_time, "quality"),
            )
            data_dict[(quot_time, "quality")] = quality_series

        # Erstelle DataFrame
        df = pd.DataFrame(data_dict)  # type: ignore
        df.index.name = "time"
        df = df.sort_index()

        # Setze MultiIndex für Spalten
        df.columns = pd.MultiIndex.from_tuples(  # type: ignore
            df.columns, names=["quotation_time", "metric"]
        )

        logger.info(f"Created comparison DataFrame with {len(df)} rows")
        return df

    # ===== Inventory-Operationen =====

    def list_items(
        self,
        inventory_name: str,
        properties: list[str] | None = None,
        namespace_name: str | None = None,
        where: dict[str, Any] | None = None,
        limit: int = 100,
    ) -> QueryResult:
        """
        Listet Items aus einem Inventory auf.

        Dies ist eine vereinfachte Methode, die intern eine GraphQL-Query durchführt.

        Args:
            inventory_name: Name des Inventorys
            namespace_name: Name des Namespaces (optional)
            properties: Liste der abzurufenden Felder (optional, Standard: nur _id und _rowVersion)
            where: Filter-Bedingungen (optional). Schema-abhängig. Beispiele:
                {"Name": {"eq": "John"}}, {"Age": {"gt": 18}},
                {"and": [{"Name": {"eq": "John"}}, {"Age": {"gt": 18}}]}
            limit: Maximale Anzahl von Items (Standard: 100)

        Returns:
            QueryResult: Query-Ergebnis, das sich wie eine Liste verhält.
                Unterstützt Iteration, Indexing und `.to_df()` für DataFrame-Konvertierung.
                Enthält immer '_id' und '_rowVersion'.

        Examples:
            ```python
            # Direkte Nutzung wie Liste
            items = client.list_items(
                inventory_name=\"TestUser\",
                namespace_name=\"TestNamespace\",
                properties=[\"_id\", \"Name\", \"Email\"],
                where={\"Name\": {\"_eq\": \"John\"}},
                limit=50
            )
            for item in items:
                print(f\"{item['Name']}: {item['Email']}\")

            # Optional: DataFrame-Konvertierung
            df = items.to_df()
            ```
        """
        logger.info(
            "Listing inventory items",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            limit=limit,
        )

        # Wenn keine Felder angegeben, lade Basis-Felder
        if properties is None:
            properties = ["_id", "_rowVersion"]

        result = self._client.inventory.list(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            properties=properties,
            where=where,
            first=limit,
        )

        items = result.get("nodes", [])
        logger.info(f"Listed {len(items)} items from {inventory_name}")
        return QueryResult(items)

    def create_items(
        self,
        inventory_name: str,
        items: list[dict[str, Any]],
        namespace_name: str | None = None,
    ) -> list[dict[str, Any]]:
        """
        Erstellt neue Items in einem Inventory.

        Bei Properties vom DataType.TIMESERIES:
        - None: Neue TimeSeries wird automatisch erstellt und zugewiesen
        - String/Int (z.B. "123" oder 123): Bestehende TimeSeries-ID wird zugewiesen
        - Property nicht vorhanden: Keine TimeSeries wird angelegt

        Args:
            inventory_name: Name des Inventorys
            namespace_name: Name des Namespaces (optional)
            items: Liste von Item-Dictionaries mit schema-abhängigen Feldern

        Returns:
            list[dict[str, Any]]: Liste der erstellten Items mit '_id', '_rowVersion', '_existed'

        Examples:
            ```python
            # Einfache Items ohne TimeSeries
            items = [
                {\"Name\": \"John Doe\", \"Email\": \"john@example.com\"},
                {\"Name\": \"Jane Smith\", \"Email\": \"jane@example.com\"}
            ]
            created = client.create_items(
                inventory_name=\"TestUser\",
                namespace_name=\"TestNamespace\",
                items=items
            )
            for item in created:
                print(f\"Created item with ID: {item['_id']}\")

            # TIMESERIES - automatische Erstellung:
            ```python
            items = [
                {"Name": "Sensor1", "temperature_data": None},  # Neue Zeitreihe wird erstellt
                {"Name": "Sensor2", "temperature_data": None},  # Neue Zeitreihe wird erstellt
            ]
            created = client.create_items(
                inventory_name="Sensors",
                namespace_name="IoT",
                items=items
            )
            # temperature_data enthält nun automatisch neue TimeSeries-IDs

            # TIMESERIES - bestehende ID verwenden:
            ```python
            items = [
                {"Name": "Sensor1", "temperature_data": "1234"},  # Bestehende ID als String
                {"Name": "Sensor2", "temperature_data": 5678},    # Bestehende ID als Int
                {"Name": "Sensor3", "temperature_data": None},    # Neue Zeitreihe erstellen
            ]
            created = client.create_items(
                inventory_name="Sensors",
                namespace_name="IoT",
                items=items
            )
            # Sensor1-2: Bestehende IDs werden zugewiesen
            # Sensor3: Neue TimeSeries wird erstellt

            # TIMESERIES - selektiv:
            ```python
            items = [
                {"Name": "Sensor1", "temperature_data": None, "humidity_data": None},  # Beide Zeitreihen erstellt
                {"Name": "Sensor2", "temperature_data": None},                         # Nur temperature_data erstellt
                {"Name": "Sensor3"},                                                   # Keine Zeitreihen erstellt
            ]
            created = client.create_items(
                inventory_name="Sensors",
                items=items
            )
            ```
        """
        logger.info(
            "Creating inventory items",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            item_count=len(items),
        )

        # Schritt 1: Lade Inventory-Schema um TIMESERIES-Properties zu identifizieren
        inventories = self._client.queries.get_inventories(
            where={
                "inventoryNames": [inventory_name],
                "namespaceNames": [namespace_name] if namespace_name else None,
            }
        )

        if not inventories:
            logger.warning(
                f"Inventory {inventory_name} not found, skipping TimeSeries auto-creation"
            )
            result = self._client.inventory.create(
                inventory_name=inventory_name,
                namespace_name=namespace_name,
                items=items,
            )
            logger.info(f"Created {len(result)} items in {inventory_name}")
            return result

        inventory = inventories[0]

        # Schritt 2: Identifiziere TIMESERIES-Properties
        timeseries_properties = {}
        if inventory.properties:
            for prop in inventory.properties:
                if prop.data_type == DataType.TIME_SERIES:
                    timeseries_properties[prop.name] = prop

        logger.debug(
            f"Found {len(timeseries_properties)} TIMESERIES properties",
            properties=list(timeseries_properties.keys()),
        )

        # Schritt 3: Erstelle TimeSeries für alle Items mit TIMESERIES-Properties
        if timeseries_properties:
            from .models.inputs import CreateTimeSeriesInput

            # Sammle alle zu erstellenden TimeSeries
            timeseries_to_create = []
            # Mapping: (item_index, property_name) -> TimeSeries-Index
            timeseries_mapping = {}

            for item_idx, item in enumerate(items):
                for prop_name, prop in timeseries_properties.items():
                    # Prüfe ob Item diese Property enthält (case-insensitive für ersten Buchstaben)
                    item_key = None
                    for key in item.keys():
                        if (
                            key.lower()[0] == prop_name.lower()[0]
                            and key[1:] == prop_name[1:]
                        ):
                            item_key = key
                            break

                    # Erstelle TimeSeries nur wenn Property im Item vorhanden ist
                    if item_key is not None:
                        item_value = item[item_key]

                        # Bestimme ob TimeSeries erstellt werden soll:
                        # - None -> Neue TimeSeries erstellen
                        # - String/Int -> Bestehende ID verwenden
                        # - Andere Formate -> Fehler
                        if item_value is None:
                            # None -> Neue TimeSeries erstellen
                            ts_input = CreateTimeSeriesInput(
                                inventoryName=inventory_name,
                                propertyName=prop_name,
                                namespaceName=namespace_name,
                            )
                            timeseries_mapping[(item_idx, item_key)] = len(
                                timeseries_to_create
                            )
                            timeseries_to_create.append(ts_input)
                        elif isinstance(item_value, (str, int)):
                            # Direkte ID als String/Int -> Bestehende ID verwenden
                            items[item_idx][item_key] = int(item_value)
                            logger.debug(
                                f"Using existing TimeSeries ID {item_value} for item {item_idx}, property {item_key}"
                            )
                        else:
                            # Unbekanntes Format -> Fehler
                            raise ValueError(
                                f"Invalid TimeSeries value for item {item_idx}, property {item_key}: {item_value}. "
                                f"Expected None (auto-create) or string/int (existing ID)."
                            )

            # Erstelle alle TimeSeries auf einmal via GraphQL Mutation
            if timeseries_to_create:
                logger.info(
                    f"Creating {len(timeseries_to_create)} TimeSeries via GraphQL"
                )
                ts_results = self._client.mutations.create_timeseries(
                    timeseries_to_create
                )

                # Schritt 4: Setze TimeSeries-IDs in Items ein
                for (item_idx, prop_key), ts_idx in timeseries_mapping.items():
                    ts_id = int(ts_results[ts_idx].id)

                    # Setze TimeSeries-ID in Item
                    # Format: Direkt die ID als String (wird später von _convert_numbers_to_strings verarbeitet)
                    items[item_idx][prop_key] = ts_id

                    logger.debug(
                        f"Assigned TimeSeries ID {ts_id} to item {item_idx}, property {prop_key}"
                    )

        # Schritt 5: Erstelle Items mit zugewiesenen TimeSeries-IDs
        result = self._client.inventory.create(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            items=items,
        )
        logger.info(f"Created {len(result)} items in {inventory_name}")
        return result

    def update_items(
        self,
        inventory_name: str,
        items: list[dict[str, Any]],
        namespace_name: str | None = None,
    ) -> list[dict[str, Any]]:
        """
        Aktualisiert bestehende Items in einem Inventory.

        Args:
            inventory_name: Name des Inventorys
            namespace_name: Name des Namespaces (optional)
            items: Liste von Item-Dictionaries (mit '_id' und '_rowVersion' erforderlich)

        Returns:
            list[dict[str, Any]]: Liste der aktualisierten Items mit '_id', '_rowVersion', '_existed'

        Examples:
            ```python
            # Items laden
            items = client.list_items(
                inventory_name=\"TestUser\",
                namespace_name=\"TestNamespace\"
            )

            # Items ändern
            items[0][\"Email\"] = \"newemail@example.com\"

            # Items aktualisieren
            updated = client.update_items(
                inventory_name=\"TestUser\",
                namespace_name=\"TestNamespace\",
                items=[items[0]]
            )
            ```
        """
        logger.info(
            "Updating inventory items",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            item_count=len(items),
        )

        result = self._client.inventory.update(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            items=items,
        )
        logger.info(f"Updated {len(result)} items in {inventory_name}")
        return result

    def delete_items(
        self,
        inventory_name: str,
        item_ids: list[int | dict[str, Any]],
        namespace_name: str | None = None,
    ) -> list[dict[str, Any]]:
        """
        Löscht Items aus einem Inventory.

        Args:
            inventory_name: Name des Inventorys
            namespace_name: Name des Namespaces (optional)
            item_ids: Liste von Item-IDs (int) oder Item-Dictionaries mit '_id' und '_rowVersion'

        Returns:
            list[dict[str, Any]]: Liste mit '_id', '_rowVersion', '_existed' für jedes gelöschte Item

        Examples:
            ```python
            # Mit IDs
            deleted = client.delete_items(
                inventory_name="TestUser",
                namespace_name="TestNamespace",
                item_ids=[1, 2, 3],
            )

            # Mit vollständigen Items
            items = client.list_items(
                inventory_name="TestUser", namespace_name="TestNamespace"
            )
            deleted = client.delete_items(
                inventory_name="TestUser",
                namespace_name="TestNamespace",
                item_ids=items[:5],  # Lösche erste 5
            )
            ```
        """
        logger.info(
            "Deleting inventory items",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            item_count=len(item_ids),
        )

        # Konvertiere IDs zu Dictionaries falls nötig
        items_to_delete = []
        for item in item_ids:
            if isinstance(item, dict):
                items_to_delete.append(item)
            else:
                # Lade Item um _rowVersion zu bekommen
                # Hinweis: _id verwendet lowercase Filter (eq) statt _eq
                loaded = self._client.inventory.list(
                    inventory_name=inventory_name,
                    namespace_name=namespace_name,
                    properties=["_id", "_rowVersion"],
                    where={"_id": {"eq": item}},
                    first=1,
                )
                if loaded.get("nodes"):
                    items_to_delete.append(loaded["nodes"][0])

        result = self._client.inventory.delete(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            items=items_to_delete,
        )

        logger.info(f"Deleted {len(result)} items from {inventory_name}")
        return result

    # ===== Verwaltungs-Operationen =====

    def create_namespace(
        self,
        name: str,
        description: str | None = None,
        if_exists: ConflictAction = ConflictAction.IGNORE,
    ) -> NamedItemResult:
        """
        Erstellt einen Namespace.

        Args:
            name: Name des Namespaces
            description: Beschreibung (optional)
            if_exists: Verhalten wenn Namespace existiert (Standard: IGNORE)

        Returns:
            NamedItemResult: Dictionary mit Namespace-Informationen

        Examples:
            ```python
            result = client.create_namespace(
                name="MyNamespace", description="Mein Test-Namespace"
            )
            print(f"Namespace erstellt: {result['name']}")
            ```
        """
        logger.info("Creating namespace", name=name)

        result = self._client.mutations.create_namespace(
            namespace_name=name,
            description=description,
            if_exists=if_exists,
        )

        logger.info(f"Namespace created: {name}, existed={result.existed}")
        return result

    def create_inventory(
        self,
        name: str,
        properties: list[
            InventoryProperty
            | StringProperty
            | IntProperty
            | LongProperty
            | DecimalProperty
            | BoolProperty
            | DateTimeProperty
            | DateTimeOffsetProperty
            | DateProperty
            | TimeProperty
            | GuidProperty
            | FileProperty
            | TimeSeriesProperty
        ],
        namespace_name: str | None = None,
        description: str | None = None,
        enable_audit: bool = False,
        relations: list[InventoryRelation] | None = None,
        if_exists: ConflictAction = ConflictAction.IGNORE,
    ) -> NamedItemResult:
        """
        Erstellt ein Inventory.

        Args:
            name: Name des Inventorys
            namespace_name: Name des Namespaces (optional)
            properties: Liste von Property-Definitionen (InventoryProperty oder spezialisierte Property-Klassen)
            description: Beschreibung (optional)
            enable_audit: Audit aktivieren (Standard: False)
            relations: Liste von Relationen (InventoryRelation Dataclasses, optional)
            if_exists: Verhalten wenn Inventory existiert (Standard: IGNORE)

        Returns:
            NamedItemResult: Dictionary mit Inventory-Informationen

        Examples:
            ```python
            # Mit spezialisierten Property-Klassen (empfohlen)
            from questra_data import (
                StringProperty,
                IntProperty,
                TimeSeriesProperty,
                TimeUnit,
            )

            properties = [
                StringProperty(propertyName="Name", maxLength=200, isRequired=True),
                IntProperty(propertyName="Age", isRequired=False),
                TimeSeriesProperty(
                    propertyName="messwerte_temperatur",
                    timeUnit=TimeUnit.MINUTE,
                    multiplier=15,
                    unit="°C",
                ),
            ]

            result = client.create_inventory(
                name="TestUser",
                namespace_name="TestNamespace",
                properties=properties,
                description="Test Inventory",
            )

            # Mit Relationen
            from questra_data import InventoryRelation, RelationType

            relations = [
                InventoryRelation(
                    propertyName="Abteilung",
                    relationType=RelationType.ONE_TO_MANY,
                    parentInventoryName="Departments",
                    parentPropertyName="Mitarbeiter",
                    parentNamespaceName="HR",
                )
            ]

            result = client.create_inventory(
                name="Employees",
                namespace_name="HR",
                properties=properties,
                relations=relations,
                description="Employee Inventory with Department relation",
            )
            ```
        """
        logger.info("Creating inventory", name=name, namespace_name=namespace_name)

        normalized_properties = self._normalize_properties(properties)

        result = self._client.mutations.create_inventory(
            inventory_name=name,
            properties=normalized_properties,
            namespace_name=namespace_name,
            description=description,
            enable_audit=enable_audit,
            relations=relations,
            if_exists=if_exists,
        )

        logger.info(f"Inventory created: {name}, existed={result.existed}")
        return result

    def delete_inventory(
        self,
        inventory_name: str,
        namespace_name: str | None = None,
        if_not_exists: ConflictAction = ConflictAction.IGNORE,
    ) -> NamedItemResult:
        """
        Löscht ein Inventory.

        Args:
            inventory_name: Name des Inventorys
            namespace_name: Name des Namespaces (optional)
            if_not_exists: Verhalten wenn Inventory nicht existiert (Standard: IGNORE)

        Returns:
            NamedItemResult: Dictionary mit Inventory-Informationen

        Examples:
            ```python
            result = client.delete_inventory(
                inventory_name="TestInventory", namespace_name="TestNamespace"
            )
            print(
                f"Inventory gelöscht: {result['name']}, existierte: {result['existed']}"
            )
            ```
        """
        logger.info(
            "Deleting inventory",
            inventory_name=inventory_name,
            namespace_name=namespace_name,
        )

        result = self._client.mutations.delete_inventory(
            inventory_name=inventory_name,
            namespace_name=namespace_name,
            if_not_exists=if_not_exists,
        )

        logger.info(f"Inventory deleted: {inventory_name}, existed={result.existed}")
        return result

    def get_system_info(self) -> SystemInfo:
        """
        Ruft System-Informationen ab.

        Returns:
            SystemInfo: Objekt mit System-Informationen

        Examples:
            ```python
            info = client.get_system_info()
            print(f"Dyno Version: {info.dynamic_objects_version}")
            print(f"Datenbank: {info.database_version}")
            ```
        """
        return self._catalog_manager.get_system_info()

    def list_namespaces(self) -> QueryResult[Namespace]:
        """
        Listet alle Namespaces auf.

        Returns:
            QueryResult[Namespace]: Query-Ergebnis, das sich wie eine Liste verhält.
                Unterstützt Iteration, Indexing und `.to_df()` für DataFrame-Konvertierung.

        Examples:
            ```python
            namespaces = client.list_namespaces()
            for ns in namespaces:
                print(f"- {ns.name}: {ns.description}")

            # Als DataFrame
            df = namespaces.to_df()
            ```
        """
        namespaces = self._catalog_manager.list_namespaces()
        return QueryResult(namespaces)

    def list_roles(self) -> QueryResult[Role]:
        """
        Listet alle Roles auf.

        Returns:
            QueryResult[Role]: Query-Ergebnis, das sich wie eine Liste verhält.
                Unterstützt Iteration, Indexing und `.to_df()` für DataFrame-Konvertierung.

        Examples:
            ```python
            roles = client.list_roles()
            for role in roles:
                print(f"- {role.name}: {role.description}")

            # Als DataFrame
            df = roles.to_df()
            ```
        """
        roles = self._catalog_manager.list_roles()
        return QueryResult(roles)

    def list_units(self) -> QueryResult[Unit]:
        """
        Listet alle Units auf.

        Returns:
            QueryResult[Unit]: Query-Ergebnis, das sich wie eine Liste verhält.
                Unterstützt Iteration, Indexing und `.to_df()` für DataFrame-Konvertierung.

        Examples:
            ```python
            units = client.list_units()
            for unit in units:
                print(f"- {unit.symbol}: {unit.aggregation}")

            # Als DataFrame
            df = units.to_df()
            ```
        """
        units = self._catalog_manager.list_units()
        return QueryResult(units)

    def list_time_zones(self) -> QueryResult[TimeZone]:
        """
        Listet alle verfügbaren Zeitzonen auf.

        Returns:
            QueryResult[TimeZone]: Query-Ergebnis, das sich wie eine Liste verhält.
                Unterstützt Iteration, Indexing und `.to_df()` für DataFrame-Konvertierung.

        Examples:
            ```python
            time_zones = client.list_time_zones()
            for tz in time_zones:
                print(f"- {tz.name}: UTC{tz.base_utc_offset}")

            # Als DataFrame
            df = time_zones.to_df()
            ```
        """
        time_zones = self._catalog_manager.list_time_zones()
        return QueryResult(time_zones)

    def list_inventories(
        self,
        namespace_name: str | None = None,
        inventory_names: list[str] | None = None,
    ) -> QueryResult[Inventory]:
        """
        Listet Inventories auf.

        Args:
            namespace_name: Filter nach Namespace (optional)
            inventory_names: Filter nach Namen (optional)

        Returns:
            QueryResult[Inventory]: Query-Ergebnis, das sich wie eine Liste verhält.
                Unterstützt Iteration, Indexing und `.to_df()` für DataFrame-Konvertierung.

        Examples:
            ```python
            inventories = client.list_inventories(namespace_name="TestNamespace")
            for inv in inventories:
                print(f"- {inv.name} ({inv.inventory_type.value})")

            # Als DataFrame
            df = inventories.to_df()
            ```
        """
        logger.info("Listing inventories", namespace_name=namespace_name)

        where = {}
        if namespace_name:
            where["namespaceNames"] = [namespace_name]
        if inventory_names:
            where["inventoryNames"] = inventory_names

        inventories = self._client.queries.get_inventories(
            where=where if where else None
        )
        return QueryResult(inventories)

    # ===== Direkter Zugriff auf Low-Level Client =====

    @property
    def lowlevel(self) -> QuestraDataCore:
        """
        Zugriff auf den Low-Level QuestraDataCore Client.

        Nützlich für fortgeschrittene Operationen, die nicht durch
        die High-Level API abgedeckt sind.

        Returns:
            QuestraDataCore: Instanz des Low-Level Clients

        Examples:
            ```python
            # High-Level API verwenden
            values = client.list_timeseries_values(...)

            # Low-Level API für spezielle Fälle
            result = client.lowlevel.execute_raw('''
                query {
                    _timeZones { name }
                }
            ''')
            ```
        """
        return self._client

    def is_authenticated(self) -> bool:
        """
        Prüft, ob der Client authentifiziert ist.

        Returns:
            bool: True wenn authentifiziert, sonst False
        """
        return self._client.is_authenticated()

    def __repr__(self) -> str:
        """String-Repräsentation des Clients."""
        auth_status = (
            "authenticated" if self.is_authenticated() else "not authenticated"
        )
        return f"QuestraData(url='{self._client.graphql_url}', status='{auth_status}')"
