#!/usr/bin/env python3
"""
One-off backfill for a single channel's older episodes.

watcher.py only discovers new videos via YouTube's channel RSS feed, which
exposes just the ~15 most recent uploads. This script instead asks yt-dlp
to list a channel's entire "Videos" tab, so it can go further back than
the automatic watcher ever will. Not run via launchd - invoke by hand
whenever you want more of a channel's back catalog.

Usage:
    python3 backfill.py <channel_id>
    python3 backfill.py <channel_id> --since 2025-01-01
    python3 backfill.py <channel_id> --limit 10
    python3 backfill.py <channel_id> --dry-run
"""

import argparse
import subprocess
import sys
from datetime import datetime, timezone

import watcher


def list_channel_videos(channel_id):
    """Return every video ID on a channel's Videos tab, newest first."""
    url = f"https://www.youtube.com/channel/{channel_id}/videos"
    cmd = [watcher.YTDLP_BIN, "--flat-playlist", "--print", "%(id)s", url]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"Failed to list channel videos: {result.stderr[-500:]}")
        return []
    return [line.strip() for line in result.stdout.splitlines() if line.strip()]


def get_video_metadata(video_id):
    """Return {title, published} for a video without downloading it."""
    url = f"https://www.youtube.com/watch?v={video_id}"
    cmd = [watcher.YTDLP_BIN, "--skip-download", "--print", "%(title)s|%(upload_date)s", url]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0 or "|" not in result.stdout:
        return None
    title, upload_date = result.stdout.strip().rsplit("|", 1)
    if len(upload_date) != 8:
        return None
    published = f"{upload_date[0:4]}-{upload_date[4:6]}-{upload_date[6:8]}T00:00:00+00:00"
    return {"title": title, "published": published}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("channel_id")
    parser.add_argument("--since", help="Only backfill videos published on or after this date (YYYY-MM-DD)")
    parser.add_argument("--limit", type=int, help="Stop after downloading this many videos")
    parser.add_argument("--dry-run", action="store_true", help="List what would be downloaded, without downloading")
    args = parser.parse_args()

    since_dt = None
    if args.since:
        since_dt = datetime.fromisoformat(args.since).replace(tzinfo=timezone.utc)

    episodes = watcher.load_episodes()
    video_ids = list_channel_videos(args.channel_id)
    print(f"Found {len(video_ids)} videos on channel {args.channel_id}.")

    new_count = 0
    for video_id in video_ids:
        if args.limit and new_count >= args.limit:
            print(f"Reached --limit of {args.limit}, stopping.")
            break

        existing = episodes.get(video_id)
        if existing and existing.get("filename"):
            continue  # already downloaded

        meta = get_video_metadata(video_id)
        if not meta:
            print(f"  Skipping {video_id}: couldn't fetch metadata")
            continue

        if since_dt:
            pub_dt = datetime.fromisoformat(meta["published"])
            if pub_dt < since_dt:
                continue

        if args.dry_run:
            print(f"  Would download: {meta['published'][:10]} - {meta['title']}")
            new_count += 1
            continue

        video = {
            "video_id": video_id,
            "title": meta["title"],
            "published": meta["published"],
            "url": f"https://www.youtube.com/watch?v={video_id}",
        }
        print(f"Downloading: {video['title']}")
        filename = watcher.download_audio(video)
        episodes[video_id] = {
            "title": video["title"],
            "published": video["published"],
            "source_url": video["url"],
            "filename": filename,
            "channel_id": args.channel_id,
        }
        watcher.save_episodes(episodes)
        if filename:
            new_count += 1

    if not args.dry_run:
        watcher.build_feed(episodes)
    print(f"Done. {new_count} episode(s) {'would be downloaded' if args.dry_run else 'downloaded'}.")


if __name__ == "__main__":
    main()
