#!/usr/bin/env python3
"""aria-llms-txt-gen — Erzeugt llms.txt + llms-full.txt für aria-brain.

KAR-183 (Phase-1 gbrain-Adoption). Adoptiert von gbrain's `llms.txt`-Pattern,
de-facto-Standard für agent-readable Doku-Maps.

Outputs:
  /root/aria/brain/llms.txt        — kompakte Übersicht (~4 KB)
  /root/aria/brain/llms-full.txt   — inlined Tier-1 + MOC-Files (~50-100 KB)

Trigger:
  - Manual: python3 aria-llms-txt-gen.py
  - Cron:   systemd-Timer aria-llms-txt.timer (täglich 04:45)

Quelle: gbrain repo, audit `02-Wissen/garrytan-github-audit-2026-05-14.md` §3.6
"""
from __future__ import annotations

import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Iterable

BRAIN_ROOT = Path("/root/aria/brain")
LLMS_TXT = BRAIN_ROOT / "llms.txt"
LLMS_FULL = BRAIN_ROOT / "llms-full.txt"

# Tier-1: SessionStart-Hook lädt diese — sind immer relevant.
TIER1_FILES = [
    "SOUL.md",
    "IDENTITY.md",
    "USER.md",
    "TOOLS.md",
    "ENGINEERING.md",
    "CORRECTIONS.md",
    "SELF-IMPROVEMENT.md",
    "HEARTBEAT.md",
    "HOOKS.md",
    "CLAUDE.md",
]

# Bereichs-Reihenfolge für llms.txt
SECTIONS = [
    ("00-Inbox", "Inbox — Vorsortierung, schnelle Notizen, append-only"),
    ("01-Projekte", "Aktive Projekte (Kadi-v2, Aria, Kunden)"),
    ("02-Wissen", "Recherchen, Konzepte, Erklärungen, Architektur-Audits"),
    ("03-Recherchen", "Tiefere Recherchen mit Quellen"),
    ("04-Feedback", "Kais-Feedback, externe Reviews"),
    ("05-Referenzen", "Aria-Templates, Snapshots, n8n-Backups, YouTube-Transcripts"),
    ("06-Daily", "Tages-Logs, Wochen-Reviews"),
]

# Soft-Caps (von 01-analysis-map definiert)
MAX_LLMS_TXT_BYTES = 4_500   # llms.txt soll ~4 KB bleiben
MAX_LLMS_FULL_BYTES = 120_000  # llms-full.txt: 100-120 KB Soft-Cap


def _read_frontmatter_title(path: Path) -> str | None:
    """Lese title aus YAML-frontmatter; None wenn nicht vorhanden."""
    try:
        text = path.read_text(encoding="utf-8", errors="replace")
    except OSError:
        return None
    if not text.startswith("---"):
        return None
    end = text.find("\n---", 4)
    if end < 0:
        return None
    fm = text[4:end]
    for line in fm.splitlines():
        if line.strip().startswith("title:"):
            return line.split(":", 1)[1].strip().strip('"').strip("'")
    return None


def _list_top_notes(section_dir: Path, limit: int = 10) -> list[tuple[str, str]]:
    """Liste Top-N Notes pro Sektion: (slug, title)."""
    if not section_dir.is_dir():
        return []
    notes = []
    for p in section_dir.rglob("*.md"):
        if p.name.startswith("."):
            continue
        title = _read_frontmatter_title(p) or p.stem
        # mtime as sort key — recently updated first
        try:
            mtime = p.stat().st_mtime
        except OSError:
            mtime = 0
        rel = p.relative_to(section_dir).as_posix()
        notes.append((mtime, rel, title))
    notes.sort(key=lambda x: x[0], reverse=True)
    return [(rel, title) for _, rel, title in notes[:limit]]


