#!/usr/bin/env python3
"""Live re-fetch right now and compare to today's cron snapshot in oracle.db.
Read-only against the DB (only reads). Does NOT store anything."""
import json, math, sys, os, time
sys.path.insert(0, os.path.dirname(__file__))
from datetime import datetime, timezone
import sqlite3

import importlib

SOURCES = ["github", "arxiv", "reddit", "hackernews", "huggingface"]

ADAPTER_CLASSES = {
    "github": lambda: importlib.import_module("adapters.github").GitHubAdapter(),
    "arxiv": lambda: importlib.import_module("adapters.arxiv").ArxivAdapter(),
    "reddit": lambda: importlib.import_module("adapters.reddit").RedditAdapter(),
    "hackernews": lambda: importlib.import_module("adapters.hackernews").HackerNewsAdapter(),
    "huggingface": lambda: importlib.import_module("adapters.huggingface").HuggingFaceAdapter(),
}
NOW = datetime.now(timezone.utc)
TODAY = NOW.strftime("%Y-%m-%d")

# ---- DB: what cron already pulled today ----
db = sqlite3.connect(os.path.join(os.path.dirname(__file__), "oracle.db"))
db_rows = db.execute(
    "select source,source_id,title,url,signal_score,raw_metadata "
    "from entries where first_seen >= ?", (TODAY + "T00:00:00",)
).fetchall()
db_by_srcid = {}
for r in db_rows:
    src, sid = r[0], r[1]
    db_by_srcid.setdefault(src, {})[sid] = r
print(f"[DB today] {len(db_rows)} entries across {len(set(r[0] for r in db_rows))} sources")

# ---- LIVE re-fetch right now ----
live = {}   # source -> list of entries
fail = {}
for name in SOURCES:
    try:
        adapter = ADAPTER_CLASSES[name]()
        t0 = time.time()
        entries = adapter.fetch(limit=20)
        live[name] = entries
        print(f"[LIVE] {name:11} {len(entries):2} entries in {time.time()-t0:5.1f}s")
    except Exception as e:
        fail[name] = str(e)
        live[name] = []
        print(f"[LIVE] {name:11} FAILED: {e}")

def norm(metric, values):
    """0-100 normalization via log scale for skewed engagement."""
    vals = [v for v in values if v is not None and v >= 0]
    if not vals:
        return {}
    mx = max(vals)
    out = {}
    for k, v in metric.items():
        if v is None or v <= 0:
            out[k] = 0.0
        else:
            out[k] = round(100 * math.log(1 + v) / math.log(1 + mx), 1) if mx > 0 else 0.0
    return out

# ---- engagement extraction per source ----
def engagement(e):
    """Return (raw_engagement, metrics_dict, virality_0_100) per source."""
    src = e["source"]
    md = json.loads(e.get("raw_metadata", "{}") or "{}")
    if src == "hackernews":
        score = md.get("score", 0) or 0
        desc = md.get("descendants", 0) or 0
        eng = score + desc * 2  # comments weighted as stronger virality signal
        metrics = {"points": score, "comments": desc}
    elif src == "reddit":
        ups = md.get("ups", 0) or 0
        comments = md.get("num_comments", 0) or 0
        eng = ups + comments * 2
        metrics = {"ups": ups, "comments": comments}
    elif src == "huggingface":
        likes = md.get("likes", 0) or 0
        dl = md.get("downloads", 0) or 0
        eng = likes * 10 + dl * 0.01  # downloads are huge; down-weight
        metrics = {"likes": likes, "downloads": dl}
    elif src == "github":
        spd = md.get("stars_per_day", 0) or 0
        stars = md.get("stars", 0) or 0
        eng = spd * 50 + stars * 0.001
        metrics = {"stars/day": round(spd, 1), "stars": stars}
    elif src == "arxiv":
        eng = 0  # no engagement metrics; virality N/A, treat as research signal only
        metrics = {"categories": ", ".join(md.get("categories", [])[:3])}
    else:
        eng = 0
        metrics = {}
    return eng, metrics

