"""Fetch commands for downloading papers from various sources."""

from pathlib import Path
from typing import Optional

import typer
from rich.console import Console

from papercut.cli.utils import handle_errors, is_quiet

app = typer.Typer(
    help="Download papers from various sources.",
    no_args_is_help=True,
)


def _get_console() -> Console:
    """Get a console for output messages (respects quiet mode for status only)."""
    return Console()


@app.command()
@handle_errors
def arxiv(
    paper_id: str = typer.Argument(..., help="arXiv paper ID (e.g., 2301.00001)"),
    output: Path = typer.Option(
        Path("."),
        "--output",
        "-o",
        help="Output directory for downloaded PDF.",
    ),
    metadata: bool = typer.Option(
        False,
        "--metadata",
        "-m",
        help="Save metadata as JSON sidecar file (.meta.json).",
    ),
):
    """Download paper from arXiv by ID."""
    from papercut.fetchers.arxiv import ArxivFetcher

    console = _get_console()
    fetcher = ArxivFetcher()

    if is_quiet():
        doc = fetcher.fetch(paper_id, output)
    else:
        with console.status(f"Fetching arXiv:{paper_id}..."):
            doc = fetcher.fetch(paper_id, output)
        console.print(f"[green]Downloaded:[/green] {doc.path}")
        if doc.title:
            console.print(f"[dim]Title: {doc.title}[/dim]")

    if metadata:
        meta_path = doc.save_metadata()
        if not is_quiet():
            console.print(f"[dim]Metadata: {meta_path}[/dim]")


@app.command()
@handle_errors
def doi(
    identifier: str = typer.Argument(..., help="DOI identifier (e.g., 10.1257/aer.20180779)"),
    output: Path = typer.Option(
        Path("."),
        "--output",
        "-o",
        help="Output directory for downloaded PDF.",
    ),
    metadata: bool = typer.Option(
        False,
        "--metadata",
        "-m",
        help="Save metadata as JSON sidecar file (.meta.json).",
    ),
):
    """Resolve DOI and download paper."""
    from papercut.fetchers.doi import DOIFetcher

    console = _get_console()
    fetcher = DOIFetcher()

    if is_quiet():
        doc = fetcher.fetch(identifier, output)
    else:
        with console.status(f"Resolving DOI:{identifier}..."):
            doc = fetcher.fetch(identifier, output)
        console.print(f"[green]Downloaded:[/green] {doc.path}")
        if doc.title:
            console.print(f"[dim]Title: {doc.title}[/dim]")

    if metadata:
        meta_path = doc.save_metadata()
        if not is_quiet():
            console.print(f"[dim]Metadata: {meta_path}[/dim]")


@app.command()
@handle_errors
def ssrn(
    paper_id: str = typer.Argument(..., help="SSRN paper ID"),
    output: Path = typer.Option(
        Path("."),
        "--output",
        "-o",
        help="Output directory for downloaded PDF.",
    ),
    metadata: bool = typer.Option(
        False,
        "--metadata",
        "-m",
        help="Save metadata as JSON sidecar file (.meta.json).",
    ),
):
    """Download paper from SSRN."""
    from papercut.fetchers.ssrn import SSRNFetcher

    console = _get_console()
    fetcher = SSRNFetcher()

    if is_quiet():
        doc = fetcher.fetch(paper_id, output)
    else:
        with console.status(f"Fetching SSRN:{paper_id}..."):
            doc = fetcher.fetch(paper_id, output)
        console.print(f"[green]Downloaded:[/green] {doc.path}")

    if metadata:
        meta_path = doc.save_metadata()
        if not is_quiet():
            console.print(f"[dim]Metadata: {meta_path}[/dim]")


@app.command()
@handle_errors
def nber(
    paper_id: str = typer.Argument(..., help="NBER working paper ID (e.g., w29000)"),
    output: Path = typer.Option(
        Path("."),
        "--output",
        "-o",
        help="Output directory for downloaded PDF.",
    ),
    metadata: bool = typer.Option(
        False,
        "--metadata",
        "-m",
        help="Save metadata as JSON sidecar file (.meta.json).",
    ),
):
    """Download paper from NBER."""
    from papercut.fetchers.nber import NBERFetcher

    console = _get_console()
    fetcher = NBERFetcher()

    if is_quiet():
        doc = fetcher.fetch(paper_id, output)
    else:
        with console.status(f"Fetching NBER:{paper_id}..."):
            doc = fetcher.fetch(paper_id, output)
        console.print(f"[green]Downloaded:[/green] {doc.path}")

    if metadata:
        meta_path = doc.save_metadata()
        if not is_quiet():
            console.print(f"[dim]Metadata: {meta_path}[/dim]")


