#!/usr/bin/env python3
"""aria-distro-sanitize — Brain-Sanitizer für Aria-Distribution (KAR-131).

Kopiert `aria-brain/` zu `aria-distro-build/`, mit:
- HARD-SKIP für Kais-private Files (USER.md, CORRECTIONS.md, 06-Daily/, etc.)
- REDACT für Files die generic gemacht werden (SOUL.md → Kais → User)
- KEEP für system + generic frameworks

Test-Modus: grep auf PII-Tokens (Kais, Aseckzai, kadi, BMW, aseckzai@gmail.com)
→ muss 0 Treffer im Output sein.

CLI:
    python3 aria-distro-sanitize.py status              # zeige plan
    python3 aria-distro-sanitize.py build               # build distro
    python3 aria-distro-sanitize.py verify              # PII-Scan im Output
    python3 aria-distro-sanitize.py build --output /tmp/distro --apply
"""
from __future__ import annotations

import argparse
import re
import shutil
import sys
from pathlib import Path

BRAIN_ROOT = Path("/root/aria/brain")
DEFAULT_OUTPUT = Path("/tmp/aria-distro-build")

# ---- Filter-Schema --------------------------------------------------------

HARD_SKIP_FILES = {
    # Identity — Kais-specific
    "USER.md",
    "CORRECTIONS.md",  # LRN-Records sind Kais-specific (LRN-20260506-003 Telegram-Token-Pasting etc.)
    # Conversations + Tagebuch
    "HANDOFF.md",
    "MEMORIES.md",   # Pointer, aber Pointer-Pfad ist Kais-VPS
}

HARD_SKIP_FOLDERS = {
    "00-Inbox",          # unsortiert
    "01-Projekte",       # Kais-specific Projekt-Ordner
    "04-Feedback",       # Kais-Feedback
    "06-Daily",          # Tages-Logs Kais
    "CORRECTIONS.archive",
    "HANDOFF.archive",
    ".git",
    ".obsidian",
    "__pycache__",
    "akp",               # AKP-pipeline outputs
    "youtube",           # YouTube-cache
}

REDACT_FILES = {
    # Need template-version: Aria-Identity bleibt, Kais-Refs raus
    "SOUL.md", "IDENTITY.md", "WELCOME.md", "TOOLS.md", "ENGINEERING.md",
    "SELF-IMPROVEMENT.md", "HEARTBEAT.md", "HOOKS.md", "RECOVERY.md",
    "CLAUDE.md", "CLAUDE.archive.md", "ONBOARDING.md",
}

KEEP_FOLDERS = {
    "02-Wissen",         # Generic Frameworks + Research (per-file PII-scan in verify)
    "03-Recherchen",
    "05-Referenzen",     # selektiv — Templates ja, snapshots nein
}

# 02-Wissen-File-Patterns die SKIP statt KEEP sind (Aria-self oder Kunden-specific)
WISSEN_SKIP_PATTERNS = [
    r"^aria-",           # aria-architektur-*, aria-phase1-eval-*
    r"^kadi",            # kadi-v2-*
    r"^openclaw",
    r"^hermes",
    r"^gibson",
    r"^transkriptor",
    r"^strategic-review",
    r"^integration-wishlist",
    r"^ai-radar/",       # AI-Radar config + Funde (Aria-self setup)
    r"^B-Unternehmertum/customer-list",  # personal contacts
]
WISSEN_SKIP_RE = [re.compile(p, re.IGNORECASE) for p in WISSEN_SKIP_PATTERNS]

# 05-Referenzen-Patterns die SKIP sind
REFERENZEN_SKIP_PATTERNS = [
    r"^kais-",
    r"^aria-",
    r"^architektur-diagramme-",  # heutige Aria-Diagramme
    r"^n8n-",                    # personal workflows
]
REFERENZEN_SKIP_RE = [re.compile(p, re.IGNORECASE) for p in REFERENZEN_SKIP_PATTERNS]

# PII-Tokens, die in Output 0× auftauchen dürfen
PII_PATTERNS = [
    r"\bKais\b",
    r"\bAseckzai\b",
    r"aseckzai@gmail\.com",
    r"@kaiss78\b",
    r"\bkaiss?78\b",
    r"\bKADiCon\b",       # User-GitHub-Account
    r"\bsrv1649305\b",     # VPS-Hostname
    r"\bBMW\b",
    r"\bKadi-v2?\b",
    r"\bkadicon\.local\b",
    r"chat_id.*1164395546",
]

# Redact-Replacements für Template-Mode
REDACT_REPLACEMENTS = [
    (r"\bKais Aseckzai\b", "{{USER_FULL_NAME}}"),
    (r"\bKais\b", "{{USER_NAME}}"),
    (r"\bAseckzai\b", "{{USER_LASTNAME}}"),
    (r"aseckzai@gmail\.com", "{{USER_EMAIL}}"),
    (r"@kaiss78\b", "@{{USER_TG_HANDLE}}"),
    (r"\bkaiss?78\b", "{{USER_TG_HANDLE}}"),
    (r"\bchat_id.*1164395546\b", "chat_id: {{USER_TG_CHAT_ID}}"),
    (r"\bKADiCon\b", "{{USER_GITHUB_ORG}}"),
    (r"\bsrv1649305\b", "{{USER_VPS_HOST}}"),
    (r"\bBMW\b", "{{CUSTOMER_ACME}}"),
    (r"\bKadi-v2?\b", "{{CUSTOMER_PROJECT}}"),
    (r"\bkadicon\.local\b", "{{USER_LOCAL_DOMAIN}}"),
]


