"""
P106 — MC-Audit-Type-Klassifizierung.

Zentrales Problem: viele Master-Controls pruefen Sachverhalte, die wir
von Aussen GAR NICHT pruefen koennen — z.B. ob das Unternehmen einen
internen Loeschkonzept-Prozess hat oder Schulungen durchgefuehrt wurden.

Bisher: alle MCs deren Pattern im Text nicht matched → FAIL.
Folge: GF-Mail mit 95 FAILs, davon ~60-70 in Wirklichkeit nur 'unknown'.

Loesung: pro MC klassifizieren:
* verifiable       → Pattern muss im sichtbaren Dokument stehen (Audit moeglich)
* process_internal → interner Prozess des Kunden (Schulung, AVV-Vertrag, …)
* doc_internal     → interne Dokumentation (VVT-Eintrag, DSFA-File, …)
* ambiguous        → koennte beides sein

In der MC-Auswertung:
* verifiable + Pattern fehlt → echtes FAIL ❌
* process_internal           → CHECK (Hinweis 'Bitte intern pruefen') ⓘ
* doc_internal               → CHECK (Hinweis 'Im VVT/DSFA dokumentiert?') ⓘ
* ambiguous                  → CHECK mit Warnung
"""

from __future__ import annotations

import logging
import re

logger = logging.getLogger(__name__)


# Patterns die auf interne Prozesse hindeuten (NICHT von aussen pruefbar)
_PROCESS_INTERNAL_PATTERNS = [
    # Schulung / Mitarbeiter
    r"\bmitarbeiter\b.*schul",
    r"\bschulung(en)?\b",
    r"\bawareness\b",
    r"\bsensibilisier",
    # Vertraege intern
    r"\bauftragsverarbeitungsvertrag\b",
    r"\bavv\b\s+abgeschlossen",
    r"\bvertrag.*abgeschlossen",
    r"\bdpa\s+(geschlossen|abgeschlossen|vorhanden)",
    r"\bscc\s+(geschlossen|abgeschlossen|implementiert)",
    # Technisch-organisatorische Massnahmen (intern). Lowercase: blob
    # ist bereits .lower(); Case-sensitive Patterns (TOM/AVV/SCC) matchen
    # nie. Daher hier explizit klein.
    r"\btechnisch[-\s]*organisatorische\s+ma(ß|ss)nahmen?\b",
    r"\btom\s+(umgesetzt|dokumentiert|implementiert)",
    r"\bverschluesselung\s+(implementiert|aktiv)",
    r"\bpseudonymisierung\s+(implementiert|aktiv)",
    r"\bbackup[s]?\s+(eingerichtet|vorhanden)",
    r"\bzugriffskontrolle",
    r"\b(rollen|berechtigungs)konzept",
    # Risikobewertung / DSFA (intern)
    r"\bdsfa\s+(durchgefuehrt|erstellt|dokumentiert)",
    r"\brisikobewertung\s+(durchgefuehrt|dokumentiert)",
    r"\brisikoanalyse",
    # Loeschkonzept / Aufbewahrung
    r"\bloeschkonzept\s+(umgesetzt|implementiert)",
    r"\baufbewahrungsfrist(en)?\s+(eingehalten|definiert)",
    r"\bloeschroutinen?\s+(aktiv|implementiert)",
    # Meldewege / Vorfallmanagement
    r"\bmeldepflicht\s+(eingehalten|umgesetzt)",
    r"\bvorfallmanagement",
    r"\bincident[\s-]?response",
    r"\b72[\s-]?stunden[\s-]?meldung",
    # Generische Prozess-Indikatoren
    r"\bdokumentiert\s+werden",
    r"\bbitte\s+(intern\s+)?dokumentieren",
    r"\bin\s+der\s+verfahrens",
    r"\bnach\s+innen\s+geh",
    # "Ausnahmen ... dokumentieren" — Wortabstand bis 60 Zeichen erlauben,
    # damit "ausnahmen bei bereits vorhandenen informationen dokumentieren"
    # matcht. Sonst Pattern zu eng → process-internal-MC bleibt FAIL.
    r"\bausnahmen\b[^\n]{0,60}\b(dokumentieren|protokollieren)\b",
    r"\bdokumentations[\s-]?pflicht",
    r"\bkostenfrei\s+(zur\s+verfuegung|gewaehren|ermoegli)",
    r"\bunentgeltlich\s+(zur\s+verfuegung)",
    # Vertragsleistung / Service-Level (intern)
    r"\bservice[\s-]?level",
    r"\breaktionszeit",
    # Auditierung / Aufsicht
    r"\binterne(s)?\s+audit",
    r"\baufsichtsbehoerde\s+gemeldet",
    r"\bbeauftragter\s+(intern|benannt)",
    # eCall + Branchen-spezifische interne Pflichten
    r"\babschaltung\s+der\s+\w+\s+kostenfrei",
    r"\bopt[\s-]?out\s+(intern|im\s+kundenportal)\s+ermoeglichen",
]

