"""aria-radar-auto-watchlist — Weekly Cross-Platform Author-Discovery (KAR-117).

Scannt die letzten 7 Tage AI-Radar-Snapshots aus /var/lib/aria-radar/snapshots/,
extrahiert Author-Identitäten pro Plattform, kreuzt Identitäten:

- **GitHub-User**: extracted from `https://github.com/<user>/<repo>` URLs
- **GitHub-Trending-User**: same pattern
- **HackerNews-User**: aus item raw.author
- **Bluesky-Handle**: aus raw.handle / URL-Profile-Path
- **Mastodon-User**: aus URL `https://<instance>/@<user>/...`
- **mcp.so-Owner**: aus raw.owner

Cross-Reference-Signal: User der auf 2+ Plattformen vorkommt UND mind. 1 Item
mit Score ≥ Top-20-Tier hat → Signal-Boost-Kandidat.

Output: Markdown-Tabelle in
  `/root/aria/brain/00-Inbox/auto-watchlist-YYYY-WW.md`

Format der Suggestion-Tabelle:
| Handle | GitHub | HN | Bluesky | Mastodon | mcp.so | Items in 7d | Top-Score |

Sonntag 04:30 Europe/Berlin via aria-radar-auto-watchlist.timer (siehe HOOKS.md).
"""
from __future__ import annotations

import argparse
import collections
import datetime as dt
import json
import logging
import re
import sys
from pathlib import Path

SNAPSHOTS_DIR = Path("/var/lib/aria-radar/snapshots")
BRAIN_INBOX = Path("/root/aria/brain/00-Inbox")
LOOKBACK_DAYS = 7
MIN_OCCURRENCES = 2     # mind. 2 Plattformen ODER 2+ items für Suggestion
TOP_SCORE_TIER = 20

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("auto-watchlist")

GITHUB_USER_RE = re.compile(r"github\.com/([\w.-]+)/[\w.-]+")
MASTODON_USER_RE = re.compile(r"https?://([\w.-]+)/@([\w.-]+)")
BLUESKY_HANDLE_RE = re.compile(r"bsky\.app/profile/([\w.-]+)")
MCPSO_RE = re.compile(r"mcp\.so/server/[^/]+/([\w.-]+)")


def load_snapshots(lookback_days: int = LOOKBACK_DAYS) -> list[dict]:
    """Load JSON-Snapshots der letzten N Tage."""
    cutoff = dt.date.today() - dt.timedelta(days=lookback_days)
    snapshots = []
    for f in sorted(SNAPSHOTS_DIR.glob("*.json"), reverse=True):
        try:
            snap_date = dt.date.fromisoformat(f.stem)
        except ValueError:
            continue
        if snap_date < cutoff:
            continue
        try:
            snapshots.append(json.loads(f.read_text(encoding="utf-8")))
        except Exception as exc:  # noqa: BLE001
            log.warning("snapshot %s unreadable: %s", f, exc)
    log.info("loaded %d snapshots since %s", len(snapshots), cutoff)
    return snapshots


def extract_authors_from_item(item: dict) -> dict[str, str]:
    """Returns {platform: handle, ...} pro item."""
    source = item.get("source", "")
    url = item.get("url", "")
    raw = item.get("raw") or {}
    authors: dict[str, str] = {}

    if source in ("github", "github_trending"):
        m = GITHUB_USER_RE.search(url)
        if m:
            authors["github"] = m.group(1)
    elif source == "hackernews":
        author = raw.get("author")
        if author:
            authors["hackernews"] = author
    elif source == "bluesky":
        handle = raw.get("handle")
        if handle:
            authors["bluesky"] = handle
        else:
            m = BLUESKY_HANDLE_RE.search(url)
            if m:
                authors["bluesky"] = m.group(1)
    elif source == "mastodon":
        m = MASTODON_USER_RE.search(url)
        if m:
            authors["mastodon"] = f"@{m.group(2)}@{m.group(1)}"
    elif source == "mcp_so":
        owner = raw.get("owner")
        if owner:
            authors["mcp_so"] = owner

    return authors


def aggregate(snapshots: list[dict]) -> dict[str, dict]:
    """Aggregiert pro (platform, handle) → {items_count, top_score, items_top, all_titles}."""
    agg: dict[tuple[str, str], dict] = collections.defaultdict(
        lambda: {"items_count": 0, "top_score": 0, "top_item": None, "titles": []}
    )
    for snap in snapshots:
        for item in snap.get("items", []):
            authors = extract_authors_from_item(item)
            score = item.get("score") or 0
            for plat, handle in authors.items():
                key = (plat, handle.lower())
                row = agg[key]
                row["items_count"] += 1
                row["titles"].append((score, item.get("title", "")[:80]))
                if score > row["top_score"]:
                    row["top_score"] = score
                    row["top_item"] = {
                        "title": item.get("title", "")[:120],
                        "url": item.get("url", ""),
                        "score": score,
                    }
    return agg