# ---- Classifier -----------------------------------------------------------

def classify(rel_path: Path) -> str:
    """Returns: 'skip' / 'redact' / 'keep' / 'keep-redact'."""
    parts = rel_path.parts
    if not parts:
        return "skip"
    first = parts[0]
    if first in HARD_SKIP_FOLDERS:
        return "skip"
    if first in KEEP_FOLDERS:
        rest = "/".join(parts[1:])
        if first == "02-Wissen":
            for rx in WISSEN_SKIP_RE:
                if rx.search(rest):
                    return "skip"
        elif first == "05-Referenzen":
            for rx in REFERENZEN_SKIP_RE:
                if rx.search(rest):
                    return "skip"
        # default für KEEP-Folder: keep + apply REDACT-Replacements (vorsichtshalber)
        return "keep-redact"
    # Root-Files
    name = parts[-1]
    if name in HARD_SKIP_FILES:
        return "skip"
    if name in REDACT_FILES:
        return "redact"
    # Default: skip unknown root-files
    return "skip"


# ---- Sanitize -------------------------------------------------------------

def redact_content(text: str) -> str:
    out = text
    for pat, repl in REDACT_REPLACEMENTS:
        out = re.sub(pat, repl, out)
    return out


def build_distro(output_dir: Path, *, apply: bool = False) -> dict:
    """Walk Brain, klassifiziere, schreibe Output."""
    stats = {"kept": 0, "redacted": 0, "keep_redact": 0, "skipped": 0, "size_bytes": 0}
    if apply and output_dir.exists():
        shutil.rmtree(output_dir)
    if apply:
        output_dir.mkdir(parents=True, exist_ok=True)

    for path in BRAIN_ROOT.rglob("*"):
        if not path.is_file():
            continue
        rel = path.relative_to(BRAIN_ROOT)
        action = classify(rel)
        if action == "skip":
            stats["skipped"] += 1
            continue
        out_path = output_dir / rel
        try:
            content = path.read_text(encoding="utf-8", errors="replace")
        except OSError:
            stats["skipped"] += 1
            continue
        if action == "redact":
            content = redact_content(content)
            stats["redacted"] += 1
        elif action == "keep-redact":
            content = redact_content(content)
            stats["keep_redact"] += 1
        else:
            stats["kept"] += 1
        if apply:
            out_path.parent.mkdir(parents=True, exist_ok=True)
            out_path.write_text(content, encoding="utf-8")
            stats["size_bytes"] += out_path.stat().st_size
        else:
            stats["size_bytes"] += len(content.encode("utf-8"))
    return stats


def verify_distro(output_dir: Path) -> dict:
    """Scan Output für PII-Patterns. 0 Treffer = Pass."""
    hits = []
    for md in output_dir.rglob("*.md"):
        text = md.read_text(encoding="utf-8", errors="replace")
        for pat in PII_PATTERNS:
            for m in re.finditer(pat, text):
                hits.append({
                    "file": str(md.relative_to(output_dir)),
                    "pattern": pat,
                    "line": text[:m.start()].count("\n") + 1,
                    "snippet": text[max(0, m.start() - 30):m.end() + 30].replace("\n", " "),
                })
                if len(hits) >= 100:
                    return {"pii_hits": hits, "pass": False, "truncated": True, "count": len(hits)}
    return {"pii_hits": hits, "pass": len(hits) == 0, "count": len(hits)}


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

def _cli_status(args) -> int:
    stats = build_distro(args.output, apply=False)
    print(f"Plan: kept={stats['kept']}, redacted={stats['redacted']}, skipped={stats['skipped']}")
    print(f"Estimated size: {stats['size_bytes']:,} bytes ({stats['size_bytes'] / 1024:.1f} KB)")
    return 0


def _cli_build(args) -> int:
    stats = build_distro(args.output, apply=True)
    print(f"Built to {args.output}/")
    print(f"  kept={stats['kept']}, redacted={stats['redacted']}, skipped={stats['skipped']}")
    print(f"  size: {stats['size_bytes']:,} bytes ({stats['size_bytes'] / 1024:.1f} KB)")
    return 0


def _cli_verify(args) -> int:
    if not args.output.exists():
        print(f"ERROR: {args.output} does not exist. Run `build` first.", file=sys.stderr)
        return 1
    res = verify_distro(args.output)
    if res["pass"]:
        print(f"✓ VERIFY PASS — 0 PII-Hits in {args.output}")
        return 0
    print(f"✗ VERIFY FAIL — {res['count']} PII-Hits found:")
    for hit in res["pii_hits"][:20]:
        print(f"  {hit['file']}:{hit['line']} matches /{hit['pattern']}/  → ...{hit['snippet']}...")
    if len(res["pii_hits"]) > 20:
        print(f"  ... + {len(res['pii_hits']) - 20} more")
    return 1


def main() -> int:
    parser = argparse.ArgumentParser(description="Aria-Distribution Sanitizer (KAR-131)")
    sub = parser.add_subparsers(dest="cmd", required=True)
    for cmd in ("status", "build", "verify"):
        p = sub.add_parser(cmd)
        p.add_argument("--output", type=Path, default=DEFAULT_OUTPUT)
        p.set_defaults(func={"status": _cli_status, "build": _cli_build, "verify": _cli_verify}[cmd])
    args = parser.parse_args()
    return args.func(args)


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