@app.command()
@handle_errors
def url(
    paper_url: str = typer.Argument(..., help="Direct URL to PDF"),
    output: Path = typer.Option(
        Path("."),
        "--output",
        "-o",
        help="Output directory for downloaded PDF.",
    ),
    name: Optional[str] = typer.Option(
        None,
        "--name",
        "-n",
        help="Custom filename (without extension).",
    ),
    metadata: bool = typer.Option(
        False,
        "--metadata",
        "-m",
        help="Save metadata as JSON sidecar file (.meta.json).",
    ),
):
    """Download paper from direct URL."""
    from papercut.fetchers.url import URLFetcher

    console = _get_console()
    fetcher = URLFetcher()

    if is_quiet():
        doc = fetcher.fetch(paper_url, output, name=name)
    else:
        with console.status(f"Downloading from URL..."):
            doc = fetcher.fetch(paper_url, output, name=name)
        console.print(f"[green]Downloaded:[/green] {doc.path}")

    if metadata:
        meta_path = doc.save_metadata()
        if not is_quiet():
            console.print(f"[dim]Metadata: {meta_path}[/dim]")


@app.command()
@handle_errors
def batch(
    file_path: Path = typer.Argument(..., help="File with paper identifiers (one per line)"),
    output: Path = typer.Option(
        Path("."),
        "--output",
        "-o",
        help="Output directory for downloaded PDFs.",
    ),
    continue_on_error: bool = typer.Option(
        True,
        "--continue-on-error/--stop-on-error",
        help="Continue after download failures.",
    ),
    delay: float = typer.Option(
        1.0,
        "--delay",
        "-d",
        help="Delay between downloads in seconds.",
    ),
    dry_run: bool = typer.Option(
        False,
        "--dry-run",
        help="Show what would be downloaded without fetching.",
    ),
    metadata: bool = typer.Option(
        False,
        "--metadata",
        "-m",
        help="Save metadata as JSON sidecar files (.meta.json).",
    ),
):
    """Download multiple papers from a list file.

    The file should contain one identifier per line. Supported formats:

    \b
        arxiv:1706.03762
        doi:10.1257/aer.20180779
        ssrn:1234567
        nber:w29000
        https://example.com/paper.pdf

    Lines starting with # are treated as comments.

    Examples:

    \b
        papercut fetch batch papers.txt -o ./library/
        papercut fetch batch papers.txt --dry-run
        papercut fetch batch papers.txt --delay 2.0 --stop-on-error
        papercut fetch batch papers.txt --metadata
    """
    import time

    from papercut.api import fetch_paper
    from papercut.output import get_formatter

    console = _get_console()
    formatter = get_formatter(quiet=is_quiet())
    quiet_mode = is_quiet()

    # Read and parse the file
    if not file_path.exists():
        console.print(f"[red]Error:[/red] File not found: {file_path}")
        raise typer.Exit(1)

    identifiers = []
    with open(file_path) as f:
        for line_no, line in enumerate(f, 1):
            line = line.strip()
            # Skip empty lines and comments
            if not line or line.startswith("#"):
                continue
            identifiers.append((line_no, line))

    if not identifiers:
        console.print("[yellow]No paper identifiers found in file[/yellow]")
        raise typer.Exit(0)

    if not quiet_mode:
        console.print(f"[bold]Found {len(identifiers)} paper(s) to fetch[/bold]")

    # Dry run mode
    if dry_run:
        result = {
            "dry_run": True,
            "file": str(file_path),
            "count": len(identifiers),
            "identifiers": [{"line": ln, "id": ident} for ln, ident in identifiers],
        }
        if formatter.use_json:
            formatter.output(result)
        else:
            console.print("\n[dim]Would download:[/dim]")
            for ln, ident in identifiers:
                console.print(f"  Line {ln}: {ident}")
        return

    # Download papers
    output.mkdir(parents=True, exist_ok=True)
    downloaded = []
    failed = []

    for i, (line_no, identifier) in enumerate(identifiers):
        if not quiet_mode:
            console.print(f"\n[dim][{i + 1}/{len(identifiers)}] Fetching: {identifier}[/dim]")

        try:
            doc = fetch_paper(identifier, output)
            downloaded.append({"line": line_no, "id": identifier, "path": str(doc.path.name)})
            if not quiet_mode:
                console.print(f"  [green]Downloaded:[/green] {doc.path.name}")
            if metadata:
                doc.save_metadata()
        except Exception as e:
            failed.append({"line": line_no, "id": identifier, "error": str(e)[:100]})
            if not quiet_mode:
                console.print(f"  [red]Failed:[/red] {e}")
            if not continue_on_error:
                break

        # Rate limiting between downloads
        if i < len(identifiers) - 1 and delay > 0:
            time.sleep(delay)

    # Summary
    if not quiet_mode:
        console.print(f"\n[bold]Done:[/bold]")
        console.print(f"  [green]{len(downloaded)} downloaded[/green]")
        if failed:
            console.print(f"  [red]{len(failed)} failed[/red]")

    # JSON output
    result = {
        "success": len(failed) == 0 or continue_on_error,
        "file": str(file_path),
        "output_dir": str(output),
        "downloaded": len(downloaded),
        "failed": len(failed),
        "downloaded_files": downloaded,
    }
    if failed:
        result["failed_items"] = failed

    if formatter.use_json:
        formatter.output(result)
