"""Mail-V2 Action library — turn findings into 'what to do where'.

Each finding type maps to a concrete action recommendation. The
mapping is intentionally pattern-matched (not LLM-generated): the
audit is deterministic, so the corrective action must be too.

Patterns matched by:
  - finding `id` prefix (mc-impressum-handelsregister → impressum/HR)
  - severity_reason (factually_wrong / missing / misclassified)
  - mismatch_type (dsi_under_actual / table_under_actual / ...)
  - cookie field name (country / duration / processing_company)

Fallback: "Manuelle Prüfung beim DSB erforderlich" with finding hint.

Returns an Action dict:
  - title:        short imperative ("Sitzland ergänzen")
  - target:       where to fix ("DSE / Vendor-Liste")
  - detail:       extended explanation
  - aggregation_key: groupBy key for bulk recommendations
                     ("missing_country" / "long_retention" / ...)
  - effort:       "low" | "med" | "hi"
"""

from __future__ import annotations

from dataclasses import asdict, dataclass


@dataclass
class Action:
    title: str
    target: str
    detail: str
    aggregation_key: str | None
    effort: str  # low | med | hi

    def to_dict(self) -> dict:
        return asdict(self)


# ── Field-level actions for cookie inventory ──────────────────────

def cookie_field_missing_action(field: str, cookie_name: str,
                                vendor: str) -> Action | None:
    """Return action when a cookie field is missing (or unknown)."""
    if field == "country":
        return Action(
            title="Sitzland ergänzen",
            target="DSE / Vendor-Tabelle",
            detail=(f"Für Cookie '{cookie_name}' (Vendor {vendor or '—'}) "
                    "ist kein Sitzland der verarbeitenden Stelle angegeben. "
                    "Art. 13 Abs. 1 lit. a DSGVO verlangt die Identität + "
                    "Anschrift des Verantwortlichen."),
            aggregation_key="missing_country",
            effort="low",
        )
    if field == "duration":
        return Action(
            title="Speicherdauer angeben",
            target="DSE / Cookie-Tabelle",
            detail=(f"Cookie '{cookie_name}' hat keine deklarierte "
                    "Speicherdauer. Art. 13 Abs. 2 lit. a DSGVO verlangt "
                    "die Dauer der Speicherung oder ein Kriterium dafür."),
            aggregation_key="missing_duration",
            effort="low",
        )
    if field == "retention_grounds":
        return Action(
            title="Löschfrist + Rechtsgrundlage angeben",
            target="Löschkonzept + DSE",
            detail=(f"Für Cookie '{cookie_name}' fehlt eine konkrete "
                    "Löschfrist. § 35 BDSG + DSK-Standard verlangen ein "
                    "dokumentiertes Löschkonzept pro Datenkategorie."),
            aggregation_key="missing_retention",
            effort="med",
        )
    if field == "processing_company":
        return Action(
            title="Verantwortliche Stelle nennen",
            target="DSE",
            detail=(f"Cookie '{cookie_name}' nennt keinen Verantwortlichen "
                    "(Firma + Adresse). Art. 13 Abs. 1 DSGVO Pflichtangabe."),
            aggregation_key="missing_processing_company",
            effort="low",
        )
    if field == "third_country":
        return Action(
            title="Drittlandtransfer absichern",
            target="DSE + AVV-Anhang",
            detail=(f"Cookie '{cookie_name}' (Vendor {vendor or '—'}) "
                    "verarbeitet Daten außerhalb EU/EWR. Erforderlich: "
                    "Angemessenheitsbeschluss, Standardvertragsklauseln "
                    "oder ausdrückliche Einwilligung (Art. 44 ff. DSGVO)."),
            aggregation_key="missing_third_country",
            effort="med",
        )
    if field == "category":
        return Action(
            title="Kategorie zuordnen",
            target="Cookie-Tabelle",
            detail=(f"Cookie '{cookie_name}' hat keine Kategorie. EDPB "
                    "Cookie-Sweep verlangt: technisch notwendig / "
                    "Statistik / Marketing / Externe Medien."),
            aggregation_key="missing_category",
            effort="low",
        )
    return None


