#!/usr/bin/env python3
"""Smart image capture for taste board entries.

Given a URL, extracts the best representative image of the creative work featured,
rather than screenshotting the blog chrome.

Strategy chain:
1. OG image (og:image meta tag) — if large enough and not a logo
2. Largest content image on the page (skip nav, icons, ads)
3. Fallback: screenshot scrolled past the header
"""

import argparse
import os
import re
import sys
import tempfile
import urllib.request
import urllib.error
from pathlib import Path
from urllib.parse import urljoin, urlparse

from bs4 import BeautifulSoup


def _fetch_html(url: str, timeout: int = 10) -> str | None:
    """Fetch HTML with a browser-like UA."""
    req = urllib.request.Request(url, 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",
    })
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return resp.read().decode("utf-8", errors="replace")
    except Exception:
        return None


def _download_image(img_url: str, dest: str, timeout: int = 10) -> bool:
    """Download image to dest. Returns True if file > 50KB."""
    req = urllib.request.Request(img_url, headers={
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
    })
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            data = resp.read()
        if len(data) < 50_000:
            return False
        with open(dest, "wb") as f:
            f.write(data)
        return True
    except Exception:
        return False


def _is_logo_url(url: str) -> bool:
    """Heuristic: skip URLs that look like site logos/icons."""
    low = url.lower()
    return any(k in low for k in ("logo", "icon", "favicon", "badge", "avatar", "sprite", "banner-ad"))


def _try_og_image(url: str, output_path: str) -> bool:
    """Attempt 1: extract og:image and download it."""
    html = _fetch_html(url)
    if not html:
        return False
    soup = BeautifulSoup(html, "html.parser")
    og = soup.find("meta", property="og:image")
    if not og:
        og = soup.find("meta", attrs={"name": "og:image"})
    if not og or not og.get("content"):
        return False
    img_url = urljoin(url, og["content"].strip())
    if _is_logo_url(img_url):
        return False
    return _download_image(img_url, output_path)


def _try_largest_image(url: str, output_path: str) -> bool:
    """Attempt 2: use Playwright to find the largest visible content image."""
    try:
        from playwright.sync_api import sync_playwright
    except ImportError:
        return False

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page(viewport={"width": 1440, "height": 900})
        try:
            page.goto(url, timeout=15000, wait_until="domcontentloaded")
            # Dismiss cookie banners / popups
            _dismiss_popups(page)
            page.wait_for_timeout(2000)

            # Find all images, pick the largest by rendered area
            best = page.evaluate("""() => {
                const imgs = Array.from(document.querySelectorAll('img'));
                let best = null, bestArea = 0;
                for (const img of imgs) {
                    const rect = img.getBoundingClientRect();
                    const area = rect.width * rect.height;
                    const src = img.currentSrc || img.src || '';
                    if (area < 10000) continue;  // skip tiny
                    if (rect.width < 200 || rect.height < 150) continue;
                    // skip stuff in nav/header/footer
                    const parent = img.closest('nav, header, footer, [role="banner"], [role="navigation"]');
                    if (parent) continue;
                    const low = src.toLowerCase();
                    if (/logo|icon|favicon|badge|avatar|sprite|banner-ad|pixel/.test(low)) continue;
                    if (area > bestArea) { bestArea = area; best = src; }
                }
                return best;
            }""")

            if best:
                img_url = urljoin(url, best)
                if _download_image(img_url, output_path):
                    return True
        except Exception:
            pass
        finally:
            browser.close()
    return False


def _try_scroll_screenshot(url: str, output_path: str) -> bool:
    """Attempt 3: screenshot the page scrolled past the header."""
    try:
        from playwright.sync_api import sync_playwright
    except ImportError:
        return False

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page(viewport={"width": 1440, "height": 900})
        try:
            page.goto(url, timeout=15000, wait_until="domcontentloaded")
            _dismiss_popups(page)
            page.wait_for_timeout(2000)

            # Scroll past header (~400px)
            page.evaluate("window.scrollTo(0, 400)")
            page.wait_for_timeout(500)

            page.screenshot(path=output_path, type="jpeg", quality=90)
            return os.path.exists(output_path) and os.path.getsize(output_path) > 5000
        except Exception:
            return False
        finally:
            browser.close()


def _dismiss_popups(page):
    """Try to dismiss cookie banners and popups."""
    selectors = [
        'button:has-text("Accept")', 'button:has-text("accept")',
        'button:has-text("OK")', 'button:has-text("Got it")',
        'button:has-text("Close")', 'button:has-text("Agree")',
        'button:has-text("I agree")', 'button:has-text("Allow")',
        '[id*="cookie"] button', '[class*="cookie"] button',
        '[id*="consent"] button', '[class*="consent"] button',
        '[aria-label="Close"]', '[aria-label="close"]',
    ]
    for sel in selectors:
        try:
            btn = page.locator(sel).first
            if btn.is_visible(timeout=500):
                btn.click(timeout=1000)
                return
        except Exception:
            continue


def capture_work_image(url: str, output_dir: str, filename: str) -> str | None:
    """Capture the best image for a creative work URL.

    Returns the path to the saved JPG, or None on failure.
    """
    os.makedirs(output_dir, exist_ok=True)
    if not filename.lower().endswith((".jpg", ".jpeg")):
        filename += ".jpg"
    output_path = os.path.join(output_dir, filename)

    # Strategy 1: OG image
    if _try_og_image(url, output_path):
        return output_path

    # Strategy 2: Largest content image via Playwright
    if _try_largest_image(url, output_path):
        return output_path

    # Strategy 3: Scroll-past-header screenshot
    if _try_scroll_screenshot(url, output_path):
        return output_path

    return None


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Smart capture of creative work images from URLs")
    parser.add_argument("url", help="URL to capture")
    parser.add_argument("--output", "-o", default=".", help="Output directory")
    parser.add_argument("--name", "-n", default="capture", help="Output filename (without extension)")
    args = parser.parse_args()

    result = capture_work_image(args.url, args.output, args.name)
    if result:
        print(f"Saved: {result}")
    else:
        print("Failed to capture image", file=sys.stderr)
        sys.exit(1)