# Patterns die auf interne Dokumentation hindeuten (VVT, DSFA-Datei, …)
_DOC_INTERNAL_PATTERNS = [
    r"\bverzeichnis\s+der\s+verarbeitungstaetigkeiten\b",
    r"\bvvt(\s+|\b)",
    r"\bdsfa[\s-]?dokument",
    r"\bauftragsverarbeitungsverzeichnis",
    r"\bsub[\s-]?prozessor[\s-]?liste",
    r"\bverarbeitungs[\s-]?register",
    r"\binternes\s+register",
    r"\baufbewahrungs[\s-]?konzept\b",
]

# Patterns die auf externe Sichtbarkeit hindeuten → DEFINITIV verifiable
_VERIFIABLE_PATTERNS = [
    r"\bin\s+der\s+(datenschutzerklaerung|dse|cookie[\s-]?richtlinie|impressum|agb)\b",
    r"\bauf\s+der\s+website\s+(genannt|sichtbar|angegeben)",
    r"\bim\s+banner\s+(genannt|sichtbar)",
    r"\bim\s+cookie[\s-]?banner",
    r"\bauf\s+der\s+startseite",
    r"\bim\s+footer",
]


def _matches_any(text: str, patterns: list[str]) -> bool:
    tl = text.lower()
    for pat in patterns:
        try:
            if re.search(pat, tl):
                return True
        except re.error:
            continue
    return False


def classify_mc_audit_type(
    title: str | None,
    check_question: str | None = None,
    fail_criteria: dict | None = None,
) -> str:
    """Returns 'verifiable', 'process_internal', 'doc_internal',
    or 'ambiguous'."""
    blob = " ".join([title or "", check_question or "",
                      str(fail_criteria or "")])
    if not blob.strip():
        return "ambiguous"

    is_verifiable_hint = _matches_any(blob, _VERIFIABLE_PATTERNS)
    is_process = _matches_any(blob, _PROCESS_INTERNAL_PATTERNS)
    is_doc = _matches_any(blob, _DOC_INTERNAL_PATTERNS)

    # Wenn explicit Verifiable-Indikator + kein Process → verifiable
    if is_verifiable_hint and not (is_process or is_doc):
        return "verifiable"
    # Wenn Process oder Doc UND nicht Verifiable → intern
    if is_process and not is_verifiable_hint:
        return "process_internal"
    if is_doc and not is_verifiable_hint:
        return "doc_internal"
    # Beides → ambiguous, im Zweifel CHECK markieren
    if is_process or is_doc:
        return "ambiguous"
    return "verifiable"


def annotate_mc_results(check_results: list[dict]) -> list[dict]:
    """In-place: setzt mc_audit_type auf jeden MC-Check und ersetzt
    Status 'failed' durch 'check' wenn audit_type != verifiable."""
    if not check_results:
        return check_results
    n_reclassified = 0
    for r in check_results:
        if not isinstance(r, dict):
            continue
        if not (r.get("id") or "").startswith("mc-"):
            continue
        if "mc_audit_type" not in r:
            r["mc_audit_type"] = classify_mc_audit_type(
                r.get("label"), r.get("hint"), r.get("fail_criteria"),
            )
        # Wenn FAIL aber audit_type != verifiable → "check" (manuell)
        if (not r.get("passed")
                and not r.get("skipped")
                and r["mc_audit_type"] in (
                    "process_internal", "doc_internal", "ambiguous",
                )):
            r["audit_status"] = "check"  # NICHT failed
            n_reclassified += 1
        elif r.get("passed"):
            r["audit_status"] = "pass"
        elif r.get("skipped"):
            r["audit_status"] = "skip"
        else:
            r["audit_status"] = "fail"
    if n_reclassified:
        logger.info(
            "MC-Audit-Type: %d/%d MCs reklassifiziert von FAIL → CHECK "
            "(interne Pruefung erforderlich)",
            n_reclassified, len(check_results),
        )
    return check_results


