"""Generator für die Workspace-Bulk-Dokumentation.

Enthält die eingebauten Standard-Vorlagen für
- die Workspace-Übersichtsdatei und
- eigenständige Berichtsdateien (wenn mehrere Berichte auf ein Semantic Model
  zugreifen).

Beide Vorlagen werden – falls noch nicht vorhanden – als Datei neben der
Anwendung erzeugt und danach als Standard verwendet. So können sie vom
Benutzer angepasst werden (analog zur Berichts-Dokumentationsvorlage).
"""
from __future__ import annotations

import os

from core.models import (
    WorkspaceItemInfo,
    WorkspaceMetadata,
    WorkspaceUserInfo,
)
from core.template_renderer import TemplateRenderer

# Dateinamen der Standard-Vorlagen (liegen neben der EXE / im Projektstamm)
WORKSPACE_TEMPLATE_FILENAME = "WorkspaceUebersichtsvorlage.md"
REPORT_TEMPLATE_FILENAME = "WorkspaceBerichtsvorlage.md"


# ---------------------------------------------------------------------------
# Eingebaute Standard-Vorlagen
# ---------------------------------------------------------------------------

DEFAULT_WORKSPACE_TEMPLATE = """# Workspace-Dokumentation: {{WS_Name}}

> Automatisch erzeugt am {{WS_ExportDatum}}

## 1. Überblick

| Eigenschaft | Wert |
|---|---|
| Workspace-Name | {{WS_Name}} |
| Workspace-ID | {{WS_ID}} |
| Beschreibung | {{WS_Beschreibung}} |
| Typ | {{WS_Typ}} |
| Dedizierte Kapazität | {{WS_DedizierteKapazitaet}} |
| Kapazitäts-ID | {{WS_Kapazitaet}} |
| Semantic Models | {{WS_AnzahlModelle}} |
| Berichte | {{WS_AnzahlBerichte}} |
| Sonstige Elemente | {{WS_AnzahlSonstige}} |

## 2. Semantic Models

{{WS_ModelleTabelle}}

## 3. Berichte

{{WS_BerichteTabelle}}

## 4. Sonstige Elemente

{{WS_SonstigeTabelle}}

## 5. Zugriffsberechtigungen (Benutzer/Gruppen pro Rolle)

{{WS_BenutzerTabelle}}
"""


DEFAULT_REPORT_TEMPLATE = """# Berichts-Dokumentation: {{RPT_Name}}

> Automatisch erzeugt am {{RPT_ExportDatum}}

## 1. Überblick

| Eigenschaft | Wert |
|---|---|
| Berichtsname | {{RPT_Name}} |
| Beschreibung | {{RPT_Beschreibung}} |
| Web-URL | {{RPT_WebUrl}} |
| Endorsement | {{RPT_Endorsement}} |

## 2. Semantic Model

Dieser Bericht basiert auf dem Semantic Model **{{RPT_ModellName}}**.
Die vollständige Modell-Dokumentation befindet sich in: {{RPT_ModellReferenz}}

## 3. Berichtsseiten & Visualisierungen

{{RPT_Visuals}}

## 4. Berichtszugriffe

{{RPT_Zugriffe}}
"""


# ---------------------------------------------------------------------------
# Vorlagen-Verwaltung
# ---------------------------------------------------------------------------

def ensure_template(directory: str, filename: str, default_content: str) -> str:
    """Stellt sicher, dass eine Vorlagendatei existiert, und gibt ihren Pfad zurück.

    Existiert die Datei noch nicht, wird sie mit dem eingebauten Standard
    erzeugt. Andernfalls bleibt die (ggf. angepasste) Datei unverändert.

    Args:
        directory: Verzeichnis, in dem die Vorlage liegen soll.
        filename: Dateiname der Vorlage.
        default_content: Eingebauter Standardinhalt.

    Returns:
        Absoluter Pfad zur Vorlagendatei.
    """
    path = os.path.join(directory, filename)
    if not os.path.isfile(path):
        os.makedirs(directory, exist_ok=True)
        with open(path, "w", encoding="utf-8") as f:
            f.write(default_content)
    return path


