"""Endorsement-Analyse für Power BI Semantic Models.

Analysiert die Metadaten-Vollständigkeit (Beschreibungen, Synonyme,
Schlüsselspalten) und erzeugt eine Bewertung mit Ranking-Labels.
"""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any

from core.models import ReportMetadata


# ---------------------------------------------------------------------------
# Ranking-Schwellenwerte (Modul-Konstanten)
# ---------------------------------------------------------------------------

RANKING_THRESHOLD_GUT = 80.0       # ≥ 80% → "gut gepflegt"
RANKING_THRESHOLD_GEPFLEGT = 50.0  # ≥ 50% → "gepflegt"
RANKING_THRESHOLD_SCHLECHT = 25.0  # ≥ 25% → "schlecht gepflegt"
# < 25% → "nicht gepflegt"

RANKING_LABEL_GUT = "gut gepflegt"
RANKING_LABEL_GEPFLEGT = "gepflegt"
RANKING_LABEL_SCHLECHT = "schlecht gepflegt"
RANKING_LABEL_NICHT = "nicht gepflegt"


# Anzeige-Übersetzung der (kanonisch deutschen) Ranking-Labels.
_RANKING_DISPLAY: dict[str, dict[str, str]] = {
    "de": {
        RANKING_LABEL_GUT: RANKING_LABEL_GUT,
        RANKING_LABEL_GEPFLEGT: RANKING_LABEL_GEPFLEGT,
        RANKING_LABEL_SCHLECHT: RANKING_LABEL_SCHLECHT,
        RANKING_LABEL_NICHT: RANKING_LABEL_NICHT,
    },
    "en": {
        RANKING_LABEL_GUT: "well maintained",
        RANKING_LABEL_GEPFLEGT: "maintained",
        RANKING_LABEL_SCHLECHT: "poorly maintained",
        RANKING_LABEL_NICHT: "not maintained",
    },
}

# Übersetzung der Markdown-Labels für format_markdown.
_FORMAT_T: dict[str, dict[str, str]] = {
    "de": {
        "overview": "### Gesamtübersicht",
        "th_metric": "| Metrik | Wert |",
        "sep_metric": "|--------|------|",
        "score": "Completeness Score",
        "filled_total": "Ausgefüllt / Gesamt",
        "rating": "Bewertung",
        "detail": "### Detailanalyse je Tabelle",
        "table": "Tabelle",
        "th_columns": "| Spalte | Beschreibung | Synonyme |",
        "sep_columns": "|--------|-------------|----------|",
    },
    "en": {
        "overview": "### Overview",
        "th_metric": "| Metric | Value |",
        "sep_metric": "|--------|-------|",
        "score": "Completeness score",
        "filled_total": "Filled / Total",
        "rating": "Rating",
        "detail": "### Detailed Analysis per Table",
        "table": "Table",
        "th_columns": "| Column | Description | Synonyms |",
        "sep_columns": "|--------|-------------|----------|",
    },
}


# ---------------------------------------------------------------------------
# Ergebnis-Dataclasses
# ---------------------------------------------------------------------------

@dataclass
class ColumnAnalysis:
    """Analyseergebnis für eine einzelne Spalte."""
    name: str
    has_description: bool
    has_synonyms: bool


@dataclass
class TableAnalysis:
    """Analyseergebnis für eine einzelne Tabelle."""
    name: str
    score_percent: float
    filled_count: int
    total_count: int
    ranking_label: str
    has_description: bool
    has_key_column: bool
    columns: list[ColumnAnalysis] = field(default_factory=list)


@dataclass
class EndorsementResult:
    """Vollständiges Endorsement-Analyseergebnis."""
    overall_score_percent: float
    overall_filled: int
    overall_total: int
    overall_ranking: str
    tables: list[TableAnalysis] = field(default_factory=list)


# ---------------------------------------------------------------------------
# Analyzer-Klasse
# ---------------------------------------------------------------------------

