"""Impressum-Specialist-Agent Phase 2 — LLM-gestützt.

Komplementiert den Pattern-Match-Agent (impressum_agent.py) durch
eine LLM-Pass. Beide Output-Formate sind identisch, sodass das B-Wiring
beide kombinieren / dedupen kann.

LLM-Setup:
  - Modell: qwen3:30b-a3b (Standard Ollama, siehe Plausibility-Check)
  - System-Prompt: KB der § 5 TMG Pflichtangaben
  - User-Prompt: Impressum-Text + business_scope-Hinweis
  - Output: JSON-Liste mit {field_id, severity, hint, evidence}

Phase-2-Ziel: schwer-mit-Regex-erfassbare Lücken finden, z.B.
  - "Geschäftsführer" wird genannt aber ohne Vor- oder Nachname
  - Aufsichtsbehörde-Pflicht erkannt, aber für falsche Branche
  - Vertretungsberechtigte einer GmbH bei mehreren Personen unvollständig
"""

from __future__ import annotations

import json
import logging
import os
import re

import httpx

logger = logging.getLogger(__name__)

OLLAMA_URL = os.environ.get(
    "OLLAMA_URL", "http://bp-core-ollama:11434",
)
MODEL = os.environ.get("IMPRESSUM_AGENT_MODEL", "qwen3:30b-a3b")
TIMEOUT = float(os.environ.get("IMPRESSUM_AGENT_TIMEOUT", "60"))


_SYSTEM_PROMPT = """Du bist ein deutscher Datenschutz-Anwalt mit Fokus
§ 5 TMG / DDG (Anbieterkennzeichnung). Deine Aufgabe: einen Impressum-
Text auf Vollständigkeit der Pflichtangaben prüfen und Lücken /
Mängel strukturiert auflisten.

Pflichtangaben nach § 5 TMG (Standard):
  - Anbieter-Name + Anschrift (juristische Person: Firma + Sitz)
  - Vertretungsberechtigte (bei juristischen Personen: ALLE Geschäftsführer
    mit Vor- und Nachname)
  - E-Mail UND Telefon (Schnelle elektronische Kontaktaufnahme + UNMITTELBAR)
  - Handelsregister-Eintrag (HRB/HRA + Registergericht)
  - USt-IdNr. (falls vorhanden — DE\\d{9})
  - Bei B2C/Onlineshop: Verbraucherschlichtung + OS-Plattform
  - Bei reglementiertem Beruf: Berufsbezeichnung + Kammer
  - Bei genehmigungspflichtigen Tätigkeiten: Aufsichtsbehörde

Ausgabe: NUR gültiges JSON mit Feld "findings", jedes Element:
  {
    "field_id": "kurzer-id",
    "severity": "HIGH"|"MEDIUM"|"LOW",
    "title": "kurze Lücken-Beschreibung",
    "evidence": "wörtliches Zitat aus dem Impressum, das das Problem belegt",
    "action": "konkrete Empfehlung"
  }

Keine Erklärung außerhalb JSON. Keine Prosa. Wenn alles vollständig:
gib {"findings": []} zurück.
"""


def _user_prompt(impressum_text: str,
                  business_scope: set[str] | None) -> str:
    scope_hint = ""
    if business_scope:
        scope_hint = (
            f"BUSINESS-SCOPE-HINTS: "
            f"{', '.join(sorted(business_scope))}\n\n"
        )
    return (
        f"{scope_hint}"
        f"IMPRESSUM-TEXT:\n"
        f"{impressum_text[:4000]}\n\n"
        "Liste Lücken nach § 5 TMG. Nur JSON."
    )


def _parse_response(content: str) -> list[dict]:
    """Robust JSON extraction (handles ```json fences, prose-wrap)."""
    if not content:
        return []
    s = content.strip()
    if s.startswith("```"):
        s = s.strip("`")
        if s.lower().startswith("json"):
            s = s[4:]
        s = s.strip()
    first = s.find("{")
    last = s.rfind("}")
    if first >= 0 and last > first:
        s = s[first:last + 1]
    try:
        data = json.loads(s)
    except Exception:
        # Try array directly
        first = content.find("[")
        last = content.rfind("]")
        if first >= 0 and last > first:
            try:
                arr = json.loads(content[first:last + 1])
                return arr if isinstance(arr, list) else []
            except Exception:
                return []
        return []
    findings = data.get("findings") if isinstance(data, dict) else data
    return findings if isinstance(findings, list) else []


async def evaluate_llm(
    impressum_text: str,
    business_scope: set[str] | None = None,
) -> list[dict]:
    """LLM-gestützte Impressum-Analyse. Returns finding dicts in the
    same shape as impressum_agent.evaluate() so callers can merge."""
    if not impressum_text or len(impressum_text.strip()) < 100:
        return []
    body = {
        "model": MODEL,
        "messages": [
            {"role": "system", "content": _SYSTEM_PROMPT},
            {"role": "user", "content": _user_prompt(
                impressum_text, business_scope,
            )},
        ],
        "format": "json",
        "stream": False,
        "options": {"temperature": 0.0, "seed": 42, "num_predict": 1200},
    }
    try:
        async with httpx.AsyncClient(timeout=TIMEOUT) as c:
            r = await c.post(f"{OLLAMA_URL}/api/chat", json=body)
            r.raise_for_status()
            content = (r.json().get("message") or {}).get("content", "") or ""
    except Exception as e:
        logger.warning("impressum_agent_llm call failed: %s", e)
        return []

    raw_findings = _parse_response(content)
    out: list[dict] = []
    for f in raw_findings:
        if not isinstance(f, dict):
            continue
        fid = re.sub(r"[^\w\-]", "_",
                     str(f.get("field_id") or "unknown"))[:40]
        sev = (f.get("severity") or "MEDIUM").upper()
        if sev not in ("HIGH", "MEDIUM", "LOW", "INFO"):
            sev = "MEDIUM"
        out.append({
            "check_id": f"IMPRESSUM-AGENT-LLM-{fid.upper()}",
            "agent": "impressum_agent_v2_llm",
            "field_id": fid,
            "severity": sev,
            "severity_reason": "missing",
            "title": str(f.get("title") or "")[:200],
            "norm": "§ 5 TMG / DDG (LLM-Analyse)",
            "evidence": str(f.get("evidence") or "")[:300],
            "action": str(f.get("action") or "")[:400],
        })
    if out:
        logger.info("impressum_agent_llm: %d finding(s)", len(out))
    return out
