"""Typer CLI — thin layer that calls core and formats output with Rich."""

from __future__ import annotations

from pathlib import Path
from typing import Optional

import typer
from rich.panel import Panel
from rich.table import Table

import gwtm
from gwtm.utils import GwtError

app = typer.Typer(
    name="gwtm",
    help="Git Worktree Manager — manage worktrees across repositories.",
    rich_markup_mode="rich",
    no_args_is_help=True,
)

# Module-level state for verbose/quiet
_verbose = False
_quiet = False


def _handle_error(e: Exception) -> None:
    gwtm.err_console.print(f"[red]Error:[/red] {e}")
    raise typer.Exit(1)


def version_callback(value: bool) -> None:
    if value:
        print(f"gwtm {gwtm.__version__}")
        raise typer.Exit()


@app.callback()
def main(
    version: Optional[bool] = typer.Option(
        None, "--version", "-V", help="Show version and exit.", callback=version_callback, is_eager=True,
    ),
    verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed output."),
    quiet: bool = typer.Option(False, "--quiet", "-q", help="Suppress non-essential output."),
    no_color: bool = typer.Option(False, "--no-color", help="Disable colored output."),
) -> None:
    """Git Worktree Manager — manage worktrees across repositories."""
    global _verbose, _quiet

    _verbose = verbose
    _quiet = quiet

    if no_color:
        from rich.console import Console
        gwtm.err_console = Console(stderr=True, no_color=True, highlight=False)
        gwtm.out_console = Console(no_color=True, highlight=False)

    from gwtm.config import ensure_global_config
    ensure_global_config()


@app.command()
def new(
    name: str = typer.Argument(help="Name for the new worktree"),
    base: Optional[str] = typer.Option(None, "--base", "-b", help="Base branch to create from"),
    existing: Optional[str] = typer.Option(None, "--existing", "-e", help="Attach to an existing branch instead of creating a new one"),
    carry: Optional[list[str]] = typer.Option(None, "--carry", "-c", help="File patterns to carry to new worktree"),
    carry_changes: bool = typer.Option(False, "--carry-changes", help="Carry uncommitted changes to new worktree"),
    no_carry: bool = typer.Option(False, "--no-carry", help="Skip all file/change carrying"),
) -> None:
    """Create a new worktree."""
    from gwtm.core import create_worktree

    if existing and base:
        gwtm.err_console.print("[red]Error:[/red] --existing and --base cannot be used together.")
        raise typer.Exit(1)

    try:
        result = create_worktree(
            name,
            base_branch=base,
            existing_branch=existing,
            carry=carry,
            do_carry_changes=carry_changes or None,
            no_carry=no_carry,
        )
    except (GwtError, ValueError) as e:
        _handle_error(e)

    # Build carry summary line
    carry_parts: list[str] = []
    if result.carried_files:
        carry_parts.append(f"{len(result.carried_files)} item(s)")
    if result.carried_changes:
        carry_parts.append("changes applied")
    carry_line = f"\n  Carried: {', '.join(carry_parts)}" if carry_parts else ""

    # Verbose: list each carried file
    verbose_lines = ""
    if _verbose and result.carried_files:
        for cf in result.carried_files:
            kind = "dir" if cf.is_dir else "file"
            verbose_lines += f"\n    [dim]link ({kind}): {cf.relative_path}[/dim]"

    gwtm.out_console.print(Panel(
        f"[green]Created worktree[/green] [bold]{result.name}[/bold]\n"
        f"  Path:   {result.path}\n"
        f"  Branch: {result.branch}\n"
        f"  Base:   {result.base_branch}"
        f"{carry_line}{verbose_lines}",
        title="[green]New Worktree[/green]",
        border_style="green",
    ))

    if result.unignored_carried:
        paths = ", ".join(result.unignored_carried)
        gwtm.err_console.print(
            f"\n[yellow]Warning:[/yellow] Carried paths not in .gitignore: [bold]{paths}[/bold]\n"
            f"  Add them to .gitignore [dim](without trailing slash — symlinks need e.g. '.ai' not '.ai/')[/dim]"
        )