# ---- Build unified compare list ----
unified = []
for src, entries in live.items():
    dbset = db_by_srcid.get(src, {})
    for e in entries:
        sid = e.get("source_id")
        eng, metrics = engagement(e)
        is_new = sid not in dbset  # NEW since cron
        unified.append({
            "source": src,
            "title": e.get("title", "")[:90],
            "url": e.get("url", ""),
            "signal": e.get("signal_score", 0),
            "engagement": eng,
            "metrics": metrics,
            "new": is_new,
            "sid": sid,
        })

# ---- Normalize virality across ALL live items (cross-source) ----
all_eng = {u["sid"]: u["engagement"] for u in unified if u["engagement"] > 0}
norm_map = norm({k: v for k, v in all_eng.items()}, list(all_eng.values()))
for u in unified:
    u["virality"] = norm_map.get(u["sid"], 0.0)

# ---- RANK by virality (desc), tiebreak signal_score ----
ranked = sorted(unified, key=lambda u: (u["virality"], u["signal"]), reverse=True)

print("\n" + "="*100)
print(f"LIVE PULL @ {NOW.strftime('%Y-%m-%d %H:%M UTC')}  |  {len(ranked)} items  |  NEW since cron: {sum(1 for u in unified if u['new'])}")
print("="*100)

# Full list
print("\n### FULL RANKED LIST (by virality)")
for i, u in enumerate(ranked, 1):
    tag = "NEW" if u["new"] else "    "
    m = " ".join(f"{k}={v}" for k, v in u["metrics"].items())
    print(f"{i:2}. [{u['virality']:5.1f}] {tag} {u['source']:10} | {u['title']}")
    if m:
        print(f"     {m}  (signal {u['signal']:.2f})")

# Trending = NEW items ranked by virality
trending = [u for u in ranked if u["new"]]
print("\n### TRENDING NOW (NEW since today's cron pull, by virality)")
if not trending:
    print("  (no new items — live pull matches today's snapshot exactly)")
for i, u in enumerate(trending, 1):
    m = " ".join(f"{k}={v}" for k, v in u["metrics"].items())
    print(f"{i:2}. [{u['virality']:5.1f}] {u['source']:10} | {u['title']}")
    if m: print(f"     {m}")

# Source-level engagement summary
print("\n### PER-SOURCE VIRALITY CEILING (top live engagement)")
by_src = {}
for u in unified:
    by_src.setdefault(u["source"], []).append(u)
for src in SOURCES:
    items = by_src.get(src, [])
    if not items:
        print(f"  {src:11}: (no live data)"); continue
    top = max(items, key=lambda x: x["virality"])
    print(f"  {src:11}: top_virality={top['virality']:.1f}  items_live={len(items)}  new={sum(1 for x in items if x['new'])}")

db.close()

# ---- Persist full list as markdown (downloadable) ----
import os as _os
out_md = f"# Athena Live Compare — {NOW.strftime('%Y-%m-%d %H:%M UTC')}\n\n"
out_md += f"- **Live pull:** {len(ranked)} items (Reddit excluded — rate-limited)\n"
out_md += f"- **NEW since today's 13:00 UTC cron:** {sum(1 for u in unified if u['new'])}\n"
out_md += f"- **Caveat:** HF scores = cumulative traction, not 24h velocity. Velocity signals = GitHub stars/day, HN points/comments.\n\n"
out_md += "## FULL RANKED LIST (by cross-source virality)\n\n"
out_md += "| # | Virality | New | Source | Title | Key metric | Signal |\n"
out_md += "|---|---|---|---|---|---|---|\n"
for i, u in enumerate(ranked, 1):
    tag = "NEW" if u["new"] else ""
    m = ", ".join(f"{k}={v}" for k, v in u["metrics"].items())
    out_md += f"| {i} | {u['virality']:.1f} | {tag} | {u['source']} | {u['title']} | {m} | {u['signal']:.2f} |\n"
out_path = _os.path.join(_os.path.dirname(__file__), f"live_compare_{NOW.strftime('%Y%m%d_%H%M')}.md")
with open(out_path, "w") as f:
    f.write(out_md)
print(f"\n[SAVED] {out_path}")

