"""Render Athena entries into a static news site.

Two-layer layout: Top News (fresh today) + aging Stack (everything else).
Read-only against oracle.db. Designed for a 20-min cron run.

Variant-aware: render_variant_html() builds themed pages from pre-filtered items.
"""
import argparse
import datetime
import html
import json
import os
import re
import sqlite3
from collections import OrderedDict
from typing import Optional

from oracle.clickability import fetch_items, compute_index, decay_index
from oracle.config import DB_PATH, WEBROOT, FALLBACK_WEBROOT, HALF_LIFE_H, TOP_N


def _clean_summary(raw):
    """Extract the most readable field from summary JSON."""
    if not raw:
        return ""
    try:
        d = json.loads(raw)
        if isinstance(d, dict):
            for k in ("one_liner", "key_technical_point", "potential_use_case"):
                v = d.get(k)
                if isinstance(v, str) and v.strip():
                    return re.sub(r"\\+|_|`", "", v).strip()
    except Exception:
        pass
    return re.sub(r"\\+|_|`", "", raw).strip()


# ── Theme palette ──────────────────────────────────────────────────────────

THEMES = {
    "dark": {
        "--bg": "#0b0e14",
        "--card": "#141925",
        "--fg": "#e6e9ef",
        "--mut": "#8b93a7",
        "--border": "#1f2533",
    },
    "light": {
        "--bg": "#f8f9fa",
        "--card": "#ffffff",
        "--fg": "#1a1a2e",
        "--mut": "#6b7280",
        "--border": "#e5e7eb",
    },
    "midnight": {
        "--bg": "#0a0a1a",
        "--card": "#111128",
        "--fg": "#c8d0e0",
        "--mut": "#5a6480",
        "--border": "#1a1a3a",
    },
}


def _theme_css(display: dict) -> str:
    """Generate CSS variables for a variant display config."""
    theme_name = display.get("theme", "dark")
    palette = THEMES.get(theme_name, THEMES["dark"])
    accent = display.get("accent", "#5b8cff")
    vars_list = ", ".join(f"{k}:{v}" for k, v in palette.items())
    return f":root {{ {vars_list}; --acc:{accent}; }}"


def _fmt_time(first_seen):
    if not first_seen:
        return ""
    try:
        dt = datetime.datetime.strptime(first_seen, "%Y-%m-%dT%H:%M:%SZ")
        return dt.strftime("%H:%M")
    except Exception:
        return ""


def _card(it, big=False):
    title = html.escape(it["title"] or "(untitled)")
    url = html.escape(it["url"] or "#")
    src = html.escape(it["source"])
    t = _fmt_time(it.get("first_seen"))
    summary_raw = _clean_summary(it.get("summary") or "")
    summary = html.escape(summary_raw[:200])
    cls = "card big" if big else "card"
    summary_html = ('<p class="summary">{0}</p>'.format(summary)) if (summary and big) else ""
    return f"""
      <article class="{cls}" data-src="{src}">
        <div class="meta"><span class="src">{src}</span>
          <span class="time">{t}</span>
          <span class="sig">sig {it.get('signal_score') or 0:.1f}</span>
          <span class="score">\U0001f525 {it['clickability_decayed']:.2f}</span></div>
        <h3><a href="{url}" target="_blank" rel="noopener">{title}</a></h3>
        {summary_html}
      </article>"""


def build_html(items):
    """Build the full HTML page from ranked items."""
    now = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    ranked = sorted(items, key=lambda x: x["clickability_decayed"], reverse=True)

    fresh = [it for it in ranked if it.get("fresh")]
    top = fresh[:TOP_N]
    stack = [it for it in ranked if it not in top]

    by_day = OrderedDict()
    for it in stack:
        day = (it.get("first_seen") or "")[:10] or "unknown"
        by_day.setdefault(day, []).append(it)

    top_html = "".join(_card(it, big=True) for it in top)

    stack_html = ""
    for day, rows in by_day.items():
        rows.sort(key=lambda x: x["clickability_decayed"], reverse=True)
        cards = "".join(_card(it) for it in rows)
        stack_html += f"""
    <h3 class="day">\U0001f4c5 {html.escape(day)}</h3>
    <div class="stack">{cards}</div>"""

    return f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Athena AI News — Ranked by Clickability</title>
