#!/usr/bin/env python3
"""Batch set Notion covers by scraping OG images directly (no Microlink)."""

import argparse, json, os, sys, time, urllib.request, urllib.error, urllib.parse, re, ssl

NOTION_DB = "2ff330c2-8646-81f0-bbd9-ec474393d7a5"

# Lenient SSL context for scraping
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

HEADERS = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"}

def get_notion_key():
    k = os.environ.get("NOTION_KEY", "")
    if not k:
        try:
            k = open("/home/clawd/secrets/notion/api_key").read().strip()
        except: pass
    return k

def query_notion_db(db_id, notion_key, limit=50):
    headers = {
        "Authorization": f"Bearer {notion_key}",
        "Content-Type": "application/json",
        "Notion-Version": "2022-06-28",
    }
    url = f"https://api.notion.com/v1/databases/{db_id}/query"
    pages = []
    body = {"page_size": min(limit, 100)}
    while len(pages) < limit:
        req = urllib.request.Request(url, data=json.dumps(body).encode(), headers=headers, method="POST")
        with urllib.request.urlopen(req, timeout=30) as r:
            resp = json.loads(r.read())
        for page in resp.get("results", []):
            if page.get("cover"):
                continue
            page_url = None
            for prop_name, prop in page.get("properties", {}).items():
                if prop.get("type") == "url" and prop.get("url"):
                    page_url = prop["url"]
                    break
            if page_url:
                title = ""
                for prop_name, prop in page.get("properties", {}).items():
                    if prop.get("type") == "title":
                        title = "".join(t.get("plain_text", "") for t in prop.get("title", []))
                        break
                pages.append({"id": page["id"], "url": page_url, "title": title})
        if not resp.get("has_more") or len(pages) >= limit:
            break
        body["start_cursor"] = resp["next_cursor"]
    return pages[:limit]

def scrape_og_image(url):
    """Scrape og:image or twitter:image from a URL."""
    try:
        req = urllib.request.Request(url, headers=HEADERS)
        with urllib.request.urlopen(req, timeout=15, context=ctx) as r:
            # Read up to 100KB (meta tags are in the head)
            html = r.read(100_000).decode("utf-8", errors="ignore")
        
        # Try og:image first, then twitter:image
        for pattern in [
            r'<meta[^>]+property=["\']og:image["\'][^>]+content=["\']([^"\']+)["\']',
            r'<meta[^>]+content=["\']([^"\']+)["\'][^>]+property=["\']og:image["\']',
            r'<meta[^>]+name=["\']twitter:image["\'][^>]+content=["\']([^"\']+)["\']',
            r'<meta[^>]+content=["\']([^"\']+)["\'][^>]+name=["\']twitter:image["\']',
        ]:
            m = re.search(pattern, html, re.IGNORECASE)
            if m:
                img_url = m.group(1).strip()
                # Make absolute
                if img_url.startswith("//"):
                    img_url = "https:" + img_url
                elif img_url.startswith("/"):
                    from urllib.parse import urlparse
                    p = urlparse(url)
                    img_url = f"{p.scheme}://{p.netloc}{img_url}"
                return img_url
    except Exception as e:
        print(f"  Scrape error: {e}", file=sys.stderr)
    return None

def is_valid_cover(img_url):
    """Filter out SVGs, favicons, tiny logos."""
    if not img_url:
        return False
    low = img_url.lower()
    if low.endswith(".svg"):
        return False
    if "favicon" in low or "logo" in low:
        return False
    # Skip data URIs
    if low.startswith("data:"):
        return False
    # Skip very short paths that are likely icons
    if any(x in low for x in ["icon", "badge", "sprite", "1x1", "pixel"]):
        return False
    return True

def set_notion_cover(page_id, img_url, notion_key):
    """Set external cover image on a Notion page."""
    url = f"https://api.notion.com/v1/pages/{page_id}"
    headers = {
        "Authorization": f"Bearer {notion_key}",
        "Content-Type": "application/json",
        "Notion-Version": "2022-06-28",
    }
    data = {"cover": {"type": "external", "external": {"url": img_url}}}
    req = urllib.request.Request(url, data=json.dumps(data).encode(), headers=headers, method="PATCH")
    with urllib.request.urlopen(req, timeout=30) as r:
        return r.status == 200
    return False

def main():
    p = argparse.ArgumentParser()
    p.add_argument("--limit", type=int, default=50)
    p.add_argument("--db", default=NOTION_DB)
    p.add_argument("--dry-run", action="store_true")
    args = p.parse_args()

    notion_key = get_notion_key()
    if not notion_key:
        print("No Notion API key", file=sys.stderr); sys.exit(1)

    print(f"Querying Notion DB for entries without covers...")
    pages = query_notion_db(args.db, notion_key, args.limit)
    print(f"Found {len(pages)} candidates\n")

    covers_set = 0
    skipped = 0
    errors = 0

    for i, page in enumerate(pages):
        label = page['title'] or page['url']
        print(f"[{i+1}/{len(pages)}] {label}")

        try:
            img = scrape_og_image(page["url"])
            
            if not is_valid_cover(img):
                print(f"  SKIP — no valid image found")
                skipped += 1
                continue

            if args.dry_run:
                print(f"  DRY-RUN — would set: {img[:100]}")
                covers_set += 1
                continue

            set_notion_cover(page["id"], img, notion_key)
            print(f"  ✓ Cover set: {img[:100]}")
            covers_set += 1
            time.sleep(0.4)  # Notion rate limit

        except Exception as e:
            print(f"  ERROR: {e}", file=sys.stderr)
            errors += 1

        time.sleep(1)  # Be polite to target sites

    print(f"\n{'='*50}")
    print(f"Total candidates: {len(pages)}")
    print(f"Covers set:       {covers_set}")
    print(f"Skipped:          {skipped}")
    print(f"Errors:           {errors}")

if __name__ == "__main__":
    main()