class EndorsementAnalyzer:
    """Analysiert die Metadaten-Vollständigkeit eines Power BI Semantic Models."""

    def __init__(self, language: str = "de") -> None:
        """Args:
            language: Anzeigesprache der Markdown-Ausgabe ("de" oder "en").
                Die internen Ranking-Labels bleiben kanonisch deutsch.
        """
        self._language = language if language in ("de", "en") else "de"

    def analyze(self, metadata: ReportMetadata) -> EndorsementResult:
        """Führt die vollständige Endorsement-Analyse auf extrahierten Metadaten durch.

        Nur nicht-versteckte Tabellen werden analysiert.
        Pro Tabelle werden 2 Tabellen-Properties (Beschreibung, Schlüsselspalte)
        und 2 Spalten-Properties pro Spalte (Beschreibung, Synonyme) geprüft.
        """
        table_analyses: list[TableAnalysis] = []
        overall_filled = 0
        overall_total = 0

        for table in metadata.tables:
            if table.is_hidden:
                continue

            filled_count = 0
            total_count = 0

            # Tabellen-Property 1: Beschreibung
            total_count += 1
            has_description = self.is_property_filled(table.description)
            if has_description:
                filled_count += 1

            # Tabellen-Property 2: Schlüsselspalte (mindestens eine Spalte mit is_key=True)
            total_count += 1
            has_key_column = any(col.is_key for col in table.columns)
            if has_key_column:
                filled_count += 1

            # Spalten-Properties
            column_analyses: list[ColumnAnalysis] = []
            for col in table.columns:
                # Spalten-Property 1: Beschreibung
                total_count += 1
                col_has_description = self.is_property_filled(col.description)
                if col_has_description:
                    filled_count += 1

                # Spalten-Property 2: Synonyme
                total_count += 1
                col_has_synonyms = self.is_property_filled(col.synonyms)
                if col_has_synonyms:
                    filled_count += 1

                column_analyses.append(ColumnAnalysis(
                    name=col.name,
                    has_description=col_has_description,
                    has_synonyms=col_has_synonyms,
                ))

            # Score für diese Tabelle berechnen
            if total_count == 0:
                score_percent = 0.0
            else:
                score_percent = (filled_count / total_count) * 100

            ranking_label = self.get_ranking_label(score_percent)

            table_analyses.append(TableAnalysis(
                name=table.name,
                score_percent=score_percent,
                filled_count=filled_count,
                total_count=total_count,
                ranking_label=ranking_label,
                has_description=has_description,
                has_key_column=has_key_column,
                columns=column_analyses,
            ))

            overall_filled += filled_count
            overall_total += total_count

        # Gesamtscore berechnen
        if overall_total == 0:
            overall_score_percent = 0.0
        else:
            overall_score_percent = (overall_filled / overall_total) * 100

        overall_ranking = self.get_ranking_label(overall_score_percent)

        return EndorsementResult(
            overall_score_percent=overall_score_percent,
            overall_filled=overall_filled,
            overall_total=overall_total,
            overall_ranking=overall_ranking,
            tables=table_analyses,
        )

    def format_markdown(self, result: EndorsementResult) -> str:
        """Formatiert das Analyseergebnis als Markdown-Kapitel.

        Erzeugt eine Gesamtübersicht als Markdown-Tabelle und eine
        Detailanalyse je Tabelle mit Spalten-Status-Markierungen.
        """
        lines: list[str] = []
        t = _FORMAT_T.get(self._language, _FORMAT_T["de"])
        ranking_display = _RANKING_DISPLAY.get(self._language, _RANKING_DISPLAY["de"])

        # Gesamtübersicht
        lines.append(t["overview"])
        lines.append("")
        lines.append(t["th_metric"])
        lines.append(t["sep_metric"])
        lines.append(f"| {t['score']} | {result.overall_score_percent:.0f}% |")
        lines.append(f"| {t['filled_total']} | {result.overall_filled} / {result.overall_total} |")
        lines.append(f"| {t['rating']} | {ranking_display.get(result.overall_ranking, result.overall_ranking)} |")
        lines.append("")

        # Detailanalyse je Tabelle
        if result.tables:
            lines.append(t["detail"])
            lines.append("")

            for table in result.tables:
                lines.append(
                    f"#### {t['table']}: {table.name} "
                    f"({table.score_percent:.0f}% – "
                    f"{table.filled_count}/{table.total_count} – "
                    f"{ranking_display.get(table.ranking_label, table.ranking_label)})"
                )
                lines.append("")

                if table.columns:
                    lines.append(t["th_columns"])
                    lines.append(t["sep_columns"])
                    for col in table.columns:
                        desc_mark = "✓" if col.has_description else "✗"
                        syn_mark = "✓" if col.has_synonyms else "✗"
                        lines.append(f"| {col.name} | {desc_mark} | {syn_mark} |")
                    lines.append("")

        return "\n".join(lines)

    @staticmethod
    def get_ranking_label(score_percent: float) -> str:
        """Ordnet einem Completeness-Score das passende Ranking-Label zu.

        Args:
            score_percent: Score in Prozent (0-100).

        Returns:
            Ranking-Label als String.
        """
        if score_percent >= RANKING_THRESHOLD_GUT:
            return RANKING_LABEL_GUT
        elif score_percent >= RANKING_THRESHOLD_GEPFLEGT:
            return RANKING_LABEL_GEPFLEGT
        elif score_percent >= RANKING_THRESHOLD_SCHLECHT:
            return RANKING_LABEL_SCHLECHT
        else:
            return RANKING_LABEL_NICHT

    @staticmethod
    def is_property_filled(value: Any) -> bool:
        """Prüft ob eine Metadaten-Eigenschaft als 'ausgefüllt' gilt.

        - Strings: nicht None und nicht leer nach strip()
        - Listen: nicht leer
        - None: nicht ausgefüllt

        Args:
            value: Der zu prüfende Wert.

        Returns:
            True wenn die Eigenschaft als ausgefüllt gilt.
        """
        if value is None:
            return False
        if isinstance(value, str):
            return len(value.strip()) > 0
        if isinstance(value, list):
            return len(value) > 0
        return False