def load_template(directory: str, filename: str, default_content: str) -> str:
    """Lädt eine Vorlage; erzeugt sie zuvor als Standard, falls nicht vorhanden."""
    path = ensure_template(directory, filename, default_content)
    try:
        with open(path, encoding="utf-8") as f:
            return f.read()
    except OSError:
        return default_content


# ---------------------------------------------------------------------------
# Markdown-Hilfsfunktionen
# ---------------------------------------------------------------------------

def _escape_cell(value: str) -> str:
    """Escaped einen Wert für die Verwendung in einer Markdown-Tabellenzelle."""
    if value is None:
        return ""
    return str(value).replace("|", "\\|").replace("\n", " ").strip()


def _md_link(label: str, rel_path: str) -> str:
    """Erzeugt einen Markdown-Link mit URL-kodiertem Pfad (Leerzeichen -> %20)."""
    href = rel_path.replace("\\", "/").replace(" ", "%20")
    return f"[{_escape_cell(label)}]({href})"


# ---------------------------------------------------------------------------
# Tabellen-Generierung
# ---------------------------------------------------------------------------

def build_models_table(
    datasets: list[WorkspaceItemInfo],
    model_file_map: dict[str, str],
    report_count_map: dict[str, int],
) -> str:
    """Baut die Markdown-Tabelle der Semantic Models.

    Args:
        datasets: Liste der Semantic Models.
        model_file_map: dataset_id -> relativer Pfad zur Modell-Doku.
        report_count_map: dataset_id -> Anzahl zugehöriger Berichte.
    """
    if not datasets:
        return "_Keine Semantic Models vorhanden._"
    lines = [
        "| Semantic Model | Berichte | Endorsement | Dokumentation |",
        "|---|---|---|---|",
    ]
    for ds in datasets:
        rel = model_file_map.get(ds.id, "")
        doc = _md_link(os.path.basename(rel), rel) if rel else "_nicht dokumentiert_"
        count = report_count_map.get(ds.id, 0)
        lines.append(
            f"| {_escape_cell(ds.name)} | {count} | "
            f"{_escape_cell(ds.endorsement) or '–'} | {doc} |"
        )
    return "\n".join(lines)


def build_reports_table(
    reports: list[WorkspaceItemInfo],
    dataset_name_map: dict[str, str],
    report_file_map: dict[str, str],
) -> str:
    """Baut die Markdown-Tabelle der Berichte.

    Args:
        reports: Liste der (Power BI) Berichte.
        dataset_name_map: dataset_id -> Modell-Name.
        report_file_map: report_id -> relativer Pfad zur Doku (Bericht ODER Modell).
    """
    if not reports:
        return "_Keine Berichte vorhanden._"
    lines = [
        "| Bericht | Semantic Model | Endorsement | Dokumentation |",
        "|---|---|---|---|",
    ]
    for rep in reports:
        model_name = dataset_name_map.get(rep.dataset_id, "–")
        rel = report_file_map.get(rep.id, "")
        doc = _md_link(os.path.basename(rel), rel) if rel else "_nicht dokumentiert_"
        lines.append(
            f"| {_escape_cell(rep.name)} | {_escape_cell(model_name)} | "
            f"{_escape_cell(rep.endorsement) or '–'} | {doc} |"
        )
    return "\n".join(lines)


def build_other_items_table(other_items: list[WorkspaceItemInfo]) -> str:
    """Baut die Tabelle aller sonstigen Elemente (keine Modelle/Berichte)."""
    if not other_items:
        return "_Keine weiteren Elemente vorhanden._"
    lines = [
        "| Element | Typ | Beschreibung |",
        "|---|---|---|",
    ]
    for item in other_items:
        lines.append(
            f"| {_escape_cell(item.name)} | {_escape_cell(item.item_type)} | "
            f"{_escape_cell(item.description) or '–'} |"
        )
    return "\n".join(lines)