# ── Status-level actions (UNDOC / ORPH / MISMATCH) ───────────────

def cookie_status_action(status_code: str, cookie_name: str,
                         vendor: str) -> Action | None:
    if status_code == "UNDOC":
        return Action(
            title="Cookie deklarieren oder entfernen",
            target="CMP-Config + DSE",
            detail=(f"Cookie '{cookie_name}' wird im Browser gesetzt, ist "
                    "aber nicht in DSE/Cookie-Tabelle deklariert. § 25 "
                    "TDDDG: entweder Deklaration nachholen oder Cookie "
                    "blockieren (CMP-Trigger prüfen)."),
            aggregation_key="undoc_cookies",
            effort="med",
        )
    if status_code == "ORPH":
        return Action(
            title="Veraltete Cookie-Angabe entfernen",
            target="DSE / Cookie-Tabelle",
            detail=(f"Cookie '{cookie_name}' ist in DSE deklariert, wird "
                    "aber im Live-Browser nicht gesetzt. Veraltete Angabe "
                    "entfernen, um Transparenz zu wahren."),
            aggregation_key="orphan_cookies",
            effort="low",
        )
    if status_code == "MISMATCH":
        return Action(
            title="Cookie-Werte korrigieren",
            target="DSE / Cookie-Tabelle",
            detail=(f"Cookie '{cookie_name}': deklarierte Werte weichen von "
                    "tatsächlich gesetzten ab. Tabelle anpassen oder "
                    "Cookie-Setup korrigieren."),
            aggregation_key="mismatch_cookies",
            effort="med",
        )
    return None


# ── Retention-comparison actions ─────────────────────────────────

def retention_action(retention_finding: dict) -> Action | None:
    mt = retention_finding.get("mismatch_type")
    cookie = retention_finding.get("cookie_name", "—")
    if mt == "dsi_under_actual":
        return Action(
            title="DSE-Speicherdauer korrigieren",
            target="DSE",
            detail=(f"DSE behauptet für '{cookie}' kürzere Speicherdauer als "
                    "real. Wert in DSE auf reale Dauer anpassen ODER Cookie-"
                    "Setup auf deklarierte Dauer reduzieren."),
            aggregation_key="dsi_too_short",
            effort="low",
        )
    if mt == "table_under_actual":
        return Action(
            title="Cookie-Tabelle korrigieren",
            target="Cookie-Tabelle / CMP",
            detail=(f"Cookie-Tabelle behauptet für '{cookie}' kürzere Dauer "
                    "als real. Wert anpassen oder Cookie-Lifetime reduzieren."),
            aggregation_key="table_too_short",
            effort="low",
        )
    if mt == "dsi_vs_table":
        return Action(
            title="DSE und Cookie-Tabelle synchronisieren",
            target="DSE + Cookie-Tabelle",
            detail=(f"DSE und Cookie-Tabelle geben unterschiedliche Werte "
                    f"für '{cookie}' an. Werte abgleichen."),
            aggregation_key="dsi_table_mismatch",
            effort="low",
        )
    if mt == "actual_under_table":
        return Action(
            title="Speicherdauer-Cap dokumentieren (Safari-ITP)",
            target="DSE",
            detail=(f"Cookie '{cookie}' lebt real kürzer als deklariert — "
                    "wahrscheinlich Safari ITP 7-Tage-Cap. In DSE ergänzen: "
                    "'Auf Safari-Geräten kann die Speicherdauer durch ITP "
                    "verkürzt werden.'"),
            aggregation_key="safari_itp",
            effort="low",
        )
    return None


# ── Reachability actions (B1) ────────────────────────────────────

