"""Datenmodelle für die PBI Documentation by Oliver Gropp.

Enthält alle Dataclasses für Metadaten, Template-Parsing,
Verbindungskonfiguration, Versionierung und Fehlerklassen.
"""
from __future__ import annotations

from dataclasses import dataclass, field


# ---------------------------------------------------------------------------
# Report-Metadaten Datenklassen
# ---------------------------------------------------------------------------

@dataclass
class ColumnInfo:
    """Informationen zu einer Spalte in einer Power BI Tabelle."""
    name: str
    table_name: str
    data_type: str
    description: str
    is_hidden: bool
    is_calculated: bool
    expression: str | None = None
    synonyms: list[str] = field(default_factory=list)
    is_key: bool = False


@dataclass
class TableInfo:
    """Informationen zu einer Power BI Tabelle."""
    name: str
    description: str
    columns: list[ColumnInfo] = field(default_factory=list)
    is_hidden: bool = False
    row_count: int | None = None
    mode: str = ""  # "import", "directQuery", "dual", "calculated"
    table_type: str = ""  # "Faktentabelle", "Dimensionstabelle", "Hilfstabelle"


@dataclass
class MeasureInfo:
    """Informationen zu einem DAX Measure."""
    name: str
    table_name: str
    expression: str
    description: str
    format_string: str | None = None


@dataclass
class RelationshipInfo:
    """Informationen zu einer Beziehung zwischen Tabellen."""
    from_table: str
    from_column: str
    to_table: str
    to_column: str
    cardinality: str  # "OneToMany", "ManyToOne", etc.
    cross_filter: str  # "Single", "Both"
    is_active: bool


@dataclass
class DataSourceInfo:
    """Informationen zu einer Datenquelle."""
    name: str
    source_type: str  # "SQL", "OData", "SharePoint", etc.
    connection_string: str
    schema: str | None = None
    tables: list[str] = field(default_factory=list)


@dataclass
class PowerQueryInfo:
    """Informationen zu einer Power Query Abfrage."""
    name: str
    source: str
    target_table: str
    m_expression: str
    description: str


@dataclass
class RLSRoleInfo:
    """Informationen zu einer Row-Level Security Rolle."""
    name: str
    description: str
    filter_expressions: dict[str, str] = field(default_factory=dict)
    members: list[str] = field(default_factory=list)


@dataclass
class VisualInfo:
    """Informationen zu einer Visualisierung auf einer Berichtsseite."""
    page_name: str
    visual_type: str  # "barChart", "table", "card", etc.
    title: str | None = None  # Technische ID
    display_name: str = ""  # Anzeigename (aus vcObjects.title)
    fields: list[str] = field(default_factory=list)


@dataclass
class DependentReportInfo:
    """Informationen zu einem Bericht, der auf dem Semantic Model basiert."""
    name: str
    report_id: str
    web_url: str = ""


@dataclass
class RefreshScheduleInfo:
    """Informationen zum Aktualisierungszeitplan."""
    frequency: str  # "Daily", "Weekly", etc.
    times: list[str] = field(default_factory=list)
    timezone: str = ""
    days: list[str] | None = None


# ---------------------------------------------------------------------------
# Workspace-Datenklassen (Bulk-Dokumentation eines Fabric-Workspaces)
# ---------------------------------------------------------------------------

@dataclass
class DataflowAttributeInfo:
    """Ein Attribut (Spalte) einer Dataflow-Entität."""
    name: str
    data_type: str = ""
    description: str = ""


@dataclass
class DataflowEntityInfo:
    """Eine Entität (Tabelle/Query) eines Dataflows.

    Deckt sowohl Gen1-Entitäten (aus der ``model.json``/CDM-Definition) als
    auch Gen2-Queries (aus dem Mashup-Dokument) ab.
    """
    name: str
    description: str = ""
    attributes: list[DataflowAttributeInfo] = field(default_factory=list)
    m_expression: str = ""  # Power Query (M) Ausdruck der Entität/Query
    is_computed: bool = False  # Berechnete Entität (verweist auf andere Entität)


