#!/usr/bin/env python3
"""
Love Letters — Display random historic love letters from Project Gutenberg.

Reads pre-downloaded letter collections from the letters/ directory.
Run download_letters.py first to populate the data.
"""

import json
import os
import random
import re
import sys
import textwrap

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
LETTERS_DIR = os.path.join(SCRIPT_DIR, "letters")


def load_all_letters(source_filter: str | None = None) -> list[dict]:
    """Load all letters from JSON files in the letters/ directory."""
    if not os.path.isdir(LETTERS_DIR):
        return []
    all_letters: list[dict] = []
    for filename in sorted(os.listdir(LETTERS_DIR)):
        if not filename.endswith(".json"):
            continue
        source_id = filename[:-5]
        if source_filter and source_id != source_filter:
            continue
        path = os.path.join(LETTERS_DIR, filename)
        with open(path, "r", encoding="utf-8") as f:
            all_letters.extend(json.load(f))
    return all_letters


def available_sources() -> list[str]:
    """Return list of source IDs from the letters/ directory."""
    if not os.path.isdir(LETTERS_DIR):
        return []
    return sorted(
        f[:-5] for f in os.listdir(LETTERS_DIR) if f.endswith(".json")
    )


def wrap_text(text: str, width: int = 78) -> str:
    """Word-wrap text while preserving paragraph breaks."""
    paragraphs = re.split(r"\n\s*\n", text)
    wrapped = []
    for para in paragraphs:
        para = " ".join(para.split())
        wrapped.append(textwrap.fill(para, width=width))
    return "\n\n".join(wrapped)


def display_letter(letter: dict) -> None:
    """Pretty-print a single love letter to the terminal."""
    print()
    print(SEPARATOR)
    print(f"  ✉  {letter['author']}  →  {letter['recipient']}")
    if letter.get("heading"):
        print(f"     {letter['heading']}")
    print(f"     ({letter['period']})")
    print(SEPARATOR)
    print()

    print(wrap_text(letter["body"]))

    print()
    print(SEPARATOR)
    print(f"  Source: {letter['source']}")
    print(f"  Via Project Gutenberg  •  gutenberg.org")
    print(SEPARATOR)
    print()


SEPARATOR = "─" * 60


def list_sources() -> None:
    """Print available letter collections with counts."""
    sources = available_sources()
    if not sources:
        print("\n  No letter collections found. Run download_letters.py first.\n")
        return
    print("\n  Available collections:\n")
    for i, source_id in enumerate(sources, 1):
        path = os.path.join(LETTERS_DIR, f"{source_id}.json")
        with open(path, "r", encoding="utf-8") as f:
            letters = json.load(f)
        if not letters:
            continue
        first = letters[0]
        print(f"  {i:2}. [{source_id}]")
        print(f"      {first['author']} → {first['recipient']} ({first['period']})")
        print(f"      {first['source']}  ({len(letters)} letters)")
        print()


def main() -> None:
    import argparse

    sources = available_sources()

    parser = argparse.ArgumentParser(
        description="Display random historic love letters from Project Gutenberg.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=textwrap.dedent("""\
            examples:
              %(prog)s                          Show a random love letter
              %(prog)s -n 3                     Show 3 random love letters
              %(prog)s --list                   List available collections
              %(prog)s --source keats_brawne    Show only Keats letters
        """),
    )
    parser.add_argument(
        "-n", "--count", type=int, default=1, metavar="N",
        help="number of letters to display (default: 1)",
    )
    parser.add_argument(
        "--list", action="store_true",
        help="list available letter collections",
    )
    parser.add_argument(
        "--source", type=str, metavar="ID",
        choices=sources if sources else None,
        help="only show letters from a specific source",
    )

    args = parser.parse_args()

    if args.list:
        list_sources()
        return

    all_letters = load_all_letters(source_filter=args.source)

    if not all_letters:
        if not sources:
            print("  No letter data found. Run download_letters.py first.")
        else:
            print(f"  No letters found for source '{args.source}'.")
        sys.exit(1)

    count = min(args.count, len(all_letters))
    chosen = random.sample(all_letters, count)

    for letter in chosen:
        display_letter(letter)


if __name__ == "__main__":
    main()
