"""Tabellen-Expander für die PBI Documentation by Oliver Gropp.

Erkennt Markdown-Tabellenzeilen mit Platzhaltern und expandiert sie
zu mehreren Zeilen basierend auf Listen-Metadaten.

Jede Gruppe hat eindeutige Platzhalter-Präfixe, daher ist das
Matching jetzt exakt — keine Heuristiken nötig.
"""
from __future__ import annotations

import re

from core.models import ReportMetadata

_PLACEHOLDER_RE = re.compile(r"\{\{(.+?)\}\}")

# Gruppen von Platzhaltern pro Tabellenzeile.
# Jeder Platzhalter ist dank Präfix eindeutig einer Gruppe zugeordnet.
TABLE_ROW_GROUPS: list[dict] = [
    {
        "list_attr": "data_sources",
        "columns": {
            "DS_Quelle": "name",
            "DS_System": "source_type",
            "DS_SchemaTabellen": "tables",
            "DS_Owner": "name",
            "DS_Domäne": "",
        },
    },
    {
        "list_attr": "power_queries",
        "columns": {
            "PQ_Name": "name",
            "PQ_Quelle": "source",
            "PQ_ZielTabelle": "target_table",
            "PQ_Beschreibung": "description",
        },
    },
    {
        "list_attr": "power_queries",
        "columns": {
            "PQT_Abfrage": "name",
            "PQT_Schrittname": "name",
            "PQT_Beschreibung": "description",
            "PQT_MCode": "m_expression",
        },
    },
    {
        "list_attr": "columns",
        "columns": {
            "COL_Abfrage": "table_name",
            "COL_Spalte": "name",
            "COL_QuellTyp": "data_type",
            "COL_ZielTyp": "data_type",
            "COL_Transformation": "",
        },
    },
    {
        "list_attr": "measures",
        "columns": {
            "DAX_Measure": "name",
            "DAX_Beschreibung": "description",
            "DAX_Formel": "expression",
        },
    },
    {
        "list_attr": "rls_roles",
        "columns": {
            "RLS_Rolle": "name",
            "RLS_Beschreibung": "description",
            "RLS_Tabelle": "_filter_table",
            "RLS_FilterLogik": "_filter_expression",
        },
    },
    {
        "list_attr": "tables",
        "columns": {
            "TBLTYP_Tabelle": "name",
            "TBLTYP_Typ": "table_type",
            "TBLTYP_Beschreibung": "description",
        },
    },
    {
        "list_attr": "relationships",
        "columns": {
            "REL_Von": "_from_display",
            "REL_Nach": "_to_display",
            "REL_Kardinalität": "cardinality",
            "REL_Kreuzfilter": "cross_filter",
            "REL_Aktiv": "_active_display",
        },
    },
]


def expand_table_rows(template: str, metadata: ReportMetadata) -> tuple[str, set[str]]:
    """Expandiert Tabellenzeilen mit Listen-Platzhaltern.

    Returns:
        Tuple aus (expandiertes Template, Set der expandierten Platzhalter-Namen).
    """
    lines = template.split("\n")
    result: list[str] = []
    expanded_placeholders: set[str] = set()

    for line in lines:
        expanded, ph_names = _try_expand_line(line, metadata)
        if expanded is not None:
            result.extend(expanded)
            expanded_placeholders.update(ph_names)
        else:
            result.append(line)

    return "\n".join(result), expanded_placeholders


def _try_expand_line(line: str, metadata: ReportMetadata) -> tuple[list[str] | None, set[str]]:
    """Versucht eine Tabellenzeile zu expandieren."""
    stripped = line.strip()
    if not stripped.startswith("|") or not stripped.endswith("|"):
        return None, set()

    placeholders = _PLACEHOLDER_RE.findall(line)
    if not placeholders:
        return None, set()
    placeholder_names = {p.strip() for p in placeholders}

    # Finde die passende Gruppe (eindeutig dank Präfixe)
    for group in TABLE_ROW_GROUPS:
        group_placeholders = set(group["columns"].keys())
        overlap = placeholder_names & group_placeholders
        if not overlap:
            continue

        # Liste aus Metadaten holen
        list_attr = group["list_attr"]
        if not hasattr(metadata, list_attr):
            continue
        items = getattr(metadata, list_attr)
        if not items or not isinstance(items, list):
            continue

        # Für jeden Eintrag eine Zeile erzeugen
        expanded_lines: list[str] = []
        for item in items:
            new_line = line
            for ph_name, attr_name in group["columns"].items():
                if ph_name not in placeholder_names:
                    continue
                if attr_name:
                    value = _get_attr_value(item, attr_name)
                else:
                    value = ""  # Leeres Mapping = manuell
                new_line = new_line.replace("{{" + ph_name + "}}", value)
            expanded_lines.append(new_line)

        if expanded_lines:
            return expanded_lines, overlap
        return None, set()

    return None, set()


