#!/usr/bin/env python3
"""aria-llm-router — Multi-LLM Routing-Heuristik + Privacy-Tier-Classifier (KAR-124 Stub).

Implementiert die 4-Dimensions-Routing-Logik aus Kais' Architektur-Doc:
1. Privacy-Tier (überschreibt alles)
2. Task-Klasse (Planning → Opus, Math → GPT, Bulk → DeepSeek, Vision → Gemini)
3. Mechanical-Validate-First (extern aufgerufen)
4. Judge ≠ Worker-Familie

Phase-1-Stub:
- KAR-124 Privacy-Classifier: heuristisch via Regex/Keyword. Phase 1: setzt Tag,
  routet NICHT (kein lokales Modell verfügbar). Nur Telegram-Notice bei sovereign.
- KAR-121-Routing: returnt Worker+Judge-Family-Tupel, ohne real Adapter-Call.
  KAR-121 echtes Provider-Adapter kommt separat sobald API-Keys da.

CLI:
   python3 aria-llm-router.py classify "<input text>"
   python3 aria-llm-router.py route --task-class plan --privacy-tier auto "<input>"

Library:
   from aria_llm_router import classify_privacy, choose_worker, choose_judge_family
"""
from __future__ import annotations

import argparse
import dataclasses
import json
import re
import sys
from typing import Optional

# ---- Privacy-Classifier (KAR-124 Stub) --------------------------------------

# Heuristik v1 — keine Production-ML, nur Regex. False-Positives akzeptabel,
# False-Negatives kritisch (eher öfter `sovereign` taggen als verpassen).
_SOVEREIGN_KEYWORDS = [
    r"\bBMW\b",
    r"\bMercedes\b",
    r"\bVolkswagen\b",
    r"\bAudi\b",
    r"\bPorsche\b",
    r"NDA[ -]Material",
    r"vertraulich",
    r"confidential",
    r"intern[ -]nur",
    r"Lieferantenliste",
    r"supplier list",
    # PII-Indikatoren
    r"\b[\w._%+-]+@[\w.-]+\.[A-Z]{2,}\b",  # email
    r"\b\d{4,}[-\s]?\d{4,}[-\s]?\d{4,}[-\s]?\d{4,}\b",  # credit-card-ish
    r"\bIBAN[: ]?[A-Z]{2}\d{2}[A-Z0-9]{4,30}\b",
    r"\bDE\d{9,12}\b",  # German tax/social IDs ish
    r"\bgeburtsdatum\b",
    r"\bdate of birth\b",
]

_AUTOMOTIVE_NEUTRAL_BOOST = [
    # Kontext-Modifier — wenn diese auch da sind, ist's wahrscheinlich Aria-Self-Diskussion ÜBER BMW, nicht BMW-Daten
    r"\bKadi-v2\b",
    r"\bMRR\b",
    r"\bSupplier\s*Pulse\b",
]


@dataclasses.dataclass
class PrivacyVerdict:
    tier: str          # "cloud" | "sovereign" | "unknown"
    score: float       # 0-1 confidence
    matched: list[str] # matched patterns
    reason: str


def classify_privacy(text: str) -> PrivacyVerdict:
    """Klassifiziert Input als `cloud` / `sovereign` / `unknown`."""
    matched_sov = []
    for pat in _SOVEREIGN_KEYWORDS:
        if re.search(pat, text, re.IGNORECASE):
            matched_sov.append(pat)

    if not matched_sov:
        return PrivacyVerdict(tier="cloud", score=0.95, matched=[],
                               reason="no privacy-trigger detected")

    matched_neutral = [p for p in _AUTOMOTIVE_NEUTRAL_BOOST if re.search(p, text, re.IGNORECASE)]
    if matched_neutral:
        return PrivacyVerdict(
            tier="cloud", score=0.65, matched=matched_sov,
            reason=f"sovereign-keywords detected but neutral-context-boost active: {matched_neutral}",
        )
    return PrivacyVerdict(
        tier="sovereign", score=0.9, matched=matched_sov,
        reason=f"sovereign-keywords detected without neutral-context",
    )