<style>
  :root {{ --bg:#0b0e14; --card:#141925; --fg:#e6e9ef; --mut:#8b93a7; --acc:#5b8cff; }}
  * {{ box-sizing:border-box; }}
  body {{ margin:0; background:var(--bg); color:var(--fg);
    font:15px/1.5 -apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; }}
  header {{ padding:28px 20px 14px; border-bottom:1px solid #1f2533; text-align:center; }}
  header h1 {{ margin:0; font-size:28px; letter-spacing:.5px; }}
  header .sub {{ color:var(--mut); font-size:13px; margin-top:6px; }}
  main {{ max-width:1000px; margin:0 auto; padding:20px; }}
  h2.sech {{ font-size:18px; margin:26px 0 12px; border-left:3px solid var(--acc); padding-left:10px; }}
  .grid {{ display:grid; grid-template-columns:repeat(auto-fill,minmax(320px,1fr)); gap:14px; }}
  .card {{ background:var(--card); border:1px solid #1f2533; border-radius:12px; padding:16px; }}
  .card.big {{ grid-column:1/-1; }}
  .meta {{ display:flex; gap:10px; align-items:center; font-size:12px; color:var(--mut); }}
  .src {{ background:#1f2533; padding:2px 8px; border-radius:20px; text-transform:uppercase; }}
  .score {{ color:#ff9d5b; font-weight:600; margin-left:auto; }}
  .card h3 {{ font-size:16px; margin:10px 0 8px; line-height:1.35; }}
  .card.big h3 {{ font-size:20px; }}
  .card h3 a {{ color:var(--fg); text-decoration:none; }}
  .card h3 a:hover {{ color:var(--acc); }}
  .summary {{ color:var(--mut); font-size:13px; margin:0; }}
  .day {{ font-size:15px; color:var(--mut); margin:28px 0 10px; border-bottom:1px solid #1f2533; padding-bottom:6px; }}
  .stack {{ display:grid; grid-template-columns:repeat(auto-fill,minmax(320px,1fr)); gap:12px; }}
</style>
</head>
<body>
<header>
  <h1>Athena AI News</h1>
  <div class="sub">Auto-ranked by Clickability Index · decays with age so the stack flows top → bottom · generated {now} · {len(items)} stories</div>
</header>
<main>
  <h2 class="sech">\U0001f534 Top News</h2>
  <div class="grid">{top_html}</div>
  <h2 class="sech">\U0001f4f0 The Stack</h2>
  {stack_html}
</main>
</body>
</html>"""


def render(dry_run=False, webroot=None):
    """Run the full render pipeline.

    Returns (output_path, item_count).
    """
    conn = sqlite3.connect(str(DB_PATH))
    items = fetch_items(conn)
    conn.close()

    items = compute_index(items)
    items = decay_index(items, HALF_LIFE_H)
    page = build_html(items)

    if dry_run:
        out = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_preview.html")
        with open(out, "w") as f:
            f.write(page)
        fresh = [it for it in items if it.get("fresh")]
        top = sorted(fresh, key=lambda x: x["clickability_decayed"], reverse=True)[:TOP_N]
        print(f"[dry-run] wrote {out} ({len(items)} items, {len(fresh)} fresh today)")
        print(f"TOP {TOP_N} FRESH (today only) by decayed clickability:")
        for i, it in enumerate(top, 1):
            print(f"  {i}. [{it['clickability_decayed']:.2f} | age {it['age_hours']:.0f}h] {it['source']:10} {it['title'][:55]}")
        return out, len(items)

    target = webroot or (WEBROOT if os.path.isdir(WEBROOT) else str(FALLBACK_WEBROOT))
    os.makedirs(target, exist_ok=True)

    with open(os.path.join(target, "index.html"), "w") as f:
        f.write(page)

    with open(os.path.join(target, "feed.json"), "w") as f:
        json.dump([
            {"title": i["title"], "url": i["url"], "source": i["source"],
             "clickability_decayed": i["clickability_decayed"], "age_hours": i["age_hours"],
             "first_seen": i.get("first_seen")}
            for i in sorted(items, key=lambda x: x["clickability_decayed"], reverse=True)
        ], f, indent=2)

    where = "WEBROOT" if target == WEBROOT else "fallback(~oracle/site)"
    print(f"[render] wrote {target}/index.html ({len(items)} items) -> {where}")
    return os.path.join(target, "index.html"), len(items)


# ── Variant rendering ──────────────────────────────────────────────────────

def _variant_card(it: dict, display: dict, big: bool = False) -> str:
    """Build an HTML card for a variant edition."""
    title = html.escape(it.get("title") or "(untitled)")
    url = html.escape(it.get("url") or "#")
    src = html.escape(it.get("source", ""))
    t = _fmt_time(it.get("first_seen"))
    summary_raw = _clean_summary(it.get("summary") or "")
    summary = html.escape(summary_raw[:200])

    score = it.get("signal_score")
    tier = it.get("source_tier")
    verdict = it.get("verdict", "")

    cls = "card big" if big else "card"
    summary_html = ('<p class="summary">{}</p>'.format(summary)) if (summary and display.get("show_summary") and big) else ""

    # Build meta badges
    badges = f'<span class="src">{src}</span>'
    if t:
        badges += f'<span class="time">{t}</span>'
    if display.get("show_score") and score is not None:
        badges += f'<span class="sig">sig {float(score):.1f}</span>'
    if display.get("show_tier") and tier is not None:
        badges += f'<span class="tier">T{tier}</span>'
    if display.get("show_verdict") and verdict:
        badges += f'<span class="verdict verdict-{verdict.lower()}">{verdict}</span>'

    return f"""
      <article class="{cls}">
        <div class="meta">{badges}</div>
        <h3><a href="{url}" target="_blank" rel="noopener">{title}</a></h3>
        {summary_html}
      </article>"""


def render_variant_html(items: list[dict], variant: dict) -> str:
    """Build the full HTML page for a variant edition."""
    display = variant.get("display", {})
    name = variant.get("name", "Athena")
    desc = variant.get("description", "")
    logo = display.get("logo", "🏛️")
    top_n = display.get("top_n", 8)
    half_life = variant.get("ranking", {}).get("half_life_h", 18)

    now = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M UTC")

    top = items[:top_n]
    stack = items[top_n:]

    # Group stack by day
    by_day = OrderedDict()
    for it in stack:
        day = (it.get("first_seen") or "")[:10] or "unknown"
        by_day.setdefault(day, []).append(it)

    top_html = "\n".join(_variant_card(it, display, big=True) for it in top)
    stack_html = ""
    for day, rows in by_day.items():
        cards = "".join(_variant_card(it, display) for it in rows)
        stack_html += f"""
    <h3 class="day">🗒️ {html.escape(day)}</h3>
    <div class="stack">{cards}</div>"""

    theme = _theme_css(display)

    return f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{html.escape(name)}</title>
<style>
  {theme}
  * {{ box-sizing:border-box; }}
  body {{ margin:0; background:var(--bg); color:var(--fg);
    font:15px/1.5 -apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; }}
  header {{ padding:28px 20px 14px; border-bottom:1px solid var(--border); text-align:center; }}
  header h1 {{ margin:0; font-size:28px; letter-spacing:.5px; }}
  header .sub {{ color:var(--mut); font-size:13px; margin-top:6px; }}
  main {{ max-width:1000px; margin:0 auto; padding:20px; }}
  h2.sech {{ font-size:18px; margin:26px 0 12px; border-left:3px solid var(--acc); padding-left:10px; }}
  .grid {{ display:grid; grid-template-columns:repeat(auto-fill,minmax(320px,1fr)); gap:14px; }}
  .card {{ background:var(--card); border:1px solid var(--border); border-radius:12px; padding:16px; }}
  .card.big {{ grid-column:1/-1; }}
  .meta {{ display:flex; gap:10px; align-items:center; font-size:12px; color:var(--mut); flex-wrap:wrap; }}
  .src {{ background:var(--border); padding:2px 8px; border-radius:20px; text-transform:uppercase; }}
  .sig {{ color:var(--acc); font-weight:600; }}
  .tier {{ color:var(--mut); }}
  .verdict {{ padding:2px 6px; border-radius:4px; font-weight:600; text-transform:uppercase; font-size:10px; }}
  .verdict-publish {{ background:#065f46; color:#a7f3d0; }}
  .verdict-watch {{ background:#1e3a5f; color:#93c5fd; }}
  .verdict-archive {{ background:#4a3b1f; color:#fcd34d; }}
  .verdict-drop {{ background:#4a1f1f; color:#fca5a5; }}
  .card h3 {{ font-size:16px; margin:10px 0 8px; line-height:1.35; }}
  .card.big h3 {{ font-size:20px; }}
  .card h3 a {{ color:var(--fg); text-decoration:none; }}
  .card h3 a:hover {{ color:var(--acc); }}
  .summary {{ color:var(--mut); font-size:13px; margin:0; }}
  .day {{ font-size:15px; color:var(--mut); margin:28px 0 10px; border-bottom:1px solid var(--border); padding-bottom:6px; }}
  .stack {{ display:grid; grid-template-columns:repeat(auto-fill,minmax(320px,1fr)); gap:12px; }}
  footer {{ text-align:center; padding:20px; color:var(--mut); font-size:12px; border-top:1px solid var(--border); margin-top:40px; }}
</style>
</head>
<body>
<header>
  <h1>{logo} {html.escape(name)}</h1>
  <div class="sub">{html.escape(desc)} · generated {now} · {len(items)} stories</div>
</header>
<main>
  <h2 class="sech">🔥 Top News</h2>
  <div class="grid">{top_html}</div>
  {('<h2 class="sech">🗄️ The Stack</h2>' + stack_html) if stack else ''}
</main>
<footer>
  Athena AI Research Oracle · <a href="feed.json">feed.json</a>
</footer>
</body>
</html>"""


def render_variant_json(items: list[dict], variant: dict, path: str) -> None:
    """Write a JSON feed for a variant edition."""
    display = variant.get("display", {})
    feed = []
    for i in items:
        entry = {
            "title": i.get("title", ""),
            "url": i.get("url", ""),
            "source": i.get("source", ""),
            "signal_score": float(i.get("signal_score") or 0),
            "first_seen": i.get("first_seen", ""),
        }
        if display.get("show_tier"):
            entry["source_tier"] = i.get("source_tier")
        if display.get("show_verdict"):
            entry["verdict"] = i.get("verdict", "")
        feed.append(entry)

    with open(path, "w") as f:
        json.dump(feed, f, indent=2)