def _get_attr_value(item: object, attr: str) -> str:
    """Extrahiert einen Attributwert als tabellensicheren String.

    Unterstützt spezielle berechnete Attribute mit Präfix '_':
    - _from_display → "from_table.from_column"
    - _to_display → "to_table.to_column"
    - _active_display → "Ja" / "Nein"
    """
    # Spezielle berechnete Attribute für Beziehungen
    if attr == "_from_display":
        ft = getattr(item, "from_table", "")
        fc = getattr(item, "from_column", "")
        return _sanitize_for_table(f"{ft}.{fc}" if ft and fc else str(ft))
    if attr == "_to_display":
        tt = getattr(item, "to_table", "")
        tc = getattr(item, "to_column", "")
        return _sanitize_for_table(f"{tt}.{tc}" if tt and tc else str(tt))
    if attr == "_active_display":
        val = getattr(item, "is_active", True)
        return "Ja" if val else "Nein"

    # Spezielle Attribute für RLS-Rollen (flattened)
    if attr == "_filter_table":
        fe = getattr(item, "filter_expressions", {})
        if isinstance(fe, dict) and fe:
            return _sanitize_for_table(", ".join(fe.keys()))
        return ""
    if attr == "_filter_expression":
        fe = getattr(item, "filter_expressions", {})
        if isinstance(fe, dict) and fe:
            parts = [f"{v}" for v in fe.values()]
            return _sanitize_for_table("; ".join(parts))
        return ""

    if not hasattr(item, attr):
        return ""
    value = getattr(item, attr)
    if value is None:
        return ""
    if isinstance(value, dict):
        if not value:
            return ""
        raw = "; ".join(f"{k}: {v}" for k, v in value.items())
    elif isinstance(value, list):
        if not value:
            return ""
        raw = ", ".join(str(v) for v in value)
    else:
        raw = str(value)

    return _sanitize_for_table(raw)


def _sanitize_for_table(text: str) -> str:
    """Bereinigt einen String für die Verwendung in einer Markdown-Tabellenzelle.

    - Zeilenumbrüche → ` ` (einzelnes Leerzeichen)
    - Pipe-Zeichen `|` → `∣` (Unicode-Ersatz, sieht gleich aus)
    - Mehrfache Leerzeichen → einzelnes Leerzeichen
    - Führende/nachfolgende Leerzeichen entfernen
    """
    # Zeilenumbrüche durch Leerzeichen ersetzen
    text = text.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
    # Pipe-Zeichen ersetzen (zerstört sonst die Tabellenspalten)
    text = text.replace("|", "∣")
    # Mehrfache Leerzeichen zusammenfassen
    text = re.sub(r" {2,}", " ", text)
    return text.strip()


def get_table_placeholder_names() -> set[str]:
    """Gibt alle Platzhalter-Namen zurück, die durch Tabellen-Expansion behandelt werden."""
    names: set[str] = set()
    for group in TABLE_ROW_GROUPS:
        names.update(group["columns"].keys())
    return names


def expand_manual_table_rows(
    template: str, values: dict[str, str]
) -> tuple[str, set[str]]:
    """Expandiert Tabellenzeilen mit manuellen Tabellen-Werten.

    Wenn ein Platzhalter-Wert Zeilenumbrüche enthält (aus dem Tabellen-Editor),
    wird die Tabellenzeile für jede Zeile dupliziert.
    """
    from core.manual_table_config import MANUAL_TABLE_GROUPS

    lines = template.split("\n")
    result: list[str] = []
    expanded_phs: set[str] = set()

    for line in lines:
        stripped = line.strip()
        if not stripped.startswith("|") or not stripped.endswith("|"):
            result.append(line)
            continue

        placeholders = _PLACEHOLDER_RE.findall(line)
        if not placeholders:
            result.append(line)
            continue

        ph_names = {p.strip() for p in placeholders}

        # Prüfe ob ein manueller Tabellen-Platzhalter mit Mehrzeilen-Wert vorliegt
        matched_group = None
        for group in MANUAL_TABLE_GROUPS:
            overlap = ph_names & set(group["columns"])
            if overlap:
                # Prüfe ob mindestens ein Wert Zeilenumbrüche hat
                has_multiline = any(
                    "\n" in values.get(col, "") for col in group["columns"]
                )
                if has_multiline:
                    matched_group = group
                    break

        if matched_group is None:
            result.append(line)
            continue

        # Anzahl Zeilen bestimmen
        col_values: dict[str, list[str]] = {}
        max_rows = 0
        for col in matched_group["columns"]:
            val = values.get(col, "")
            rows = val.split("\n") if val else [""]
            col_values[col] = rows
            max_rows = max(max_rows, len(rows))

        # Für jede Zeile eine Tabellenzeile erzeugen
        for row_idx in range(max_rows):
            new_line = line
            for col in matched_group["columns"]:
                rows = col_values.get(col, [""])
                cell_val = rows[row_idx] if row_idx < len(rows) else ""
                new_line = new_line.replace("{{" + col + "}}", _sanitize_for_table(cell_val))
            result.append(new_line)

        expanded_phs.update(ph_names & set(matched_group["columns"]))

    return "\n".join(result), expanded_phs
