"""Server-rendered share/landing page for /a/<id>.

A *pointer* page (never a republished article): the story's own title, "why it's
uplifting" note, and image, wrapped with rich OpenGraph/Twitter meta and a
prominent link to the original source. Social scrapers don't run JS, so this is
plain server-rendered HTML. All dynamic values are HTML-escaped.
"""

from __future__ import annotations

from html import escape


def _tag(name: str, content: str | None, attr: str = "property") -> str:
    if not content:
        return ""
    return f'<meta {attr}="{escape(name)}" content="{escape(content)}">'


def render_share_page(article: dict, base_url: str, summary: str | None = None,
                      explanation: dict | None = None) -> str:
    aid = article["id"]
    title = (article.get("title") or "Upbeat Bytes").strip()
    why = (article.get("reason_text") or article.get("description")
           or "A calm, constructive story worth your attention.").strip()
    source = (article.get("source_name") or "the source").strip()
    source_id = article.get("source_id")
    # Link the source name into the app's publication feed for that source.
    source_html = (
        f'<a class="src srclink" href="/?source={source_id}">{escape(source)}</a>'
        if source_id else f'<div class="src">{escape(source)}</div>'
    )
    src_url = article.get("canonical_url") or base_url
    image = article.get("image_url")
    page_url = f"{base_url}/a/{aid}"
    # With an image: a large-image card. Without: a clean text unfurl (title +
    # why + brand) — tidy, not a broken/muddy preview. (A branded fallback PNG
    # can replace this later.)
    twitter_card = "summary_large_image" if image else "summary"

    meta = "\n".join(filter(None, [
        _tag("og:site_name", "Upbeat Bytes"),
        _tag("og:type", "article"),
        _tag("og:title", title),
        _tag("og:description", why),
        _tag("og:url", page_url),
        _tag("og:image", image),
        _tag("twitter:card", twitter_card, attr="name"),
        _tag("twitter:title", title, attr="name"),
        _tag("twitter:description", why, attr="name"),
        _tag("twitter:image", image, attr="name"),
    ]))

    media = (
        f'<img class="media" src="{escape(image)}" alt="" referrerpolicy="no-referrer">'
        if image else ""
    )

    raw_tags = (article.get("tags") or "")
    chips = "".join(
        f'<span class="chip">{escape(t.replace("-", " "))}</span>'
        for t in raw_tags.split(",") if t
    )
    groupings = f'<div class="chips">{chips}</div>' if chips else ""
    if summary:
        summary_block = f'<p class="summary" id="summary">{escape(summary)}</p>'
    else:
        pending = (
            f"✦ We’re putting together a quick summary — in the meantime, "
            f"read the full story at {escape(source)} below."
        )
        summary_block = f'<p class="summary pending" id="summary">{pending}</p>'

    # The structured "Why it belongs" editorial section — only when the LLM gave a
    # clean three-part read; otherwise the single reason line is the calm fallback.
    def _why_row(lbl: str, text: str) -> str:
        return f'<div class="why-row"><span class="lbl">{lbl}</span><p>{escape(text)}</p></div>'

    if explanation:
        why_block = (
            '<div class="why" id="why">'
            + _why_row("What happened", explanation["what_happened"])
            + _why_row("Why it matters", explanation["why_matters"])
            + _why_row("Why it belongs here", explanation["why_belongs"])
            + '</div>'
        )
    else:
        why_block = f'<div class="why" id="why"><div class="why-row"><span class="lbl">Why it’s here</span><p>{escape(why)}</p></div></div>'

    # Quietly poll until BOTH the summary and the structured read are cached, then
    # swap each in (older summaries get topped up with the section on first view).
    poll = ""
    if not summary or not explanation:
        poll = f"""<script>
(function(){{
  var id={aid}, box=document.getElementById('summary'), n=0;
  function row(l,t){{
    var d=document.createElement('div'); d.className='why-row';
    var s=document.createElement('span'); s.className='lbl'; s.textContent=l;
    var p=document.createElement('p'); p.textContent=t;
    d.appendChild(s); d.appendChild(p); return d;
  }}
  function go(){{
    n++;
    fetch('/api/summary/'+id).then(function(r){{return r.json();}}).then(function(d){{
      var done=true;
      if(d&&d.summary){{ box.textContent=d.summary; box.className='summary'; }} else {{ done=false; }}
      if(d&&d.explanation){{
        var w=document.getElementById('why');
        if(w){{ w.innerHTML=''; w.appendChild(row('What happened',d.explanation.what_happened));
          w.appendChild(row('Why it matters',d.explanation.why_matters));
          w.appendChild(row('Why it belongs here',d.explanation.why_belongs)); }}
      }} else {{ done=false; }}
      if(!done && n<12){{ setTimeout(go,2500); }}
    }}).catch(function(){{ if(n<12) setTimeout(go,3000); }});
  }}
  go();
}})();
</script>"""

    # Fire a privacy-respecting 'source_click' when the reader heads to the source
    # (reuses the SPA's anonymous visitor token from same-origin localStorage).
    src_click = f"""<script>
(function(){{
  var b=document.querySelector('[data-src-click]'); if(!b) return;
  b.addEventListener('click',function(){{
    try{{
      var v=localStorage.getItem('goodnews:visitor')||'';
      var body=JSON.stringify({{kind:'source_click',article_id:{aid},visitor:v}});
      if(navigator.sendBeacon) navigator.sendBeacon('/api/events',new Blob([body],{{type:'application/json'}}));
    }}catch(e){{}}
  }});
}})();
</script>"""

    return f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{escape(title)} · Upbeat Bytes</title>
