"""AI-Act Art. 50 Transparenzpflicht-Check (vereinfacht).

Art. 50 AI Act verlangt, dass Nutzer beim Interagieren mit einem
KI-System (Chatbot, Sprachassistent etc.) erkennen können, dass sie
mit einer KI sprechen — es sei denn, das ist offensichtlich aus dem
Kontext heraus.

Der Check ist heuristisch (kein LLM) und prüft drei Schichten:

  1. AI-Provider-Detection in DSE und Vendor-Liste
     (Vertex AI, OpenAI, Anthropic, etc.)
  2. Disclosure-Text-Detection in DSE / Cookie-Doc
     ("KI-System", "Sie chatten mit einer KI", "automatisiert",
      "Artificial Intelligence", "Konversations-KI", "GPT", …)
  3. Cross-Check: AI-Provider gefunden + keine Disclosure → HIGH
     AI-Provider gefunden + Disclosure vorhanden, aber kein "Sie
     interagieren mit einer KI"-Hinweis → MEDIUM (Pre-Chat-Hinweis
     vor erstem Input gefordert; kann nur ein consent-tester-DOM-Scan
     verifizieren)

Bekannte Limitation: ohne consent-tester-Erweiterung kann der Check
nicht entscheiden, ob ein Pre-Chat-Hinweis im Live-DOM vor dem
ersten Nutzer-Input erscheint. Wir flaggen das daher als MEDIUM
"manuell verifizieren".
"""

from __future__ import annotations

import logging
import re

logger = logging.getLogger(__name__)


# AI-Anbieter / Modelle / Frameworks, die "AI" auslösen.
_AI_KEYWORDS = (
    "vertex ai", "google vertex", "openai", "gpt-3", "gpt-4", "chatgpt",
    "anthropic", "claude.ai", "claude-3", "mistral ai", "huggingface",
    "hugging face", "stable diffusion", "midjourney", "llama-2", "llama-3",
    "qwen", "deepseek", "perplexity ai", "azure openai", "copilot",
    "konversations-ki", "konversations ki",
    "ai assistant", "ai-assistant", "ki-assistent", "ki assistent",
    "intelligenter assistent",
    "automatisierter chat", "chatbot", "live-chat",
)


# Phrasen, die als Art-50-Disclosure gelten.
_DISCLOSURE_PHRASES = (
    "sie chatten mit einer ki", "sie kommunizieren mit einer ki",
    "automatisierter chat", "automatisierter assistent",
    "ki-gestützter", "ki gestützt", "ki-gestuetzt",
    "künstliche intelligenz", "kuenstliche intelligenz",
    "artificial intelligence",
    "art. 50 ai act", "ai act art. 50", "art. 50 ki-vo",
    "ki-verordnung", "ki verordnung",
    "automatisiertes system",
    "generativ", "generative ki", "generative ai",
    "large language model", "llm-",
    "machine learning",
)


def _has_any(text: str, phrases) -> list[str]:
    text_lc = (text or "").lower()
    if not text_lc:
        return []
    return [p for p in phrases if p in text_lc]


def _find_ai_in_vendors(cmp_vendors: list[dict]) -> list[dict]:
    """Find vendors whose name or category mentions an AI provider."""
    hits: list[dict] = []
    for v in cmp_vendors or []:
        haystack = " ".join([
            (v.get("name") or "").lower(),
            (v.get("category") or "").lower(),
            (v.get("processing_company") or "").lower(),
        ])
        if not haystack.strip():
            continue
        matched = [k for k in _AI_KEYWORDS if k in haystack]
        if matched:
            hits.append({
                "vendor": v.get("name") or "—",
                "matched": matched[:3],
            })
    return hits