@dataclass
class DataflowInfo:
    """Gesammelte Metadaten eines Dataflows (Gen1 oder Gen2).

    Wird für die eigenständige Dataflow-Dokumentation verwendet – analog zu
    :class:`ReportMetadata` für Semantic Models.
    """
    id: str
    name: str
    generation: str = "Gen1"  # "Gen1" oder "Gen2"
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    entities: list[DataflowEntityInfo] = field(default_factory=list)
    document_m: str = ""  # Vollständiges Mashup-/M-Dokument (falls verfügbar)
    refresh_schedule: RefreshScheduleInfo | None = None
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class NotebookCellInfo:
    """Eine einzelne Zelle eines Fabric-Notebooks (Code oder Markdown)."""
    index: int  # 1-basierte Position im Notebook
    cell_type: str = "code"  # "code" oder "markdown"
    source: str = ""  # Vollständiger Zellinhalt
    language: str = ""  # Sprache der Zelle (z. B. "python", "sql", "scala")


@dataclass
class NotebookInfo:
    """Gesammelte Metadaten eines Fabric-Notebooks.

    Wird für die eigenständige Notebook-Dokumentation verwendet – analog zu
    :class:`DataflowInfo` für Dataflows.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    default_language: str = ""  # Standardsprache/Kernel (z. B. "PySpark (Python)")
    default_lakehouse: str = ""  # Angebundenes Standard-Lakehouse (falls bekannt)
    cells: list[NotebookCellInfo] = field(default_factory=list)
    document_source: str = ""  # Vollständiger Notebook-Quelltext (falls verfügbar)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class CopyJobColumnMappingInfo:
    """Ein Spalten-Mapping (Quelle -> Ziel) innerhalb einer Copyjob-Aktivität."""
    source_name: str = ""
    source_type: str = ""
    destination_name: str = ""
    destination_type: str = ""


@dataclass
class CopyJobActivityInfo:
    """Ein einzelner Kopierauftrag (Tabelle/Objekt) eines Copyjobs."""
    index: int  # 1-basierte Position
    source_object: str = ""  # z. B. "primary.table1" oder Dateipfad
    destination_object: str = ""
    write_behavior: str = ""  # "Append", "Overwrite", "Upsert"
    table_option: str = ""  # z. B. "autoCreate"
    read_method: str = ""  # CDC: z. B. "SnapshotPlusIncremental"
    mappings: list[CopyJobColumnMappingInfo] = field(default_factory=list)


@dataclass
class CopyJobInfo:
    """Gesammelte Metadaten eines Fabric Copy Jobs.

    Wird für die eigenständige Copyjob-Dokumentation verwendet – analog zu
    :class:`DataflowInfo` und :class:`NotebookInfo`.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    job_mode: str = ""  # "Batch" oder "CDC" (Incremental)
    source_type: str = ""  # Verbindungstyp der Quelle (z. B. "AzureSqlDatabase")
    source_details: str = ""  # Lesbare Kurzbeschreibung der Quelle
    destination_type: str = ""  # Verbindungstyp des Ziels
    destination_details: str = ""  # Lesbare Kurzbeschreibung des Ziels
    timeout: str = ""  # Policy-Timeout
    retry: str = ""  # Policy-Retries
    activities: list[CopyJobActivityInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class LakehouseTableInfo:
    """Eine Tabelle innerhalb eines Fabric-Lakehouses."""
    name: str
    table_type: str = ""  # "Managed" oder "External"
    table_format: str = ""  # z. B. "delta"
    location: str = ""  # OneLake-/abfss-Pfad


@dataclass
class LakehouseInfo:
    """Gesammelte Metadaten eines Fabric-Lakehouses.

    Wird für die eigenständige Lakehouse-Dokumentation verwendet – analog zu
    :class:`DataflowInfo`, :class:`NotebookInfo` und :class:`CopyJobInfo`.
    Lakehouses besitzen keine ``getDefinition``; die Doku basiert auf den
    Item-Properties (SQL-Endpoint, OneLake-Pfade) und der Tabellenliste.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    default_schema: str = ""  # Nur bei schema-fähigen Lakehouses
    sql_connection_string: str = ""
    sql_endpoint_id: str = ""
    sql_provisioning_status: str = ""
    onelake_tables_path: str = ""
    onelake_files_path: str = ""
    tables: list[LakehouseTableInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class WarehouseInfo:
    """Gesammelte Metadaten eines Fabric-Warehouses.

    Property-basiertes Item (kein ``getDefinition``); die Doku basiert auf den
    Item-Properties (SQL-Verbindung, Collation, Zeitstempel).
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    connection_string: str = ""
    collation_type: str = ""
    created_date: str = ""
    last_updated_time: str = ""
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class EventhouseDatabaseInfo:
    """Eine (KQL-)Datenbank innerhalb eines Fabric-Eventhouses."""
    id: str
    name: str = ""


@dataclass
class EventhouseInfo:
    """Gesammelte Metadaten eines Fabric-Eventhouses.

    Property-basiertes Item (kein ``getDefinition``); die Doku basiert auf den
    Item-Properties (Query-/Ingestion-URI, enthaltene KQL-Datenbanken).
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    query_service_uri: str = ""
    ingestion_service_uri: str = ""
    minimum_consumption_units: str = ""
    databases: list[EventhouseDatabaseInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class KqlDatabaseInfo:
    """Gesammelte Metadaten einer Fabric KQL-Datenbank (KQL Database).

    Property-basiertes Item (kein ``getDefinition``); die Doku basiert auf den
    Item-Properties (Query-/Ingestion-URI, übergeordnetes Eventhouse, Typ).
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    parent_eventhouse_id: str = ""
    query_service_uri: str = ""
    ingestion_service_uri: str = ""
    database_type: str = ""
    onelake_caching_period: str = ""
    onelake_standard_storage_period: str = ""
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class SqlDatabaseInfo:
    """Gesammelte Metadaten einer Fabric SQL Database.

    Property-basiertes Item (kein ``getDefinition``); die Doku basiert auf den
    Item-Properties (SQL-Verbindung, Server-FQDN, Collation, Restore-Points).
    Das Datenbankschema ist über den SQL-Endpoint (T-SQL) erreichbar und
    fachlich zu ergänzen.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    connection_string: str = ""
    database_name: str = ""
    server_fqdn: str = ""
    collation: str = ""
    earliest_restore_point: str = ""
    latest_restore_point: str = ""
    backup_retention_days: str = ""
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class EnvironmentLibraryInfo:
    """Eine Bibliothek (custom oder extern) eines Fabric-Environments."""
    name: str
    library_type: str = ""  # "Custom" oder "External"
    version: str = ""


@dataclass
class EnvironmentInfo:
    """Gesammelte Metadaten eines Fabric-Environments (Spark-Environment).

    Property-basiertes Item; die Doku basiert auf den Item-Properties sowie den
    (best-effort) separaten Endpunkten für installierte Bibliotheken und die
    Spark-Compute-/Runtime-Einstellungen.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    publish_state: str = ""
    runtime_version: str = ""
    instance_pool_name: str = ""
    instance_pool_type: str = ""
    driver_cores: str = ""
    driver_memory: str = ""
    executor_cores: str = ""
    executor_memory: str = ""
    dynamic_allocation: str = ""
    libraries: list[EnvironmentLibraryInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class MirroredTableInfo:
    """Eine gespiegelte Tabelle einer Fabric Mirrored Database."""
    schema_name: str = ""
    table_name: str = ""


@dataclass
class MirroredDatabaseInfo:
    """Gesammelte Metadaten einer Fabric Mirrored Database.

    Definition-basiertes Item (``getDefinition`` → ``mirroring.json``); die Doku
    basiert auf der Quell-/Zielkonfiguration und den gespiegelten Tabellen.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    source_type: str = ""
    source_database: str = ""
    target_type: str = ""
    default_schema: str = ""
    target_format: str = ""
    retention_days: str = ""
    mirrored_tables: list[MirroredTableInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class EventstreamNodeInfo:
    """Ein Knoten (Quelle, Ziel oder Operator) eines Fabric Eventstreams."""
    name: str
    node_type: str = ""


@dataclass
class EventstreamInfo:
    """Gesammelte Metadaten eines Fabric Eventstreams.

    Definition-basiertes Item (``getDefinition`` → ``eventstream.json``); die
    Doku basiert auf Quellen, Zielen und Operatoren/Transformationen.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    sources: list[EventstreamNodeInfo] = field(default_factory=list)
    destinations: list[EventstreamNodeInfo] = field(default_factory=list)
    operators: list[EventstreamNodeInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class SparkJobDefinitionInfo:
    """Gesammelte Metadaten einer Fabric Spark Job Definition.

    Definition-basiertes Item (``getDefinition`` → ``SparkJobDefinitionV1.json``);
    die Doku basiert auf der ausführbaren Datei, Sprache, Hauptklasse und den
    Argumenten/Bibliotheken.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    language: str = ""
    executable_file: str = ""
    main_class: str = ""
    default_lakehouse: str = ""
    command_line_arguments: str = ""
    additional_libraries: list[str] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class KqlQueryInfo:
    """Eine gespeicherte Abfrage (Tab) eines Fabric KQL-Querysets."""
    name: str
    query_text: str = ""


@dataclass
class KqlQuerysetInfo:
    """Gesammelte Metadaten eines Fabric KQL-Querysets.

    Definition-basiertes Item (``getDefinition`` → ``RealTimeQueryset.json``);
    die Doku basiert auf den gespeicherten KQL-Abfragen/Tabs.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    queries: list[KqlQueryInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class ReflexObjectInfo:
    """Ein Objekt/eine Entität eines Fabric Reflex (Data Activator)."""
    name: str
    object_type: str = ""


@dataclass
class ReflexRuleInfo:
    """Eine Regel/ein Trigger eines Fabric Reflex (Data Activator)."""
    name: str
    rule_type: str = ""


@dataclass
class ReflexInfo:
    """Gesammelte Metadaten eines Fabric Reflex-Items (Data Activator).

    Definition-basiertes Item (``getDefinition`` → ``ReflexEntities.json``); die
    Doku basiert auf den Objekten/Entitäten und den Regeln/Triggern.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    objects: list[ReflexObjectInfo] = field(default_factory=list)
    rules: list[ReflexRuleInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class GraphQlDataSourceInfo:
    """Eine angebundene Datenquelle einer Fabric API for GraphQL."""
    name: str
    source_type: str = ""


@dataclass
class GraphQlApiInfo:
    """Gesammelte Metadaten eines Fabric „API for GraphQL"-Items.

    Definition-basiertes Item (``getDefinition``); die Doku basiert auf den
    angebundenen Datenquellen und dem GraphQL-Schema.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    schema: str = ""
    data_sources: list[GraphQlDataSourceInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class DataPipelineActivityInfo:
    """Eine Aktivität innerhalb einer Fabric Data Pipeline."""
    name: str
    activity_type: str = ""
    depends_on: list[str] = field(default_factory=list)  # Namen der Vorgänger
    container: str = ""  # Name der übergeordneten Aktivität (bei Verschachtelung)
    timeout: str = ""
    retry: str = ""


@dataclass
class DataPipelineParameterInfo:
    """Ein Parameter oder eine Variable einer Data Pipeline."""
    name: str
    param_type: str = ""
    default_value: str = ""


@dataclass
class DataPipelineInfo:
    """Gesammelte Metadaten einer Fabric Data Pipeline.

    Wird für die eigenständige Pipeline-Dokumentation verwendet – analog zu
    :class:`CopyJobInfo`. Basiert auf ``getDefinition`` → ``pipeline-content.json``.
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    activities: list[DataPipelineActivityInfo] = field(default_factory=list)
    parameters: list[DataPipelineParameterInfo] = field(default_factory=list)
    variables: list[DataPipelineParameterInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


@dataclass
class DashboardTileInfo:
    """Eine Kachel (Tile) eines Power BI / Fabric Dashboards."""
    id: str = ""
    title: str = ""
    subtitle: str = ""
    report_id: str = ""  # verknüpfter Bericht (falls vorhanden)
    dataset_id: str = ""  # verknüpftes Semantic Model (falls vorhanden)


@dataclass
class DashboardInfo:
    """Gesammelte Metadaten eines Power BI / Fabric Dashboards.

    Dashboards sind Power-BI-Artefakte (kein ``getDefinition``); die Doku
    basiert auf den Item-Properties und der Kachelliste (``/tiles``).
    """
    id: str
    name: str
    description: str = ""
    configured_by: str = ""
    modified_time: str = ""
    web_url: str = ""
    is_read_only: bool = False
    data_classification: str = ""
    tiles: list[DashboardTileInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


# ---------------------------------------------------------------------------
# Workspace-Metadaten
# ---------------------------------------------------------------------------

@dataclass
class WorkspaceUserInfo:
    """Ein Benutzer oder eine Gruppe mit Zugriff auf den Workspace."""
    display_name: str
    identifier: str  # E-Mail / UPN / Objekt-ID
    principal_type: str  # "User", "Group", "App", "None"
    access_right: str  # "Admin", "Member", "Contributor", "Viewer"


@dataclass
class WorkspaceItemInfo:
    """Ein Element (Artefakt) innerhalb eines Fabric-Workspaces.

    Deckt Berichte, Semantic Models sowie sonstige Artefakte
    (Dashboards, Dataflows, Paginated Reports usw.) ab.
    """
    id: str
    name: str
    item_type: str  # "Report", "SemanticModel", "Dashboard", "Dataflow", ...
    dataset_id: str = ""  # Nur bei Berichten: gebundenes Semantic Model
    web_url: str = ""
    description: str = ""
    endorsement: str = ""  # "Promoted", "Certified" oder ""
    configured_by: str = ""
    subtype: str = ""  # Feinere Klassifizierung, z.B. "Gen1"/"Gen2" bei Dataflows


@dataclass
class WorkspaceMetadata:
    """Gesammelte Metadaten eines Fabric-Workspaces (ohne Detail-Extraktion)."""
    workspace_id: str
    workspace_name: str
    description: str = ""
    capacity_id: str = ""
    workspace_type: str = ""
    is_on_dedicated_capacity: bool = False
    users: list[WorkspaceUserInfo] = field(default_factory=list)
    reports: list[WorkspaceItemInfo] = field(default_factory=list)
    datasets: list[WorkspaceItemInfo] = field(default_factory=list)
    other_items: list[WorkspaceItemInfo] = field(default_factory=list)
    extracted_at: str = ""


# ---------------------------------------------------------------------------
# Template/Parsing Datenklassen
# ---------------------------------------------------------------------------

@dataclass
class Placeholder:
    """Ein im Template gefundener Platzhalter."""
    name: str
    position: int
    line: int
    section: str
    context: str


@dataclass
class ParseResult:
    """Ergebnis des Template-Parsings."""
    placeholders: list[Placeholder] = field(default_factory=list)
    template_content: str = ""
    sections: list[str] = field(default_factory=list)


@dataclass
class PlaceholderMapping:
    """Zuordnung eines Platzhalters zu einem Wert."""
    placeholder: Placeholder
    value: str | None
    source: str  # "auto", "manual", "partial"
    confidence: float = 1.0


@dataclass
class PlaceholderCategory:
    """Kategorisierung eines Platzhalters nach Verfügbarkeit."""
    placeholder_name: str
    category: str  # "automatisch", "manuell", "teilweise"
    available_in: list[str] = field(default_factory=list)


@dataclass
class CategorySummary:
    """Zusammenfassung der Platzhalter-Kategorisierung."""
    auto_count: int = 0
    manual_count: int = 0
    partial_count: int = 0
    details: list[PlaceholderCategory] = field(default_factory=list)


# ---------------------------------------------------------------------------
# Verbindungs- und Metadaten-Datenklassen
# ---------------------------------------------------------------------------

@dataclass
class ConnectionConfig:
    """Konfiguration für eine Verbindung zu einer Power BI Quelle."""
    connection_type: str  # "fabric", "local", "pbip"
    params: dict[str, str] = field(default_factory=dict)


@dataclass
class ReportMetadata:
    """Gesammelte Metadaten eines Power BI Berichts."""
    report_name: str
    tables: list[TableInfo] = field(default_factory=list)
    measures: list[MeasureInfo] = field(default_factory=list)
    columns: list[ColumnInfo] = field(default_factory=list)
    relationships: list[RelationshipInfo] = field(default_factory=list)
    data_sources: list[DataSourceInfo] = field(default_factory=list)
    power_queries: list[PowerQueryInfo] = field(default_factory=list)
    rls_roles: list[RLSRoleInfo] = field(default_factory=list)
    visuals: list[VisualInfo] = field(default_factory=list)
    dependent_reports: list[DependentReportInfo] = field(default_factory=list)
    refresh_schedule: RefreshScheduleInfo | None = None
    report_users: list[WorkspaceUserInfo] = field(default_factory=list)
    raw_metadata: dict = field(default_factory=dict)


# ---------------------------------------------------------------------------
# Versionierungs-Datenklassen
# ---------------------------------------------------------------------------

@dataclass
class Change:
    """Eine erkannte Änderung zwischen zwei Dokumentversionen."""
    field: str
    old_value: str
    new_value: str
    change_type: str  # "fachlich", "technisch"


@dataclass
class ExistingDocInfo:
    """Informationen aus einer bestehenden Dokumentation."""
    version: str
    values: dict[str, str] = field(default_factory=dict)
    changelog: list[dict] = field(default_factory=list)
    last_updated: str = ""


# ---------------------------------------------------------------------------
# Fehlerklassen
# ---------------------------------------------------------------------------

class TemplateParseError(Exception):
    """Vorlage kann nicht gelesen oder geparst werden."""


class PBIConnectionError(Exception):
    """Verbindung zu Power BI Quelle fehlgeschlagen.

    Benannt als PBIConnectionError um Shadowing des builtin
    ConnectionError zu vermeiden.
    """


class AuthenticationError(Exception):
    """Authentifizierung fehlgeschlagen (Fabric Service)."""


class MetadataExtractionError(Exception):
    """Einzelnes Metadatum kann nicht extrahiert werden."""


class PBIPStructureError(Exception):
    """PBIP-Ordner enthält keine gültigen Dateien."""


class PBIPFileReadError(Exception):
    """Einzelne PBIP-Datei nicht lesbar."""


class RenderError(Exception):
    """Fehler beim Erzeugen des Ausgabedokuments."""


class VersionParseError(Exception):
    """Bestehende Dokumentation kann nicht geparst werden."""


class FileWriteError(Exception):
    """Ausgabedokument kann nicht gespeichert werden."""


class BridgeTimeoutError(Exception):
    """Backend-Operation hat das Zeitlimit überschritten."""


class CancelledError(Exception):
    """Operation wurde vom Benutzer abgebrochen."""
