#!/usr/bin/env python3
"""
journal_daily.py - Daily journal scout for Bo Wang
Fetches recent papers from Nature/Science/Cell series via PubMed API (abstracts included).
Usage: python3 journal_daily.py [telegram|email]
"""

import urllib.request
import urllib.parse
import xml.etree.ElementTree as ET
import re
import sys
from datetime import datetime, timedelta, timezone

# ── Journals ─────────────────────────────────────────────────────────────────

JOURNALS = [
    # Nature series
    "Nature",
    "Nature methods",
    "Nature biotechnology",
    "Nature medicine",
    "Nature genetics",
    "Nature cell biology",
    "Nature chemical biology",
    "Nature structural & molecular biology",
    "Nature communications",
    "Nature aging",
    "Nature computational science",
    # Science series
    "Science",
    "Science advances",
    "Science translational medicine",
    "Science immunology",
    # Cell series
    "Cell",
    "Molecular cell",
    "Cancer cell",
    "Cell systems",
    "Cell reports",
    "Cell chemical biology",
    "Cell host & microbe",
    "Cell stem cell",
]

# ── Scoring (same weights as arxiv_daily.py) ─────────────────────────────────

HIGH_PRIORITY = [
    r"single.cell", r"scrna.?seq", r"scatac", r"spatial transcriptomics",
    r"cell atlas", r"drug discovery", r"drug design", r"molecular generation",
    r"protein structure", r"foundation model", r"virtual cell", r"perturbation",
    r"gene regulatory network", r"cell type", r"multiom", r"multi.om",
    r"scgpt", r"crispr", r"rna.seq", r"trajectory inference",
    r"cancer genomics", r"epigenomics", r"deep mutational",
]

MEDIUM_PRIORITY = [
    r"large language model", r"\bllm\b", r"transformer", r"reinforcement learning",
    r"\bagent\b", r"generative model", r"diffusion model",
    r"genomics", r"proteomics", r"biomarker", r"clinical trial", r"medical imaging",
    r"zero.shot", r"few.shot", r"self.supervised", r"representation learning",
    r"deep learning", r"neural network", r"machine learning",
    r"drug target", r"molecular docking", r"sequence model",
    r"immunotherapy", r"tumor microenvironment", r"cell therapy",
    r"organoid", r"genome editing", r"base editing", r"prime editing",
]

# ── PubMed fetch ──────────────────────────────────────────────────────────────

BASE   = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
EMAIL  = "moon.clawdbot@gmail.com"   # PubMed asks for contact email in query

def build_journal_query(days_back=7):
    journal_terms = " OR ".join(f'"{j}"[Journal]' for j in JOURNALS)
    cutoff = (datetime.now(timezone.utc) - timedelta(days=days_back)).strftime("%Y/%m/%d")
    date_filter = f'("{cutoff}"[Date - Publication] : "3000"[Date - Publication])'
    return f"({journal_terms}) AND {date_filter}"

def esearch(query, max_results=200):
    params = urllib.parse.urlencode({
        "db":      "pubmed",
        "term":    query,
        "retmax":  max_results,
        "sort":    "pub date",
        "retmode": "json",
        "tool":    "journal-daily-scout",
        "email":   EMAIL,
    })
    url = f"{BASE}/esearch.fcgi?{params}"
    req = urllib.request.Request(url, headers={"User-Agent": "journal-daily-scout/1.0"})
    with urllib.request.urlopen(req, timeout=30) as r:
        import json
        data = json.loads(r.read())
    return data.get("esearchresult", {}).get("idlist", [])

def efetch(pmids):
    if not pmids:
        return []
    params = urllib.parse.urlencode({
        "db":      "pubmed",
        "id":      ",".join(pmids),
        "rettype": "abstract",
        "retmode": "xml",
        "tool":    "journal-daily-scout",
        "email":   EMAIL,
    })
    url = f"{BASE}/efetch.fcgi?{params}"
    req = urllib.request.Request(url, headers={"User-Agent": "journal-daily-scout/1.0"})
    with urllib.request.urlopen(req, timeout=30) as r:
        return r.read().decode("utf-8")