def reachability_action(rb1: dict) -> Action | None:
    if rb1.get("passed"):
        return None
    reason = rb1.get("severity_reason")
    if reason == "missing":
        return Action(
            title="Cookie-Einstellungen-Link im Footer ergänzen",
            target="Website-Footer (alle Seiten)",
            detail=("Art. 7 Abs. 3 DSGVO: Widerruf muss so einfach wie "
                    "Erteilung sein. Footer-Link 'Cookie-Einstellungen' "
                    "ergänzen, der den CMP direkt öffnet (kein neuer Tab, "
                    "kein Zwischendokument)."),
            aggregation_key="footer_reachability",
            effort="low",
        )
    if reason == "misclassified":
        return Action(
            title="CMP direkt öffnen statt neuer Tab",
            target="Footer-Link-Config",
            detail=("Bestehender Footer-Link öffnet die CMP nicht direkt. "
                    "JavaScript-Trigger umstellen: kein target=_blank, "
                    "keine externe Policy-Seite — CMP-Layer direkt öffnen."),
            aggregation_key="footer_reachability",
            effort="low",
        )
    if reason == "factually_wrong":
        return Action(
            title="Eigenen CMP statt Browser-Verweis",
            target="Footer + CMP",
            detail=("Nutzer wird auf Browser-Einstellungen verwiesen — das "
                    "ist nach LfDI BW kein gleichwertiger Widerruf. Eigenen "
                    "CMP-Re-Open-Mechanismus implementieren."),
            aggregation_key="footer_reachability",
            effort="med",
        )
    return None


# ── Generic finding → action ────────────────────────────────────

_ID_PATTERNS = {
    "handelsregister": ("HR-Eintrag im Impressum ergänzen",
                        "Impressum",
                        "§ 5 Abs. 1 Nr. 4 TMG: Registereintrag mit "
                        "Registergericht + HR-Nr."),
    "ust-id": ("USt-IdNr. ergänzen",
               "Impressum",
               "§ 5 Abs. 1 Nr. 6 TMG: USt-IdNr. falls vorhanden."),
    "vertretungsberechtig": ("Vertretungsberechtigte Person nennen",
                              "Impressum",
                              "§ 5 Abs. 1 Nr. 1 TMG"),
    "aufsichtsbehoerde": ("Aufsichtsbehörde nennen",
                          "Impressum",
                          "§ 5 Abs. 1 Nr. 3 TMG (regulierte Branchen)"),
    "berufsordnung": ("Berufsrechtliche Angaben ergänzen",
                      "Impressum",
                      "§ 5 Abs. 1 Nr. 5 TMG"),
    "dsb": ("DSB benennen",
            "DSE",
            "Art. 37 ff. DSGVO: Datenschutzbeauftragten benennen + DSE "
            "ergänzen."),
    "odr": ("OS-Link auf EU-Plattform ergänzen",
            "Impressum / AGB",
            "Art. 14 EU-VO 524/2013 (B2C-Onlineshop)"),
    "widerrufsbelehrung": ("Widerrufsbelehrung anpassen",
                           "Widerruf-Dokument",
                           "§ 312g BGB + Art. 246a EGBGB Muster-Widerrufs-"
                           "belehrung."),
}


def derive_generic_action(finding_id: str, label: str,
                          hint: str) -> Action | None:
    """Pattern-match a generic MC finding ID to an action template."""
    fid = (finding_id or "").lower()
    haystack = f"{fid} {label.lower()}"
    for kw, (title, target, detail) in _ID_PATTERNS.items():
        if kw in haystack:
            return Action(
                title=title,
                target=target,
                detail=detail + (f" Hinweis: {hint[:200]}" if hint else ""),
                aggregation_key=f"mc_{kw}",
                effort="low",
            )
    if hint:
        return Action(
            title="Manuelle Prüfung beim DSB",
            target=label or "Doc",
            detail=hint[:400],
            aggregation_key=None,
            effort="med",
        )
    return None


def action_for_finding(finding_id: str, severity: str, label: str,
                      hint: str) -> Action | None:
    """Top-level entry point for MC findings."""
    return derive_generic_action(finding_id, label, hint)