@app.command("ls")
def ls(
    all: bool = typer.Option(False, "--all", "-a", help="List worktrees across all repos"),
    names_only: bool = typer.Option(False, "--names-only", help="Print worktree names only (for shell completion)"),
) -> None:
    """List worktrees for the current repo (or all repos)."""
    from gwtm.core import get_worktree_statuses

    try:
        repo_statuses = get_worktree_statuses(all_repos=all)
    except GwtError as e:
        _handle_error(e)

    if names_only:
        for statuses in repo_statuses.values():
            for wt in statuses:
                if not wt.is_main:
                    print(wt.name)
        return

    for repo_name, statuses in repo_statuses.items():
        table = Table(title=f"{repo_name}", title_style="bold")
        table.add_column("Name", style="cyan")
        table.add_column("Branch", style="dim")
        table.add_column("Status")

        for wt in statuses:
            status_parts: list[str] = []
            if wt.ahead:
                status_parts.append(f"[green]{wt.ahead} ahead[/green]")
            if wt.behind:
                status_parts.append(f"[yellow]{wt.behind} behind[/yellow]")
            if wt.dirty:
                status_parts.append(f"[red]{wt.dirty} uncommitted[/red]")
            if wt.merged and not wt.is_main:
                status_parts.append("[dim]merged[/dim]")
            if not status_parts:
                status_parts.append("[green]clean[/green]")

            name_display = f"[bold]{wt.name}[/bold]" if wt.is_main else wt.name
            table.add_row(
                name_display,
                wt.branch or "[dim]detached[/dim]",
                ", ".join(status_parts),
            )

        gwtm.out_console.print(table)


@app.command()
def rm(
    name: str = typer.Argument(help="Worktree to remove"),
    force: bool = typer.Option(False, "--force", "-f", help="Remove even with uncommitted changes"),
    keep_branch: bool = typer.Option(False, "--keep-branch", help="Keep the branch after removing worktree"),
) -> None:
    """Remove a worktree and its branch."""
    from gwtm.core import remove_worktree

    try:
        result = remove_worktree(name, force=force, keep_branch=keep_branch)
    except GwtError as e:
        _handle_error(e)

    branch_msg = "branch deleted" if result.branch_deleted else "branch kept"
    gwtm.out_console.print(Panel(
        f"[red]Removed worktree[/red] [bold]{result.name}[/bold]\n"
        f"  Path: {result.path}\n"
        f"  {branch_msg}",
        title="[red]Removed[/red]",
        border_style="red",
    ))


@app.command()
def cd(
    name: str = typer.Argument(help="Worktree name (or 'main')"),
) -> None:
    """Resolve worktree path for shell cd integration."""
    from gwtm.core import resolve_worktree_path

    try:
        result = resolve_worktree_path(name)
    except GwtError as e:
        _handle_error(e)

    if result.found:
        # Raw path to stdout — shell wrapper captures this
        print(result.path)
    else:
        gwtm.err_console.print(f"[red]Error:[/red] Worktree '{name}' not found")
        if result.suggestions:
            gwtm.err_console.print("\n[dim]Did you mean?[/dim]")
            for s in result.suggestions:
                gwtm.err_console.print(f"  [cyan]{s}[/cyan]")
        raise typer.Exit(1)


@app.command()
def path(
    name: str = typer.Argument(help="Worktree name (or 'main')"),
) -> None:
    """Print the absolute path to a worktree."""
    from gwtm.core import resolve_worktree_path

    try:
        result = resolve_worktree_path(name)
    except GwtError as e:
        _handle_error(e)

    if result.found:
        print(result.path)
    else:
        gwtm.err_console.print(f"[red]Error:[/red] Worktree '{name}' not found")
        if result.suggestions:
            gwtm.err_console.print("\n[dim]Did you mean?[/dim]")
            for s in result.suggestions:
                gwtm.err_console.print(f"  [cyan]{s}[/cyan]")
        raise typer.Exit(1)


@app.command()
def clone(
    url: str = typer.Argument(help="Git repository URL to clone"),
    name: Optional[str] = typer.Option(None, "--name", "-n", help="Override repo name"),
    dest: Optional[str] = typer.Option(None, "--dest", "-d", help="Destination directory"),
) -> None:
    """Clone a repo and set up gwtm structure."""
    from gwtm.core import clone_repo

    try:
        with gwtm.err_console.status("[bold]Cloning repository..."):
            result = clone_repo(url, name=name, dest=Path(dest) if dest else None)
    except GwtError as e:
        _handle_error(e)

    gwtm.out_console.print(Panel(
        f"[green]Cloned[/green] [bold]{result.repo_name}[/bold]\n"
        f"  Path:           {result.clone_path}\n"
        f"  Default branch: {result.default_branch}\n"
        f"  Worktrees dir:  {result.worktrees_dir}\n\n"
        f"[dim]Next: cd {result.clone_path} && gwtm new <name>[/dim]",
        title="[green]Clone Complete[/green]",
        border_style="green",
    ))


