"""Layer-0 Regex-Boost für den DSE-Agent — die kuratierten Art-13/14-Patterns
als deterministische Vor-Stufe vor dem Keyword-Match aus doc_check_controls.

Analog zu impressum/regex_boost.py + cookie_policy/regex_boost.py:
  - run_v3_pipeline lädt die 267 text-MCs (doc_type='dse') und macht
    Keyword-Match aus deren pass_criteria.
  - MEIN Beitrag (Layer 0): die präzisen Art-13-Patterns (mcs.py / aus
    ART13_CHECKLIST) laufen ZUERST. Trifft ein Pattern → das thematisch
    passende DB-MC wird zu PASS geboostet (auch wenn der Keyword-Match unklar
    war). Mapping: field_id → typische Wörter in der pass_criteria der DB-MC.

Damit gehen die kuratierten DSE-Patterns nicht verloren, sondern boosten das
DB-Control-System (statt es zu ersetzen).
"""

from __future__ import annotations

import logging

from .mcs import MCS, scope_matches

logger = logging.getLogger(__name__)


# field_id (aus ART13_CHECKLIST) → Wörter, wie sie in der pass_criteria der
# zugehörigen DSE-DB-MCs vorkommen. Treffen ≥2 dieser Wörter in den criteria
# eines DB-MC, gehört es zu diesem Feld → Boost. Die Vokabeln sind an den
# real beobachteten DB-Kriterien ausgerichtet (DATA/SEC/AUTH-Controls).
BOOST_KEYWORDS: dict[str, tuple[str, ...]] = {
    "controller": (
        "verantwortlich", "verantwortliche stelle", "verantwortlichen",
        "kontaktdaten des verantwortlichen", "name und kontaktdaten",
        "firmenname", "rechtsform", "anschrift", "ladungsfähige",
        "identität des verantwortlichen", "controller",
    ),
    "dpo": (
        "datenschutzbeauftragt", "datenschutzbeauftragter",
        "datenschutzbeauftragte", "data protection officer",
        "kontaktdaten des datenschutz", "benennung", "art. 37",
    ),
    "purposes": (
        "zweck", "zwecke", "verarbeitungszweck", "zweck der verarbeitung",
        "zwecke der verarbeitung", "verarbeitungstätigkeit", "purpose",
        "zweckbindung", "erhebung",
    ),
    "legal_basis": (
        "rechtsgrundlage", "berechtigte interesse", "berechtigtes interesse",
        "einwilligung", "vertragserfüllung", "vertragserfuellung",
        "interessenabwägung", "interessenabwaegung", "art. 6",
        "rechtmäßigkeit", "rechtmaessigkeit", "erforderlich",
    ),
    "recipients": (
        "empfänger", "empfaenger", "empfängerkategorien",
        "empfaengerkategorien", "weitergabe", "übermittlung an",
        "auftragsverarbeit", "auftragsverarbeiter", "dienstleister",
        "dritte", "drittempfänger", "kategorien von empfängern",
    ),
    "third_country": (
        "drittland", "drittstaat", "drittländer", "drittlaender",
        "standardvertragsklausel", "angemessenheitsbeschluss",
        "übermittlung in ein drittland", "geeignete garantien",
        "schutzgarantien", "data privacy framework", "ewr",
        "internationale übermittlung", "transfermechanismus",
    ),
    "third_country_mechanism": (
        "standardvertragsklausel", "angemessenheitsbeschluss",
        "geeignete garantien", "data privacy framework",
        "schutzgarantien", "art. 46", "art. 45",
    ),
    "retention": (
        "speicherdauer", "aufbewahrungsfrist", "aufbewahrungsdauer",
        "löschfrist", "loeschfrist", "speicherbegrenzung", "löschung",
        "loeschung", "dauer der speicherung", "kriterien für die festlegung",
        "speicherfrist", "aufbewahrung",
    ),
    "retention_periods": (
        "aufbewahrungsfrist", "löschfrist", "speicherdauer",
        "gesetzliche frist", "handelsrechtlich", "steuerrechtlich",
    ),
    "rights": (
        "betroffenenrecht", "betroffenenrechte", "recht auf auskunft",
        "auskunft", "berichtigung", "löschung", "loeschung",
        "einschränkung", "einschraenkung", "datenübertragbarkeit",
        "datenuebertragbarkeit", "widerspruch", "widerruf",
        "rechte der betroffenen", "art. 15", "art. 17", "art. 21",
    ),
    "complaint": (
        "beschwerderecht", "aufsichtsbehörde", "aufsichtsbehoerde",
        "datenschutzbehörde", "datenschutzbehoerde", "beschwerde",
        "recht auf beschwerde", "art. 77", "zuständige aufsichtsbehörde",
    ),
    "rights_art22_profiling": (
        "automatisierte entscheidung", "profiling", "art. 22",
        "automatisierte einzelentscheidung", "scoring",
    ),
    "dse_version_date": (
        "stand", "letzte aktualisierung", "zuletzt geändert",
        "gültig ab", "gueltig ab", "version", "versionsdatum",
        "aktualität", "nachweisbarkeit",
    ),
}


