#!/usr/bin/env python3
import re, csv, time, argparse
from pathlib import Path
from datetime import datetime
import requests
from bs4 import BeautifulSoup
from tqdm import tqdm

ARCHIVE_URL_2002 = "https://archive.schillerinstitute.com/calendars/lar_show-2003.html"
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"

def fetch(url, tries=4, timeout=25, stream=False):
    hdrs = {"User-Agent": UA}
    for i in range(tries):
        try:
            r = requests.get(url, headers=hdrs, timeout=timeout, stream=stream, allow_redirects=True)
            r.raise_for_status()
            return r
        except Exception:
            if i == tries - 1:
                raise
            time.sleep(1.2 * (i + 1))

def pick_best_links_for_block(block_html):
    """
    Returns (best_url, best_label, size_mb_float_or_none)
    Strategy:
      1) Prefer any link whose anchor text contains "High-speed" and "Download"
      2) Among those, prefer ones that also mention "Audio and video" (these are special high-quality shows)
      3) If multiple remain, pick the largest "(XX.XX MB)" noted in anchor text
      4) Fallback to any ".mp3" or ".wma/.wmv" Download if present
    """
    soup = BeautifulSoup(block_html, "html.parser")
    candidates = []

    # collect all <a> along with nearby text
    for a in soup.find_all("a"):
        label = a.get_text(" ", strip=True)
        href = a.get("href") or ""
        if not href:
            continue
        t = label.lower()

        # keep only media.schillerinstitute links
        if "media.schillerinstitute.org" not in href:
            continue

        score = 0
        if "download" in t:
            score += 50
        if "high-speed" in t or "high speed" in t or "high" in t:
            score += 30
        if "audio and video" in t:
            score += 25  # special programs with A/V
        if "low-speed" in t or "low speed" in t or "low" in t:
            score -= 40

        # size in MB if present
        size = None
        m = re.search(r"\(([\d.]+)\s*MB\)", label)
        if m:
            try:
                size = float(m.group(1))
                score += min(size / 2.0, 60)  # lightly prefer larger
            except ValueError:
                pass

        # file type hints
        if href.lower().endswith(".mp3"):
            score += 20
        elif href.lower().endswith((".wma", ".wmv", ".asx")):
            score += 5

        candidates.append((score, href, label, size))

    if not candidates:
        return None

    candidates.sort(key=lambda x: x[0], reverse=True)
    _, url, label, size = candidates[0]
    return url, label, size

def parse_2002(archive_url=ARCHIVE_URL_2002):
    r = fetch(archive_url)
    html = r.text
    # Split on the dated headings like "December 28, 2002:" etc.
    # Keep each date block intact so its links are in-window.
    # Use months of 2002 to be robust.
    month_names = ("January|February|March|April|May|June|July|August|September|October|November|December")
    date_re = re.compile(rf"({month_names})\s+\d{{1,2}},\s+2002:", re.I)

    parts = date_re.split(html)
    # parts looks like [pre, Month, rest..., Month, rest...]
    blocks = []
    for i in range(1, len(parts), 2):
        month = parts[i]
        rest = parts[i+1]
        block = month + rest  # re-stitch
        blocks.append(block)

    shows = []
    for b in blocks:
        # date line is the first sentence up to colon
        text = BeautifulSoup(b, "html.parser").get_text("\n")
        first_line = text.strip().split("\n", 1)[0]
        m = re.match(rf"({month_names})\s+(\d{{1,2}}),\s+2002:", first_line, re.I)
        if not m:
            continue
        month, day = m.group(1), m.group(2)
        dt = datetime.strptime(f"{month} {day} 2002", "%B %d %Y")
        ymd = dt.strftime("%Y-%m-%d")

        # title/desc is remainder of the first line after the colon (if any)
        desc = first_line.split(":", 1)[1].strip() if ":" in first_line else "The LaRouche Show"

        best = pick_best_links_for_block(b)
        if not best:
            continue
        url, label, size = best

        shows.append({
            "date": ymd,
            "title": desc if desc else "The LaRouche Show",
            "url": url,
            "label": label,
            "size_mb": size
        })

    # de-dup by date (in case page lists multiple items for same date)
    dedup = {}
    for s in shows:
        key = s["date"]
        # prefer greater size if both present
        cur = dedup.get(key)
        if (cur is None) or ((cur["size_mb"] or 0) < (s["size_mb"] or 0)):
            dedup[key] = s

    return [dedup[k] for k in sorted(dedup.keys())]

def filename_from_response(resp, fallback_stem):
    # Try to infer a filename/extension
    # 1) Content-Disposition
    cd = resp.headers.get("Content-Disposition", "")
    m = re.search(r'filename\*?=("?)([^";]+)\1', cd)
    if m:
        return m.group(2)
    # 2) final URL path
    try:
        tail = Path(resp.url.split("?")[0]).name
        if tail:
            return tail
    except Exception:
        pass
    # 3) MIME type
    ctype = resp.headers.get("Content-Type", "").lower()
    ext = ".mp3" if "mpeg" in ctype else (".wma" if "x-ms-wma" in ctype else (".wmv" if "x-ms-wmv" in ctype else ".bin"))
    return fallback_stem + ext

def download(url, dest_path: Path):
    with fetch(url, stream=True) as r:
        # If dest has no extension, improve it from headers/final url
        if dest_path.suffix == "":
            inferred = filename_from_response(r, dest_path.stem)
            dest_path = dest_path.with_name(inferred)
        total = int(r.headers.get("Content-Length", 0))
        with open(dest_path, "wb") as f, tqdm(total=total, unit="B", unit_scale=True, desc=dest_path.name) as p:
            for chunk in r.iter_content(chunk_size=1 << 15):
                if chunk:
                    f.write(chunk)
                    if total:
                        p.update(len(chunk))
    return dest_path

def safe_slug(s: str) -> str:
    s = s.strip().lower()
    s = re.sub(r"[^\w\s-]", "", s)
    s = re.sub(r"[\s-]+", "_", s)
    return s[:120]  # keep it tidy

def main():
    ap = argparse.ArgumentParser(description="Scrape 2002 LaRouche Show (best quality per show).")
    ap.add_argument("--out", default="larouche_show/2002", help="Output folder (default: larouche_show/2002)")
    ap.add_argument("--skip-download", action="store_true", help="Only build manifest; do not download files.")
    args = ap.parse_args()

    out = Path(args.out)
    out.mkdir(parents=True, exist_ok=True)

    shows = parse_2002()
    if not shows:
        print("No shows parsed for 2002.")
        return

    manifest = out / "urls_2002.csv"
    with open(manifest, "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["date", "title", "url", "label", "size_mb", "suggested_filename"])
        for s in shows:
            stem = f"{s['date']}_{safe_slug(s['title'])}"
            w.writerow([s["date"], s["title"], s["url"], s["label"], s["size_mb"] or "", stem])

    print(f"{len(shows)} shows → {manifest}")

    if args.skip_download:
        return

    for s in shows:
        stem = f"{s['date']}_{safe_slug(s['title'])}"
        dest = out / stem  # extension will be inferred on download
        try:
            final_path = download(s["url"], dest)
            print(f"Saved: {final_path}")
        except Exception as e:
            print(f"FAILED {s['date']}: {e}")

if __name__ == "__main__":
    main()
