"""Inventory-bezogene Type Models."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from enum import Enum


class InventoryType(str, Enum):
    """
    Typ des Inventars.

    Attributes:
        DATA: Daten-Inventory
        MANY_TO_MANY: Many-to-Many Beziehungs-Inventory
    """

    DATA = "DATA"
    MANY_TO_MANY = "MANY_TO_MANY"


class RelationType(str, Enum):
    """
    Typ der Relation.

    Attributes:
        ONE_TO_ONE: Eins-zu-Eins Beziehung
        ONE_TO_MANY: Eins-zu-Viele Beziehung
        MANY_TO_MANY: Viele-zu-Viele Beziehung
    """

    ONE_TO_ONE = "ONE_TO_ONE"
    ONE_TO_MANY = "ONE_TO_MANY"
    MANY_TO_MANY = "MANY_TO_MANY"


class DataType(str, Enum):
    """
    Datentyp für Properties.

    Attributes:
        ID: Eindeutige ID
        ROW_VERSION: Versionsnummer
        INSERTED_BY: Erstellt von (Benutzer)
        INSERTED_AT: Erstellungszeitpunkt
        UPDATED_BY: Geändert von (Benutzer)
        UPDATED_AT: Änderungszeitpunkt
        REFERENCE: Referenz auf anderes Inventory
        NAVIGATION: Navigation zu anderem Inventory
        STRING: Text/String
        INT: Ganzzahl (32-bit)
        LONG: Ganzzahl (64-bit)
        DECIMAL: Dezimalzahl
        DATE_TIME: Datum und Uhrzeit
        DATE_TIME_OFFSET: Datum und Uhrzeit mit Zeitzone
        DATE: Datum
        TIME: Uhrzeit
        BOOLEAN: Wahrheitswert (true/false)
        GUID: Global Unique Identifier
        FILE: Datei
        TIME_SERIES: Zeitreihe
    """

    ID = "ID"
    ROW_VERSION = "ROW_VERSION"
    INSERTED_BY = "INSERTED_BY"
    INSERTED_AT = "INSERTED_AT"
    UPDATED_BY = "UPDATED_BY"
    UPDATED_AT = "UPDATED_AT"
    REFERENCE = "REFERENCE"
    NAVIGATION = "NAVIGATION"
    STRING = "STRING"
    INT = "INT"
    LONG = "LONG"
    DECIMAL = "DECIMAL"
    DATE_TIME = "DATE_TIME"
    DATE_TIME_OFFSET = "DATE_TIME_OFFSET"
    DATE = "DATE"
    TIME = "TIME"
    BOOLEAN = "BOOLEAN"
    GUID = "GUID"
    FILE = "FILE"
    TIME_SERIES = "TIME_SERIES"


# ===== Output Config Classes (für GraphQL Query Responses) =====


@dataclass
class StringConfig:
    """
    String-Property Konfiguration (Query Output).

    Attributes:
        max_length: Maximale Länge in Zeichen
        is_case_sensitive: Groß-/Kleinschreibung beachten
    """

    max_length: int
    is_case_sensitive: bool

    @staticmethod
    def from_dict(data: dict) -> StringConfig:
        """Erstellt StringConfig aus GraphQL Response."""
        return StringConfig(
            max_length=int(data["maxLength"]),
            is_case_sensitive=data["isCaseSensitive"],
        )


@dataclass
class FileConfig:
    """
    File-Property Konfiguration (Query Output).

    Attributes:
        max_length: Maximale Länge in Bytes
    """

    max_length: int

    @staticmethod
    def from_dict(data: dict) -> FileConfig:
        """Erstellt FileConfig aus GraphQL Response."""
        return FileConfig(
            max_length=int(data["maxLength"]),
        )


@dataclass
class IntervalInfo:
    """
    Intervall-Information (Query Output).

    Attributes:
        time_unit: Zeiteinheit
        multiplier: Multiplikator
    """

    time_unit: str
    multiplier: int

    @staticmethod
    def from_dict(data: dict) -> IntervalInfo:
        """Erstellt IntervalInfo aus GraphQL Response."""
        return IntervalInfo(
            time_unit=data["timeUnit"],
            multiplier=int(data["multiplier"]),
        )


@dataclass
class TimeSeriesConfig:
    """
    TimeSeries-Property Konfiguration (Query Output).

    Attributes:
        interval: Zeitintervall
        unit: Einheit (z.B. "kWh", "°C")
        time_zone: Zeitzone
        specifics_per_instance_allowed: Spezifikationen pro Instanz erlaubt (optional)
        value_alignment: Wert-Ausrichtung (optional)
        value_availability: Wert-Verfügbarkeit (optional)
        default_aggregation: Standard-Aggregation (optional)
        start_of_time: Start-Zeitpunkt (optional)
        audit_enabled: Audit aktiviert (optional)
        quotation_enabled: Quotierung aktiviert (optional)
        default_quotation_behavior: Quotierungsverhalten (optional)
    """

    interval: IntervalInfo
    unit: str | None = None
    time_zone: str | None = None
    specifics_per_instance_allowed: bool | None = None
    value_alignment: str | None = None
    value_availability: str | None = None
    default_aggregation: str | None = None
    start_of_time: str | None = None
    audit_enabled: bool | None = None
    quotation_enabled: bool | None = None
    default_quotation_behavior: str | None = None

    @staticmethod
    def from_dict(data: dict) -> TimeSeriesConfig:
        """Erstellt TimeSeriesConfig aus GraphQL Response."""
        interval = (
            IntervalInfo.from_dict(data["interval"]) if data.get("interval") else None
        )
        return TimeSeriesConfig(
            interval=interval,
            unit=data.get("unit"),
            time_zone=data.get("timeZone"),
            specifics_per_instance_allowed=data.get("specificsPerInstanceAllowed"),
            value_alignment=data.get("valueAlignment"),
            value_availability=data.get("valueAvailability"),
            default_aggregation=data.get("defaultAggregation"),
            start_of_time=data.get("startOfTime"),
            audit_enabled=data.get("auditEnabled"),
            quotation_enabled=data.get("quotationEnabled"),
            default_quotation_behavior=data.get("defaultQuotationBehavior"),
        )


@dataclass
class InventoryProperty:
    """
    Property eines Inventars.

    Attributes:
        name: Name der Property
        field_name: Feldname in der Datenbank
        data_type: Datentyp
        is_required: Pflichtfeld
        is_unique: Eindeutig
        is_array: Array-Feld
        sort_order: Sortierreihenfolge
        description: Optionale Beschreibung
        string_config: String-spezifische Konfiguration (nur bei DataType.STRING)
        file_config: File-spezifische Konfiguration (nur bei DataType.FILE)
        timeseries_config: TimeSeries-spezifische Konfiguration (nur bei DataType.TIME_SERIES)
    """

    name: str
    field_name: str
    data_type: DataType
    is_required: bool
    is_unique: bool
    is_array: bool
    sort_order: int
    description: str | None = None
    string_config: StringConfig | None = None
    file_config: FileConfig | None = None
    timeseries_config: TimeSeriesConfig | None = None

    @staticmethod
    def from_dict(data: dict) -> InventoryProperty:
        """Erstellt InventoryProperty aus GraphQL Response."""
        # Parse typspezifische Configs
        string_config = None
        if data.get("string"):
            string_config = StringConfig.from_dict(data["string"])

        file_config = None
        if data.get("file"):
            file_config = FileConfig.from_dict(data["file"])

        timeseries_config = None
        if data.get("timeSeries"):
            timeseries_config = TimeSeriesConfig.from_dict(data["timeSeries"])

        return InventoryProperty(
            name=data["name"],
            field_name=data["fieldName"],
            data_type=DataType(data["dataType"]),
            is_required=data["isRequired"],
            is_unique=data["isUnique"],
            is_array=data["isArray"],
            sort_order=int(data["sortOrder"]),
            description=data.get("description"),
            string_config=string_config,
            file_config=file_config,
            timeseries_config=timeseries_config,
        )


@dataclass
class InventoryRelation:
    """
    Relation zwischen Inventaren.

    Attributes:
        relation_type: Typ der Relation
        is_required: Pflicht-Relation
    """

    relation_type: RelationType
    is_required: bool

    @staticmethod
    def from_dict(data: dict) -> InventoryRelation:
        """Erstellt InventoryRelation aus GraphQL Response."""
        return InventoryRelation(
            relation_type=RelationType(data["relationType"]),
            is_required=data["isRequired"],
        )


@dataclass
class Inventory:
    """
    Inventory (Tabelle/Entity).

    Attributes:
        name: Name des Inventars
        inventory_type: Typ des Inventars
        audit_enabled: Audit aktiviert
        created_by: Ersteller ID
        created_at: Erstellungszeitpunkt
        altered_by: Bearbeiter ID
        altered_at: Bearbeitungszeitpunkt
        namespace_name: Name des Namespace
        select_field_name: Feldname für SELECT
        insert_field_name: Feldname für INSERT
        update_field_name: Feldname für UPDATE
        delete_field_name: Feldname für DELETE
        description: Optionale Beschreibung
        properties: Liste der Properties
        parent_relations: Parent-Relationen
        child_relations: Child-Relationen
    """

    name: str
    inventory_type: InventoryType
    audit_enabled: bool
    created_by: int
    created_at: datetime
    altered_by: int
    altered_at: datetime
    namespace_name: str
    select_field_name: str
    insert_field_name: str
    update_field_name: str
    delete_field_name: str
    description: str | None = None
    properties: list[InventoryProperty] | None = None
    parent_relations: list[InventoryRelation] | None = None
    child_relations: list[InventoryRelation] | None = None

    @staticmethod
    def from_dict(data: dict) -> Inventory:
        """Erstellt Inventory aus GraphQL Response."""
        properties = None
        if data.get("properties"):
            properties = [InventoryProperty.from_dict(p) for p in data["properties"]]

        parent_relations = None
        if data.get("parentRelations"):
            parent_relations = [
                InventoryRelation.from_dict(r) for r in data["parentRelations"]
            ]

        child_relations = None
        if data.get("childRelations"):
            child_relations = [
                InventoryRelation.from_dict(r) for r in data["childRelations"]
            ]

        # Namespace kann ein Objekt oder nur der Name sein
        namespace_name = (
            data.get("namespace", {}).get("name")
            if isinstance(data.get("namespace"), dict)
            else None
        )

        return Inventory(
            name=data["name"],
            inventory_type=InventoryType(data["inventoryType"]),
            audit_enabled=data["auditEnabled"],
            created_by=int(data["createdBy"]),
            created_at=datetime.fromisoformat(data["createdAt"].replace("Z", "+00:00")),
            altered_by=int(data["alteredBy"]),
            altered_at=datetime.fromisoformat(data["alteredAt"].replace("Z", "+00:00")),
            namespace_name=namespace_name,
            select_field_name=data["selectFieldName"],
            insert_field_name=data["insertFieldName"],
            update_field_name=data["updateFieldName"],
            delete_field_name=data["deleteFieldName"],
            description=data.get("description"),
            properties=properties,
            parent_relations=parent_relations,
            child_relations=child_relations,
        )
