"""Media management commands for Phoxtail."""

import re
import subprocess
from pathlib import Path

import typer
from rich.console import Console
from rich.progress import BarColumn, Progress, TaskProgressColumn, TextColumn

app = typer.Typer()
console = Console()

# Matches rsync --info=progress2 output like:
#   1,234,567  45%  12.34MB/s    0:01:23
_PROGRESS_RE = re.compile(r"(\d+)%")


def _parse_rsync_stats(output: str) -> dict:
    """Parse rsync --stats output into a summary dict."""
    stats = {}

    match = re.search(r"Number of regular files transferred:\s*([\d,]+)", output)
    if match:
        stats["files"] = int(match.group(1).replace(",", ""))

    match = re.search(r"Total transferred file size:\s*([\d,]+)", output)
    if match:
        size_bytes = int(match.group(1).replace(",", ""))
        if size_bytes >= 1_073_741_824:
            stats["size"] = f"{size_bytes / 1_073_741_824:.1f} GB"
        elif size_bytes >= 1_048_576:
            stats["size"] = f"{size_bytes / 1_048_576:.1f} MB"
        elif size_bytes >= 1024:
            stats["size"] = f"{size_bytes / 1024:.1f} KB"
        else:
            stats["size"] = f"{size_bytes} bytes"

    return stats


def _run_rsync_with_progress(rsync_cmd: list[str]) -> str:
    """Run rsync and display a Rich progress bar from --info=progress2 output.

    Returns the combined stdout/stderr output for stats parsing.
    """
    proc = subprocess.Popen(
        rsync_cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True,
    )

    output_lines = []

    with Progress(
        TextColumn("[bold cyan]Syncing media"),
        BarColumn(),
        TaskProgressColumn(),
        transient=True,
    ) as progress:
        task = progress.add_task("sync", total=100)

        for line in proc.stdout:
            output_lines.append(line)
            match = _PROGRESS_RE.search(line)
            if match:
                progress.update(task, completed=int(match.group(1)))

    proc.wait()
    if proc.returncode != 0:
        raise subprocess.CalledProcessError(proc.returncode, rsync_cmd)

    return "".join(output_lines)


@app.command(name="pull")
def pull(
    remote_host: str = typer.Argument(
        ...,
        help="SSH connection string (e.g., user@example.com)",
    ),
    remote_dir: str = typer.Argument(
        ...,
        help="Path to the project on the remote server",
    ),
    dry_run: bool = typer.Option(
        False,
        "--dry-run",
        "-n",
        help="Show what would be synced without transferring",
    ),
    verbose: bool = typer.Option(
        False,
        "--verbose",
        "-v",
        help="Show raw rsync output",
    ),
) -> None:
    """Sync media files from the remote server to the local environment."""
    local_media_dir = Path.cwd() / "media"
    local_media_dir.mkdir(parents=True, exist_ok=True)

    remote_path = f"{remote_host}:{remote_dir}/media/"

    rsync_cmd = [
        "rsync",
        "-az",
        "--info=progress2",
        "--stats",
        remote_path,
        str(local_media_dir),
    ]

    if dry_run:
        rsync_cmd[2:2] = ["--dry-run", "--verbose"]
        rsync_cmd.remove("--info=progress2")
        console.print(
            f"[bold]Dry run:[/bold] checking what would sync from "
            f"[cyan]{remote_path}[/cyan]\n"
        )

    if verbose and not dry_run:
        rsync_cmd[2:2] = ["--verbose", "--progress"]
        rsync_cmd.remove("--info=progress2")

    try:
        if verbose or dry_run:
            subprocess.run(rsync_cmd, check=True)
        else:
            output = _run_rsync_with_progress(rsync_cmd)

            stats = _parse_rsync_stats(output)
            files = stats.get("files", 0)
            size = stats.get("size", "0 bytes")

            if files > 0:
                console.print(
                    f"[green bold]✓ Synced {files} file{'s' if files != 1 else ''} "
                    f"({size})[/green bold]"
                )
            else:
                console.print("[green bold]✓ Already up to date[/green bold]")

        if dry_run:
            console.print(
                "\n[dim]No files were transferred. Remove --dry-run to sync.[/dim]"
            )

    except subprocess.CalledProcessError:
        console.print("[red]Error syncing media[/red]")
        raise typer.Exit(1)