def cross_reference(agg: dict) -> list[dict]:
    """Findet Handles die unter Verschiedenen Plattformen vorkommen (durch Substring).
    Heuristik: gleicher 'handle'-String als Schlüssel über Plattformen hinweg."""
    by_handle: dict[str, dict[str, dict]] = collections.defaultdict(dict)
    for (plat, handle), row in agg.items():
        by_handle[handle][plat] = row

    suggestions = []
    for handle, by_plat in by_handle.items():
        if not handle or len(handle) < 2:
            continue
        if len(by_plat) >= MIN_OCCURRENCES:
            # Cross-Platform-Match
            total_items = sum(r["items_count"] for r in by_plat.values())
            top_score = max(r["top_score"] for r in by_plat.values())
            suggestions.append({
                "handle": handle,
                "platforms": list(by_plat.keys()),
                "items_count": total_items,
                "top_score": top_score,
                "tier": "cross-platform",
                "rows": by_plat,
            })
            continue
        # Single-Platform aber sehr aktiv (≥3 Items in 7 Tagen)
        only_plat, only_row = list(by_plat.items())[0]
        if only_row["items_count"] >= 3:
            suggestions.append({
                "handle": handle,
                "platforms": [only_plat],
                "items_count": only_row["items_count"],
                "top_score": only_row["top_score"],
                "tier": "single-active",
                "rows": by_plat,
            })

    suggestions.sort(key=lambda s: (-len(s["platforms"]), -s["top_score"], -s["items_count"]))
    return suggestions


def render_markdown(suggestions: list[dict], lookback_days: int) -> str:
    today = dt.date.today()
    iso_year, iso_week, _ = today.isocalendar()
    lines = [
        "---",
        f"title: 'Auto-Watchlist {iso_year}-W{iso_week:02d} ({today.isoformat()})'",
        "type: inbox",
        "tags: [ai-radar, auto-watchlist, kar-117]",
        f"date: {today.isoformat()}",
        "status: aktiv",
        f"description: 'Automatisch generierte Vorschläge für watchlist.md-Erweiterung. Lookback {lookback_days}d, Top-20-Tier.'",
        "---",
        "",
        f"# Auto-Watchlist {iso_year}-W{iso_week:02d}",
        "",
        f"> Lookback: letzte {lookback_days} Tage. Cross-Platform-Tier: Handle auf 2+ Plattformen. Single-Active-Tier: ≥3 Items auf einer Plattform.",
        "",
        f"## Cross-Platform-Suggestions ({sum(1 for s in suggestions if s['tier'] == 'cross-platform')})",
        "",
    ]
    cross = [s for s in suggestions if s["tier"] == "cross-platform"]
    if cross:
        lines.append("| Handle | Plattformen | Items 7d | Top-Score | Top-Item |")
        lines.append("|---|---|---:|---:|---|")
        for s in cross[:30]:
            plats = ", ".join(s["platforms"])
            top_row = max(s["rows"].values(), key=lambda r: r["top_score"])
            top_item = top_row.get("top_item") or {}
            title = (top_item.get("title", "") or "")[:60]
            lines.append(f"| `{s['handle']}` | {plats} | {s['items_count']} | {s['top_score']:,} | {title} |")
    else:
        lines.append("_keine Cross-Platform-Matches diese Woche._")
    lines.append("")
    lines.append(f"## Single-Platform-Active ({sum(1 for s in suggestions if s['tier'] == 'single-active')})")
    lines.append("")
    active = [s for s in suggestions if s["tier"] == "single-active"]
    if active:
        lines.append("| Handle | Plattform | Items 7d | Top-Score | Top-Item |")
        lines.append("|---|---|---:|---:|---|")
        for s in active[:30]:
            plat = s["platforms"][0]
            top_row = s["rows"][plat]
            top_item = top_row.get("top_item") or {}
            title = (top_item.get("title", "") or "")[:60]
            lines.append(f"| `{s['handle']}` | {plat} | {s['items_count']} | {s['top_score']:,} | {title} |")
    else:
        lines.append("_keine Single-Active mit ≥3 Items diese Woche._")

    lines.extend([
        "",
        "## Review-Anleitung",
        "",
        "Aria reviewt diese Datei Sonntag morgen:",
        "1. Cross-Platform-Suggestions: in `02-Wissen/ai-radar/watchlist.md` Tier-1 oder Tier-2 aufnehmen",
        "2. Single-Active: prüfen ob Person/Org wirklich AI-Signal bringt oder Lärm ist",
        "3. Verwerfen mit Begründung in dieser Datei dokumentieren (für nächste Woche Lern-Feedback)",
        "",
        "Cross-Reference: [[02-Wissen/ai-radar/watchlist]] · [[02-Wissen/ai-radar/architecture]]",
    ])
    return "\n".join(lines)


def main() -> int:
    parser = argparse.ArgumentParser(description="AI-Radar Auto-Watchlist (KAR-117)")
    parser.add_argument("--lookback-days", type=int, default=LOOKBACK_DAYS)
    parser.add_argument("--dry-run", action="store_true", help="print to stdout, no file write")
    args = parser.parse_args()

    snapshots = load_snapshots(args.lookback_days)
    if not snapshots:
        log.warning("no snapshots in lookback window")
        return 1

    agg = aggregate(snapshots)
    suggestions = cross_reference(agg)
    log.info("found %d suggestions (%d cross-platform, %d single-active)",
             len(suggestions),
             sum(1 for s in suggestions if s["tier"] == "cross-platform"),
             sum(1 for s in suggestions if s["tier"] == "single-active"))

    md = render_markdown(suggestions, args.lookback_days)
    if args.dry_run:
        print(md)
        return 0

    today = dt.date.today()
    iso_year, iso_week, _ = today.isocalendar()
    out_path = BRAIN_INBOX / f"auto-watchlist-{iso_year}-W{iso_week:02d}.md"
    BRAIN_INBOX.mkdir(parents=True, exist_ok=True)
    # Atomic via aria-atomic-write wenn verfügbar, sonst plain
    try:
        sys.path.insert(0, "/root/aria/scripts")
        from aria_atomic_write import atomic_write_text  # type: ignore
        atomic_write_text(out_path, md, mode=0o644)
    except ImportError:
        out_path.write_text(md, encoding="utf-8")
    log.info("wrote %s (%d chars)", out_path, len(md))
    return 0


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