<meta name="description" content="{escape(why)}">
<link rel="canonical" href="{escape(page_url)}">
<link rel="icon" href="/favicon.svg">
{meta}
<style>
  :root {{ --accent:#0083ad; --accent-deep:#006b8e; --ink:#16263a; --muted:#5d6b78;
           --bg:#f7f4ec; --surface:#fffdf8; --line:#e8e3d8; }}
  * {{ box-sizing:border-box; }}
  body {{ margin:0; background:var(--bg); color:var(--ink);
          font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
          line-height:1.6; }}
  .bar {{ background:var(--surface); border-bottom:1px solid var(--line); }}
  .bar .inner {{ max-width:680px; margin:0 auto; padding:12px 20px;
                 display:flex; align-items:center; justify-content:space-between; gap:12px; }}
  .bar img {{ height:40px; display:block; }}
  .back {{ display:inline-flex; align-items:center; gap:7px;
           background:none; border:1px solid var(--line); color:var(--accent-deep);
           border-radius:999px; padding:8px 17px 8px 14px; font-size:.92rem; font-weight:600;
           font-family:inherit; cursor:pointer; line-height:1; white-space:nowrap;
           transition:border-color .14s ease, background .14s ease; }}
  .back svg {{ width:19px; height:19px; display:block; }}
  .back:hover {{ border-color:var(--accent); background:var(--bg); }}
  .wrap {{ max-width:680px; margin:0 auto; padding:24px 20px 60px; }}
  .card {{ background:var(--surface); border:1px solid var(--line); border-radius:16px;
           overflow:hidden; box-shadow:0 10px 30px rgba(40,38,28,.06); }}
  .media {{ width:100%; height:auto; display:block; max-height:380px; object-fit:cover; }}
  .body {{ padding:24px 26px 28px; }}
  .src {{ color:var(--muted); font-size:.82rem; text-transform:uppercase; letter-spacing:.08em; }}
  a.srclink {{ text-decoration:none; cursor:pointer; border-bottom:1px dotted transparent; }}
  a.srclink:hover {{ color:var(--accent-deep); border-bottom-color:var(--line); }}
  h1 {{ font-family:"Iowan Old Style",Palatino,Georgia,serif; font-weight:600;
        font-size:1.7rem; line-height:1.2; margin:6px 0 14px; }}
  .summary {{ font-size:1.05rem; color:var(--ink); margin:0 0 18px; }}
  .summary.pending {{ color:var(--muted); font-style:italic; }}
  .why {{ color:#3b4754; font-size:.92rem; margin:0 0 22px;
          border-left:2px solid var(--accent); padding-left:14px; }}
  .why-row {{ margin:0 0 12px; }}
  .why-row:last-child {{ margin-bottom:0; }}
  .why-row p {{ margin:0; }}
  .why .lbl {{ display:block; text-transform:uppercase; letter-spacing:.08em; font-size:.7rem;
               color:var(--accent-deep); font-weight:600; margin-bottom:2px; }}
  .chips {{ display:flex; flex-wrap:wrap; gap:7px; margin:0 0 22px; }}
  .chip {{ background:#e0eef3; color:var(--accent-deep); border-radius:999px; padding:3px 11px;
           font-size:.7rem; text-transform:uppercase; letter-spacing:.06em; }}
  .actions {{ display:flex; flex-wrap:wrap; gap:12px; align-items:center; }}
  .primary {{ background:var(--accent); color:#fff; text-decoration:none; font-weight:600;
              padding:12px 20px; border-radius:999px; display:inline-block; }}
  .primary:hover {{ background:var(--accent-deep); }}
  .secondary {{ color:var(--accent-deep); text-decoration:none; font-size:.92rem;
                background:none; border:none; cursor:pointer; font-family:inherit; padding:0; }}
  .secondary:hover {{ text-decoration:underline; }}
  .note {{ color:var(--muted); font-size:.8rem; margin-top:22px; }}
</style>
</head>
<body>
  <div class="bar"><div class="inner"><a href="/"><img src="/logo.svg" alt="Upbeat Bytes"></a><button class="back" type="button" data-back aria-label="Go back"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M11 6l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round"/></svg>Back</button></div></div>
  <main class="wrap">
    <article class="card">
      {media}
      <div class="body">
        {source_html}
        <h1>{escape(title)}</h1>
        {summary_block}
        {why_block}
        {groupings}
        <div class="actions">
          <a class="primary" href="{escape(src_url)}" target="_blank" rel="noopener" data-src-click>Read the full story at {escape(source)}</a>
          <button class="secondary" type="button" data-share>Copy link</button>
          <a class="secondary" href="/">Explore Upbeat Bytes →</a>
        </div>
        <p class="note">Upbeat Bytes summarizes in its own words and links to the original publisher — it doesn't host the article.</p>
      </div>
    </article>
  </main>
  <script>
(function(){{
  try{{
    var v=localStorage.getItem('goodnews:visitor');
    if(!v){{v=crypto.randomUUID?crypto.randomUUID():String(Math.random()).slice(2)+Date.now();localStorage.setItem('goodnews:visitor',v);}}
    function beacon(o){{var b=JSON.stringify(o);if(navigator.sendBeacon)navigator.sendBeacon('/api/events',new Blob([b],{{type:'application/json'}}));}}
    beacon({{kind:'summary_viewed',article_id:{aid},visitor:v}});
    // This page is server-rendered (outside the Svelte app), so the SPA's daily
    // visit isn't recorded for a /a/ landing — count it here, once per day per device.
    var t=new Date().toISOString().slice(0,10);
    if(localStorage.getItem('goodnews:visitday')!==t){{localStorage.setItem('goodnews:visitday',t);beacon({{kind:'visit',article_id:0,visitor:v}});}}
  }}catch(e){{}}
}})();
</script>
  {src_click}
  <script>
(function(){{
  var s=document.querySelector('[data-share]'); if(!s) return;
  s.addEventListener('click',function(){{
    var url=location.href;
    if(navigator.share){{ navigator.share({{title:document.title,url:url}}).catch(function(){{}}); }}
    else if(navigator.clipboard){{ navigator.clipboard.writeText(url).then(function(){{ s.textContent='Copied!'; setTimeout(function(){{s.textContent='Copy link';}},1500); }}); }}
  }});
}})();
</script>
  <script>
(function(){{
  var b=document.querySelector('[data-back]'); if(!b) return;
  b.addEventListener('click',function(){{
    if(document.referrer && history.length>1){{ history.back(); }} else {{ location.href='/'; }}
  }});
}})();
</script>
  {poll}
</body>
</html>"""


def render_digest(items: list[dict], base_url: str, brief_date: str | None) -> str:
    """A shareable, indexable 'today's good news, summarized' page."""
    lead_img = next((i.get("image_url") for i in items if i.get("image_url")), None)
    intro = "The day's calm, constructive news — summarized in plain English, so you can get the gist and go deeper only if you want."
    page_url = f"{base_url}/today"

    cards = ""
    for it in items:
        aid = it["id"]
        title = escape((it.get("title") or "").strip())
        source = escape((it.get("source_name") or "the source").strip())
        src_url = escape(it.get("canonical_url") or base_url)
        gist = escape((it.get("summary") or it.get("reason_text") or it.get("description") or "").strip())
        cards += (
            '<article class="item">'
            f'<a class="t" href="/a/{aid}"><h2>{title}</h2></a>'
            f'<div class="src">{source}</div>'
            f'<p class="gist">{gist}</p>'
            f'<div class="links"><a href="/a/{aid}">Read the summary</a>'
            f'<a href="{src_url}" rel="noopener">Full story at {source} →</a></div>'
            "</article>"
        )

    meta = "\n".join(filter(None, [
        _tag("og:site_name", "Upbeat Bytes"),
        _tag("og:type", "website"),
        _tag("og:title", "Today's good news, summarized"),
        _tag("og:description", intro),
        _tag("og:url", page_url),
        _tag("og:image", lead_img),
        _tag("twitter:card", "summary_large_image" if lead_img else "summary", attr="name"),
        _tag("twitter:title", "Today's good news, summarized", attr="name"),
        _tag("twitter:description", intro, attr="name"),
        _tag("twitter:image", lead_img, attr="name"),
    ]))

    return f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Today's good news, summarized · Upbeat Bytes</title>
<meta name="description" content="{escape(intro)}">
<link rel="canonical" href="{page_url}">
<link rel="icon" href="/favicon.svg">
{meta}
<style>
  :root {{ --accent:#0083ad; --accent-deep:#006b8e; --ink:#16263a; --muted:#5d6b78;
           --bg:#f7f4ec; --surface:#fffdf8; --line:#e8e3d8; }}
  * {{ box-sizing:border-box; }}
  body {{ margin:0; background:var(--bg); color:var(--ink);
          font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; line-height:1.6; }}
  .bar {{ background:var(--surface); border-bottom:1px solid var(--line); }}
  .bar .inner {{ max-width:720px; margin:0 auto; padding:12px 20px; }}
  .bar img {{ height:40px; display:block; }}
  .wrap {{ max-width:720px; margin:0 auto; padding:26px 20px 60px; }}
  h1 {{ font-family:"Iowan Old Style",Palatino,Georgia,serif; font-weight:600; font-size:2rem; margin:0 0 4px; }}
  .lede {{ color:var(--muted); margin:0 0 26px; }}
  .item {{ border-top:1px solid var(--line); padding:20px 0; }}
  .item .t {{ text-decoration:none; color:var(--ink); }}
  .item h2 {{ font-family:"Iowan Old Style",Palatino,Georgia,serif; font-weight:600; font-size:1.3rem; margin:0 0 4px; }}
  .item .t:hover h2 {{ color:var(--accent-deep); }}
  .item .src {{ color:var(--muted); font-size:.78rem; text-transform:uppercase; letter-spacing:.07em; margin-bottom:8px; }}
  .item .gist {{ margin:0 0 10px; }}
  .item .links {{ display:flex; flex-wrap:wrap; gap:16px; font-size:.9rem; }}
  .item .links a {{ color:var(--accent-deep); text-decoration:none; }}
  .item .links a:hover {{ text-decoration:underline; }}
  .more {{ margin-top:28px; }}
  .more a {{ display:inline-block; background:var(--accent); color:#fff; text-decoration:none;
             padding:11px 20px; border-radius:999px; font-weight:600; }}
</style>
</head>
<body>
  <div class="bar"><div class="inner"><a href="/"><img src="/logo.svg" alt="Upbeat Bytes"></a></div></div>
  <main class="wrap">
    <h1>Today's good news</h1>
    <p class="lede">{escape(intro)}{f' · {escape(brief_date)}' if brief_date else ''}</p>
    {cards}
    <p class="more"><a href="/">Browse more on Upbeat Bytes →</a></p>
  </main>
</body>
</html>"""


def render_not_found(base_url: str) -> str:
    return f"""<!doctype html>
<html lang="en"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Story not found · Upbeat Bytes</title><link rel="icon" href="/favicon.svg">
<style>
  body {{ margin:0; min-height:100vh; display:flex; flex-direction:column; align-items:center;
          justify-content:center; gap:14px; text-align:center; padding:40px;
          background:#f7f4ec; color:#16263a;
          font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; }}
  a {{ color:#006b8e; }}
</style></head>
<body>
  <img src="/logo.svg" alt="Upbeat Bytes" style="height:44px">
  <h1 style="font-family:Georgia,serif;font-weight:600">That story isn't here</h1>
  <p style="color:#5d6b78">It may have moved on — the good news refreshes often.</p>
  <a href="/">← Back to Upbeat Bytes</a>
</body></html>"""