def _build_llms_txt() -> str:
    """Kompakter Doku-Map (~4 KB)."""
    now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    lines = [
        "# aria-brain — Doku-Map",
        f"> Auto-generated by aria-llms-txt-gen on {now}.",
        "> Aria ist Kais' KI-Partner. Identity in Tier-1-Files (siehe llms-full.txt).",
        "",
        "## Tier-1 (SessionStart-Hook lädt automatisch)",
    ]
    for fname in TIER1_FILES:
        p = BRAIN_ROOT / fname
        if p.exists():
            title = _read_frontmatter_title(p) or fname
            size = p.stat().st_size
            lines.append(f"- `{fname}` ({size//1024} KB) — {title}")
    lines.append("")
    lines.append("## Bereiche")
    for slug, desc in SECTIONS:
        d = BRAIN_ROOT / slug
        if not d.is_dir():
            continue
        top = _list_top_notes(d, limit=6)
        lines.append(f"")
        lines.append(f"### {slug}/")
        lines.append(f"{desc}")
        for rel, title in top:
            lines.append(f"- `{slug}/{rel}` — {title}")
    lines.append("")
    lines.append("## Wichtige Standing-Orders")
    lines.append("- Brain-Doku Pflicht: alle wichtigen Erkenntnisse in Brain, nicht nur Linear/Daily")
    lines.append("- Brain proaktiv lesen vor Entscheidungen")
    lines.append("- Telegram-Replies via reply-Tool (Bot liest nicht zurück)")
    lines.append("- Secrets nie via Telegram (Replies leaken in jsonl)")
    lines.append("")
    lines.append("## See also")
    lines.append("- `llms-full.txt` — Tier-1 + MOCs inlined (~50-100 KB)")
    lines.append("- `CLAUDE.md` — vault schema, Folder-Konventionen")
    return "\n".join(lines) + "\n"


def _build_llms_full() -> str:
    """Inlined Tier-1 + 02-Wissen MOC-Files (~50-100 KB)."""
    now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    chunks = [
        "# aria-brain — Full Doku-Inline",
        f"> Auto-generated by aria-llms-txt-gen on {now}.",
        "> Tier-1-Identity + ausgewählte MOC-Files für agent-readable Bootstrap.",
        "",
    ]

    # Tier-1: full inline
    for fname in TIER1_FILES:
        p = BRAIN_ROOT / fname
        if not p.exists():
            continue
        chunks.append(f"\n\n===== {fname} =====")
        chunks.append(p.read_text(encoding="utf-8", errors="replace"))

    # 02-Wissen MOC-Hinweise: Files mit `type: reference` oder `audit` und mtime <30d
    moc_dir = BRAIN_ROOT / "02-Wissen"
    if moc_dir.is_dir():
        chunks.append("\n\n===== 02-Wissen — Top-MOC-Files =====")
        relevant = []
        for p in moc_dir.glob("*.md"):
            try:
                text = p.read_text(encoding="utf-8", errors="replace")
                if "type: reference" in text[:500] or "type: audit" in text[:500]:
                    relevant.append(p)
            except OSError:
                continue
        # Top-15 by mtime
        relevant.sort(key=lambda x: x.stat().st_mtime, reverse=True)
        for p in relevant[:15]:
            title = _read_frontmatter_title(p) or p.stem
            chunks.append(f"\n--- {p.name} — {title} ---")
            # Erste 80 Zeilen je File — Cap
            text = p.read_text(encoding="utf-8", errors="replace")
            head = "\n".join(text.splitlines()[:80])
            chunks.append(head)

    result = "\n".join(chunks) + "\n"
    if len(result.encode("utf-8")) > MAX_LLMS_FULL_BYTES:
        # Trunkiere von hinten
        b = result.encode("utf-8")[:MAX_LLMS_FULL_BYTES]
        # Schneide am letzten kompletten Newline
        result = b.decode("utf-8", errors="ignore")
        last_nl = result.rfind("\n")
        if last_nl > 0:
            result = result[:last_nl] + "\n\n[...truncated to fit cap...]\n"
    return result


def main(argv: list[str]) -> int:
    llms_txt = _build_llms_txt()
    llms_full = _build_llms_full()

    LLMS_TXT.write_text(llms_txt, encoding="utf-8")
    LLMS_FULL.write_text(llms_full, encoding="utf-8")

    print(f"[llms-txt-gen] llms.txt: {len(llms_txt):,} bytes ({len(llms_txt.splitlines())} lines)")
    print(f"[llms-txt-gen] llms-full.txt: {len(llms_full):,} bytes ({len(llms_full.splitlines())} lines)")
    if len(llms_txt.encode("utf-8")) > MAX_LLMS_TXT_BYTES:
        print(f"  WARN: llms.txt over soft-cap {MAX_LLMS_TXT_BYTES} bytes — consider tightening")
    return 0


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