def build_users_table(users: list[WorkspaceUserInfo]) -> str:
    """Baut die Tabelle der freigegebenen Benutzer/Gruppen pro Rolle."""
    if not users:
        return (
            "_Keine Zugriffsinformationen verfügbar "
            "(ggf. fehlende Berechtigungen)._"
        )
    # Nach Rolle gruppieren
    order = ["Admin", "Member", "Contributor", "Viewer"]

    def _sort_key(u: WorkspaceUserInfo) -> tuple[int, str]:
        try:
            idx = order.index(u.access_right)
        except ValueError:
            idx = len(order)
        return idx, u.display_name.lower()

    lines = [
        "| Name | Typ | Rolle | Kennung |",
        "|---|---|---|---|",
    ]
    for u in sorted(users, key=_sort_key):
        lines.append(
            f"| {_escape_cell(u.display_name)} | {_escape_cell(u.principal_type) or '–'} | "
            f"{_escape_cell(u.access_right) or '–'} | {_escape_cell(u.identifier) or '–'} |"
        )
    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Overview-Rendering
# ---------------------------------------------------------------------------

def build_overview_values(
    ws: WorkspaceMetadata,
    model_file_map: dict[str, str],
    report_file_map: dict[str, str],
    report_count_map: dict[str, int],
) -> dict[str, str]:
    """Baut das Platzhalter-Wörterbuch für die Übersichtsvorlage."""
    dataset_name_map = {d.id: d.name for d in ws.datasets}
    return {
        "WS_Name": ws.workspace_name,
        "WS_ID": ws.workspace_id,
        "WS_Beschreibung": ws.description or "–",
        "WS_Typ": ws.workspace_type or "–",
        "WS_Kapazitaet": ws.capacity_id or "–",
        "WS_DedizierteKapazitaet": "Ja" if ws.is_on_dedicated_capacity else "Nein",
        "WS_ExportDatum": ws.extracted_at or "",
        "WS_AnzahlModelle": str(len(ws.datasets)),
        "WS_AnzahlBerichte": str(len(ws.reports)),
        "WS_AnzahlSonstige": str(len(ws.other_items)),
        "WS_ModelleTabelle": build_models_table(
            ws.datasets, model_file_map, report_count_map
        ),
        "WS_BerichteTabelle": build_reports_table(
            ws.reports, dataset_name_map, report_file_map
        ),
        "WS_SonstigeTabelle": build_other_items_table(ws.other_items),
        "WS_BenutzerTabelle": build_users_table(ws.users),
    }


def render_overview(
    template_content: str,
    ws: WorkspaceMetadata,
    model_file_map: dict[str, str],
    report_file_map: dict[str, str],
    report_count_map: dict[str, int],
) -> str:
    """Rendert die Workspace-Übersicht anhand der Vorlage."""
    values = build_overview_values(
        ws, model_file_map, report_file_map, report_count_map
    )
    rendered, _warnings = TemplateRenderer().render(template_content, values)
    return rendered


def build_visuals_table_from_metadata(metadata) -> str:  # type: ignore[no-untyped-def]
    """Baut eine Seiten-/Visual-Tabelle aus ReportMetadata.visuals."""
    visuals = getattr(metadata, "visuals", None) or []
    if not visuals:
        return "_Keine Visualisierungen verfügbar._"
    lines = [
        "| Seite | Visualtyp | Anzeigename |",
        "|---|---|---|",
    ]
    for v in visuals:
        lines.append(
            f"| {_escape_cell(v.page_name)} | {_escape_cell(v.visual_type)} | "
            f"{_escape_cell(v.display_name) or '–'} |"
        )
    return "\n".join(lines)


def render_standalone_report(
    template_content: str,
    report: WorkspaceItemInfo,
    model_name: str,
    model_ref_rel_path: str,
    metadata,  # type: ignore[no-untyped-def]
    export_date: str,
    report_access_md: str = "",
) -> str:
    """Rendert eine eigenständige Berichtsdatei (mehrere Berichte pro Modell)."""
    values = {
        "RPT_Name": report.name,
        "RPT_Beschreibung": report.description or "–",
        "RPT_WebUrl": report.web_url or "–",
        "RPT_Endorsement": report.endorsement or "–",
        "RPT_ModellName": model_name,
        "RPT_ModellReferenz": _md_link(
            os.path.basename(model_ref_rel_path), model_ref_rel_path
        )
        if model_ref_rel_path
        else "–",
        "RPT_Visuals": build_visuals_table_from_metadata(metadata),
        "RPT_Zugriffe": report_access_md or "–",
        "RPT_ExportDatum": export_date,
    }
    rendered, _warnings = TemplateRenderer().render(template_content, values)
    return rendered