def check_ai_act_transparency(state: dict) -> list[dict]:
    """Return findings about AI Art. 50 transparency obligations."""
    doc_texts = state.get("doc_texts") or {}
    dse_text = doc_texts.get("dse") or ""
    cookie_text = doc_texts.get("cookie") or ""
    cmp_vendors = state.get("cmp_vendors") or []

    if not dse_text and not cookie_text and not cmp_vendors:
        return []

    ai_mentions_dse = _has_any(dse_text, _AI_KEYWORDS)
    ai_mentions_cookie = _has_any(cookie_text, _AI_KEYWORDS)
    ai_vendors = _find_ai_in_vendors(cmp_vendors)

    has_ai_signal = bool(
        ai_mentions_dse or ai_mentions_cookie or ai_vendors
    )
    if not has_ai_signal:
        return []

    disc_dse = _has_any(dse_text, _DISCLOSURE_PHRASES)
    disc_cookie = _has_any(cookie_text, _DISCLOSURE_PHRASES)
    has_disclosure = bool(disc_dse or disc_cookie)

    findings: list[dict] = []
    summary_signals = (
        f"DSE-AI-Hinweise: {len(ai_mentions_dse)} "
        f"(z.B. {', '.join(ai_mentions_dse[:3])}); "
        f"Cookie-AI-Hinweise: {len(ai_mentions_cookie)}; "
        f"AI-Vendors: {len(ai_vendors)}"
    )

    if not has_disclosure:
        findings.append({
            "check_id": "AI-ACT-TRANSPARENCY-001",
            "severity": "HIGH",
            "severity_reason": "missing",
            "title": (
                "AI-Act Art. 50 Transparenz-Hinweis fehlt — "
                "KI-System eingesetzt, aber keine Nutzer-Erklärung"
            ),
            "norm": "AI Act Art. 50 Abs. 1 (Transparenz gegenüber Nutzern)",
            "detected_signals": summary_signals,
            "ai_vendors": ai_vendors,
            "ai_keywords_in_dse": ai_mentions_dse[:5],
            "ai_keywords_in_cookie": ai_mentions_cookie[:5],
            "action": (
                "DSE und Pre-Chat-UI mit ausdrücklichem Hinweis "
                "'Sie kommunizieren mit einer KI (System X)' ergänzen. "
                "Anbieter offen nennen + Rechtsgrundlage + Speicherdauer."
            ),
        })
    else:
        # AI detected + DSE-Disclosure vorhanden — aber Pre-Chat-Hinweis
        # im Live-DOM kann der Check nicht verifizieren.
        findings.append({
            "check_id": "AI-ACT-TRANSPARENCY-002",
            "severity": "MEDIUM",
            "severity_reason": "manual_review_required",
            "title": (
                "AI-Act Art. 50: DSE-Disclosure vorhanden — Pre-Chat-Hinweis "
                "im UI manuell verifizieren"
            ),
            "norm": "AI Act Art. 50 Abs. 1",
            "detected_signals": summary_signals,
            "ai_vendors": ai_vendors,
            "disclosure_in_dse": disc_dse[:3],
            "disclosure_in_cookie": disc_cookie[:3],
            "action": (
                "Pre-Chat-UI öffnen: vor der ersten Nutzereingabe muss "
                "ein klarer Hinweis erscheinen, dass die Konversation "
                "mit einer KI geführt wird. Verifizieren ob Banner/Modal "
                "vorhanden oder reine Footnote."
            ),
        })

    # Zusatzcheck: Wenn AI vorhanden und Rechtsgrundlage = berechtigtes
    # Interesse (Art. 6 Abs. 1 lit. f) statt Einwilligung — MEDIUM
    if ai_vendors or ai_mentions_dse:
        if _legitimate_interest_for_ai(dse_text):
            findings.append({
                "check_id": "AI-ACT-RISK-001",
                "severity": "MEDIUM",
                "severity_reason": "misclassified",
                "title": (
                    "Rechtsgrundlage 'berechtigtes Interesse' für "
                    "KI-Verarbeitung — Einwilligung empfehlen"
                ),
                "norm": "DSGVO Art. 6 Abs. 1 lit. a vs lit. f + AI Act",
                "detected_signals": (
                    "AI-Provider erkannt; Art. 6 Abs. 1 lit. f als "
                    "Rechtsgrundlage in DSE genannt"
                ),
                "ai_vendors": ai_vendors,
                "action": (
                    "Bei generativer KI (insbesondere mit Drittland-"
                    "Transfer und Profiling-Verwandtschaft) "
                    "Rechtsgrundlage auf Einwilligung (Art. 6 Abs. 1 "
                    "lit. a) umstellen. Interessenabwägung dokumentieren."
                ),
            })

    if findings:
        logger.info("ai-act-transparency: %d findings", len(findings))
    return findings


def _legitimate_interest_for_ai(dse_text: str) -> bool:
    """Detect 'Rechtsgrundlage Art. 6 Abs. 1 lit. f' near AI mentions."""
    text_lc = (dse_text or "").lower()
    if not text_lc:
        return False
    # crude proximity check: any of the AI keywords AND lit-f phrase
    # within a ~600 char window
    import re
    lit_f_patterns = (
        "art. 6 abs. 1 lit. f", "artikel 6 abs. 1 lit. f",
        "art. 6 1 f", "berechtigtes interesse", "berechtigten interesses",
    )
    for ai_kw in _AI_KEYWORDS:
        for pos in range(0, len(text_lc) - 200):
            window = text_lc[max(0, pos-300):pos+300]
            if ai_kw in window and any(p in window for p in lit_f_patterns):
                return True
            # don't walk every char; jump to next ai_kw occurrence
            idx = text_lc.find(ai_kw, pos)
            if idx == -1:
                break
            pos = idx + 1
            break
    return False