# ---- Task-Klasse Routing (KAR-121 Vorbereitung) -----------------------------

TASK_CLASS_TO_WORKER = {
    "plan":       ("anthropic", "claude-opus-4-7"),
    "architecture": ("anthropic", "claude-opus-4-7"),
    "code-review": ("anthropic", "claude-opus-4-7"),
    "security-audit": ("anthropic", "claude-opus-4-7"),
    "multi-file-refactor": ("google", "gemini-pro-2.5"),
    "agentic-coding": ("google", "gemini-pro-2.5"),
    "math":       ("openai", "gpt-5"),
    "algorithmic": ("openai", "gpt-5"),
    "bulk":       ("deepseek", "deepseek-coder-v3"),
    "translation": ("deepseek", "deepseek-coder-v3"),
    "doc-extract": ("deepseek", "deepseek-coder-v3"),
    "vision":     ("google", "gemini-pro-2.5"),
    "default":    ("anthropic", "claude-opus-4-7"),
}

# Cross-Family-Mapping (Worker-Family → preferred Judge-Family)
_FAMILY_TO_JUDGE_FAMILY = {
    "anthropic": "openai",   # Claude → GPT
    "openai":    "anthropic", # GPT → Claude
    "google":    "anthropic", # Gemini → Claude
    "deepseek":  "anthropic", # DeepSeek → Claude
}


def choose_worker(task_class: str) -> tuple[str, str]:
    """Returns (family, model_id) für Worker."""
    return TASK_CLASS_TO_WORKER.get(task_class.lower(), TASK_CLASS_TO_WORKER["default"])


def choose_judge_family(worker_family: str) -> str:
    """Returns Judge-Family ≠ Worker-Family."""
    return _FAMILY_TO_JUDGE_FAMILY.get(worker_family, "openai")


# ---- CLI --------------------------------------------------------------------

def main() -> int:
    parser = argparse.ArgumentParser(description="Aria LLM Router (KAR-121/124)")
    sub = parser.add_subparsers(dest="cmd", required=True)

    p_clf = sub.add_parser("classify", help="classify privacy tier of text")
    p_clf.add_argument("text")
    p_clf.add_argument("--json", action="store_true")

    p_route = sub.add_parser("route", help="full routing decision")
    p_route.add_argument("text")
    p_route.add_argument("--task-class", default="default")
    p_route.add_argument("--json", action="store_true")

    args = parser.parse_args()

    if args.cmd == "classify":
        v = classify_privacy(args.text)
        if args.json:
            print(json.dumps(dataclasses.asdict(v), indent=2))
        else:
            print(f"tier: {v.tier} (score {v.score:.0%})")
            print(f"reason: {v.reason}")
            if v.matched:
                print(f"matched: {v.matched}")
        return 0

    if args.cmd == "route":
        privacy = classify_privacy(args.text)
        worker_fam, worker_model = choose_worker(args.task_class)
        judge_fam = choose_judge_family(worker_fam)
        decision = {
            "privacy_tier": privacy.tier,
            "privacy_score": privacy.score,
            "privacy_matched": privacy.matched,
            "task_class": args.task_class,
            "worker_family": worker_fam,
            "worker_model": worker_model,
            "judge_family": judge_fam,
            "notice": (
                "PHASE-1-STUB: sovereign-tier routing NICHT aktiv. "
                "Input wird trotzdem an Cloud-Worker geleitet. "
                "Phase 4 (KAR-118) wird das Routing einschalten."
                if privacy.tier == "sovereign" else None
            ),
        }
        print(json.dumps(decision, indent=2) if args.json else json.dumps(decision, indent=2))
        return 0

    return 2


if __name__ == "__main__":
    sys.exit(main())