def split_by_audit_type(check_results: list[dict]) -> dict[str, list[dict]]:
    """Liefert {verifiable_fails, internal_checks, passes, skips}."""
    out = {"verifiable_fails": [], "internal_checks": [],
            "passes": [], "skips": []}
    for r in (check_results or []):
        if not isinstance(r, dict):
            continue
        if not (r.get("id") or "").startswith("mc-"):
            continue
        status = r.get("audit_status")
        if status == "pass":
            out["passes"].append(r)
        elif status == "skip":
            out["skips"].append(r)
        elif status == "check":
            out["internal_checks"].append(r)
        elif status == "fail" or (not r.get("passed") and not r.get("skipped")):
            out["verifiable_fails"].append(r)
    return out


def build_internal_checks_block_html(
    internal_checks: list[dict],
    limit: int = 30,
) -> str:
    if not internal_checks:
        return ""
    by_type: dict[str, list[dict]] = {}
    for c in internal_checks:
        t = c.get("mc_audit_type", "ambiguous")
        by_type.setdefault(t, []).append(c)

    sections: list[str] = []
    labels = {
        "process_internal": ("Interne Prozesse — bitte beim DSB pruefen",
                              "#1e40af"),
        "doc_internal":    ("Interne Dokumentation — bitte im VVT/DSFA pruefen",
                              "#5b21b6"),
        "ambiguous":       ("Unklar ob Audit-Befund oder interne Pruefung",
                              "#92400e"),
    }
    for atype, (heading, color) in labels.items():
        items = by_type.get(atype) or []
        if not items:
            continue
        rows = "".join(
            f'<li style="margin-bottom:4px;font-size:11px;line-height:1.45">'
            f'<strong>{(c.get("label") or "")[:160]}</strong>'
            + (f' <span style="color:#94a3b8">({c.get("regulation") or "—"})</span>'
               if c.get("regulation") else '') +
            f'</li>'
            for c in items[:limit]
        )
        sections.append(
            f'<div style="margin-bottom:10px">'
            f'<div style="font-size:11px;color:{color};text-transform:uppercase;'
            f'letter-spacing:1px;font-weight:600;margin-bottom:4px">'
            f'{heading} ({len(items)})</div>'
            f'<ul style="margin:0 0 0 18px;padding:0">{rows}</ul>'
            f'</div>'
        )
    return (
        '<div style="font-family:-apple-system,BlinkMacSystemFont,sans-serif;'
        'max-width:760px;margin:0 auto 16px;padding:12px 16px;'
        'background:#f0f9ff;border:1px solid #bfdbfe;border-radius:8px">'
        '<div style="font-size:11px;color:#1e40af;text-transform:uppercase;'
        'letter-spacing:1.2px;margin-bottom:4px;font-weight:600">'
        'Pruefungen die wir von aussen NICHT durchfuehren koennen</div>'
        f'<h3 style="margin:0 0 6px;font-size:14px;color:#1e293b">'
        f'{len(internal_checks)} Pruefpunkt'
        f'{"e" if len(internal_checks) != 1 else ""} sind '
        'NUR intern beim Kunden zu pruefen</h3>'
        '<p style="margin:0 0 10px;font-size:11px;color:#475569;'
        'line-height:1.5">'
        'Diese Anforderungen koennen wir per externem Website-Audit nicht '
        'als erfuellt oder nicht-erfuellt bewerten — sie betreffen interne '
        'Prozesse (Schulungen, AVV-Vertraege, TOM-Doku) oder interne '
        'Dokumentation (VVT, DSFA, Loeschkonzept). Sie sind also <strong>kein '
        'Verstoss</strong>, sondern Hinweis-Checks fuer Ihren DSB.</p>'
        + "".join(sections) +
        '</div>'
    )