def parse_articles(xml_str):
    root = ET.fromstring(xml_str)
    articles = []
    for art in root.findall(".//PubmedArticle"):
        # Title
        title_el = art.find(".//ArticleTitle")
        title = "".join(title_el.itertext()).strip() if title_el is not None else ""
        title = re.sub(r"\s+", " ", title)

        # Abstract
        abstract_parts = art.findall(".//AbstractText")
        abstract = " ".join(
            "".join(el.itertext()).strip()
            for el in abstract_parts
        ).strip()
        abstract = re.sub(r"\s+", " ", abstract)

        # Authors
        authors = []
        for author in art.findall(".//Author")[:4]:
            ln = author.findtext("LastName", "")
            fn = author.findtext("ForeName", "")
            if ln:
                authors.append(f"{fn} {ln}".strip())

        # Journal
        journal = art.findtext(".//Journal/Title", "") or art.findtext(".//ISOAbbreviation", "")

        # DOI
        doi = ""
        for aid in art.findall(".//ArticleId"):
            if aid.get("IdType") == "doi":
                doi = aid.text or ""
        
        # PMID
        pmid_el = art.find(".//PMID")
        pmid = pmid_el.text if pmid_el is not None else ""

        # Date
        pub_date = art.find(".//PubDate")
        year  = pub_date.findtext("Year", "")  if pub_date is not None else ""
        month = pub_date.findtext("Month", "") if pub_date is not None else ""
        day   = pub_date.findtext("Day", "")   if pub_date is not None else ""
        date_str = f"{year} {month} {day}".strip()

        url = f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/" if pmid else (
              f"https://doi.org/{doi}" if doi else "")

        if not title:
            continue

        articles.append({
            "title":    title,
            "abstract": abstract,
            "authors":  authors,
            "journal":  journal,
            "doi":      doi,
            "pmid":     pmid,
            "date":     date_str,
            "url":      url,
        })
    return articles

# ── Scoring ───────────────────────────────────────────────────────────────────

def score(paper):
    text = (paper["title"] + " " + paper["abstract"]).lower()
    s = 0
    for kw in HIGH_PRIORITY:
        if re.search(kw, text):
            s += 3
    for kw in MEDIUM_PRIORITY:
        if re.search(kw, text):
            s += 1
    return s

# ── Format ────────────────────────────────────────────────────────────────────

def format_report(papers, mode="telegram"):
    today = datetime.now().strftime("%B %d, %Y")
    if mode == "telegram":
        lines = [f"🔬 *Journal Scout — {today}*\n_(Nature · Science · Cell series)_\n"]
        for i, p in enumerate(papers, 1):
            authors = ", ".join(p["authors"][:2])
            if len(p["authors"]) > 2:
                authors += " et al."
            snippet = (p["abstract"][:280].rstrip() + "…") if p["abstract"] else "_No abstract available_"
            lines.append(
                f"*{i}. {p['title']}*\n"
                f"_{authors}_ · _{p['journal']}_ · {p['date']}\n"
                f"{snippet}\n"
                f"🔗 {p['url']}\n"
            )
        return "\n".join(lines)
    else:
        lines = [f"Journal Scout — {today}\nNature · Science · Cell series\n{'='*50}\n"]
        for i, p in enumerate(papers, 1):
            authors = ", ".join(p["authors"][:2])
            if len(p["authors"]) > 2:
                authors += " et al."
            snippet = p["abstract"][:400].rstrip() + ("…" if p["abstract"] else "")
            lines.append(
                f"{i}. {p['title']}\n"
                f"   {authors} | {p['journal']} | {p['date']}\n"
                f"   {snippet}\n"
                f"   {p['url']}\n"
            )
        return "\n".join(lines)

# ── Main ──────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    mode = sys.argv[1] if len(sys.argv) > 1 else "telegram"

    query  = build_journal_query(days_back=7)
    pmids  = esearch(query, max_results=200)

    if not pmids:
        print(f"No papers found in the last 7 days." if mode != "telegram"
              else "🔬 *Journal Scout* — no new papers found in the last 7 days.")
        sys.exit(0)

    xml_str  = efetch(pmids[:100])   # cap at 100 for efetch batch
    articles = parse_articles(xml_str)

    for a in articles:
        a["score"] = score(a)

    top5 = sorted(articles, key=lambda x: x["score"], reverse=True)[:5]
    print(format_report(top5, mode=mode))