def compute_regex_boosts(text: str, business_scope: set[str] | None = None) -> set[str]:
    """Welche DSE-field_ids haben die kuratierten Patterns erkannt?

    Returns die Menge gehit'ter field_ids, über die später entschieden wird,
    ob ein DB-MC darüber automatisch passed werden kann. business_scope wird
    akzeptiert (Signatur-Parität mit impressum), für DSE aber nicht gegated —
    Art-13-Pflichten sind universell.
    """
    if not text or len(text) < 50:
        return set()
    scope = business_scope or set()
    hits: set[str] = set()
    for mc in MCS:
        if not scope_matches(mc, scope):
            continue
        if any(p.search(text) for p in mc.patterns):
            hits.add(mc.field_id)
    return hits


def boost_matches_db_mc(
    boosts: set[str],
    pass_criteria: list,
    fail_criteria: list | None = None,
) -> str | None:
    """Hat ein gebooster field_id ≥2 Keyword-Überlapp mit den pass/fail_criteria
    eines DB-MC? Returns field_id (höchster Match-Count) oder None."""
    if not boosts:
        return None
    crit_parts: list[str] = []
    for c in (pass_criteria or []):
        if c:
            crit_parts.append(str(c).lower())
    for c in (fail_criteria or []):
        if c:
            crit_parts.append(str(c).lower())
    if not crit_parts:
        return None
    crit_text = " ".join(crit_parts)
    best: tuple[int, str] | None = None
    for field_id in boosts:
        kws = BOOST_KEYWORDS.get(field_id) or ()
        match_count = sum(1 for kw in kws if kw in crit_text)
        if match_count >= 2:
            if best is None or match_count > best[0]:
                best = (match_count, field_id)
    return best[1] if best else None


def criteria_on_topic(
    pass_criteria: list | None,
    fail_criteria: list | None = None,
    min_hits: int = 2,
) -> bool:
    """Deterministischer Themen-Gate: gehört ein DB-MC überhaupt ins DSE-
    Themenfeld (Art 13/14)? ≥min_hits unterschiedliche Schlüsselwörter aus
    IRGENDEINEM DSE-Feld in den kombinierten criteria. Fängt fremd-getaggte
    MCs ab. Leere Kriterien → on-topic behalten (konservativ)."""
    crit_parts: list[str] = []
    for c in (pass_criteria or []):
        if c:
            crit_parts.append(str(c).lower())
    for c in (fail_criteria or []):
        if c:
            crit_parts.append(str(c).lower())
    if not crit_parts:
        return True
    crit_text = " ".join(crit_parts)
    hits: set[str] = set()
    for kws in BOOST_KEYWORDS.values():
        for kw in kws:
            if kw in crit_text:
                hits.add(kw)
                if len(hits) >= min_hits:
                    return True
    return False