@app.command()
def init(
    force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing config"),
) -> None:
    """Generate a .gwtconfig.yaml for the current repo."""
    from gwtm.core import init_repo

    try:
        result = init_repo(force=force)
    except GwtError as e:
        _handle_error(e)

    gwtm.out_console.print(Panel(
        f"[green]Initialized gwtm config[/green] for [bold]{result.repo_name}[/bold]\n"
        f"  Config: {result.config_path}\n"
        f"  Branch: {result.default_branch}\n\n"
        f"[dim]Edit the file to customize carry patterns, sync strategy, etc.[/dim]",
        title="[green]Init[/green]",
        border_style="green",
    ))


@app.command()
def sync(
    name: Optional[str] = typer.Argument(None, help="Worktree to sync (default: current)"),
    all: bool = typer.Option(False, "--all", "-a", help="Sync all worktrees"),
) -> None:
    """Sync worktree(s) with the base branch."""
    from gwtm.core import sync_worktree

    try:
        with gwtm.err_console.status("[bold]Syncing..."):
            results = sync_worktree(name=name, all_worktrees=all)
    except GwtError as e:
        _handle_error(e)

    for r in results:
        if r.success:
            gwtm.out_console.print(f"  [green]OK[/green]  {r.name} — {r.message}")
        else:
            gwtm.out_console.print(f"  [red]FAIL[/red] {r.name} — {r.message}")

    failed = [r for r in results if not r.success]
    if failed:
        raise typer.Exit(1)


@app.command()
def status() -> None:
    """Show detailed status of all worktrees for the current repo."""
    from rich.tree import Tree
    from gwtm.core import get_repo_status

    try:
        repo_name, main_path, statuses = get_repo_status()
    except GwtError as e:
        _handle_error(e)

    tree = Tree(f"[bold]{repo_name}[/bold]")

    for wt in statuses:
        status_parts: list[str] = []
        if wt.ahead:
            status_parts.append(f"[green]{wt.ahead} ahead[/green]")
        if wt.behind:
            status_parts.append(f"[yellow]{wt.behind} behind[/yellow]")
        if wt.dirty:
            status_parts.append(f"[red]{wt.dirty} uncommitted[/red]")
        if wt.merged and not wt.is_main:
            status_parts.append("[dim]merged[/dim]")
        if not status_parts:
            status_parts.append("[green]clean[/green]")

        status_str = ", ".join(status_parts)
        label = f"[bold]{wt.name}[/bold]" if wt.is_main else wt.name

        node = tree.add(f"{label}  [dim]{wt.branch or 'detached'}[/dim]")
        node.add(f"Path:   {wt.path}")
        node.add(f"Status: {status_str}")

    gwtm.out_console.print(tree)


@app.command()
def clean(
    dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be removed"),
) -> None:
    """Remove worktrees whose branches have been merged."""
    from gwtm.core import clean_worktrees

    try:
        candidates = clean_worktrees(dry_run=dry_run)
    except GwtError as e:
        _handle_error(e)

    if not candidates:
        gwtm.out_console.print("[dim]No merged worktrees to clean.[/dim]")
        return

    if dry_run:
        table = Table(title="Would remove (dry run)")
        table.add_column("Name", style="cyan")
        table.add_column("Branch", style="dim")
        table.add_column("Path")

        for c in candidates:
            table.add_row(c.name, c.branch or "", str(c.path))

        gwtm.out_console.print(table)
    else:
        for c in candidates:
            gwtm.out_console.print(f"  [red]Removed[/red] {c.name} ({c.branch})")


@app.command("init-shell")
def init_shell(
    shell: Optional[str] = typer.Option(None, "--shell", "-s", help="Shell type (zsh, bash, fish). Auto-detected if omitted."),
) -> None:
    """Print shell integration code for eval/source."""
    import importlib.resources
    import os

    if shell is None:
        shell_path = os.environ.get("SHELL", "")
        if "zsh" in shell_path:
            shell = "zsh"
        elif "fish" in shell_path:
            shell = "fish"
        elif "bash" in shell_path:
            shell = "bash"
        else:
            gwtm.err_console.print("[red]Error:[/red] Cannot detect shell. Use --shell <zsh|bash|fish>.")
            raise typer.Exit(1)

    filename = f"gwtm.{shell}"
    try:
        script = importlib.resources.files("gwtm.shell").joinpath(filename).read_text()
    except FileNotFoundError:
        gwtm.err_console.print(f"[red]Error:[/red] No shell integration for '{shell}'. Supported: zsh, bash, fish.")
        raise typer.Exit(1)

    print(script)
