"""Command-line interface for Legionnaire."""

import argparse
import contextlib
import shutil
import sys
from pathlib import Path

from legionnaire import __version__
from legionnaire.agent import (
    AgentStatus,
    AgentType,
    get_agent_status,
    kill_agent_if_running,
    launch_command,
)
from legionnaire.git_ops import (
    GitError,
    branch_delete,
    branch_exists,
    current_branch,
    current_head,
    current_upstream,
    ensure_worktree_add,
    is_worktree_dirty,
    list_branches_with_prefix,
    rev_parse,
    set_branch_upstream,
    worktree_list_porcelain,
    worktree_remove,
)
from legionnaire.ids import InvalidIdError, slugify_and_validate
from legionnaire.paths import (
    NotInGitRepoError,
    child_branch_name,
    child_worktree_path,
    get_repo_root,
    get_worktrees_root,
    parent_branch_name,
    parent_dir,
    parent_worktree_path,
)
from legionnaire.tmux_ops import (
    SessionNotManagedError,
    apply_colors,
    ensure_child_window,
    ensure_managed_session,
    ensure_parent_window,
    get_session,
    is_session_managed,
    kill_session,
    kill_window,
    reorder_windows,
    send_keys_to_window,
)


def cmd_init(args: argparse.Namespace) -> int:
    """Handle the 'init' command."""
    verbose = args.verbose
    dry_run = args.dry_run
    no_tmux = args.no_tmux
    no_agent = args.no_agent
    agent_type = AgentType(args.agent) if not no_agent else None

    # Validate parent ID
    try:
        parent_id = slugify_and_validate(args.parent)
    except InvalidIdError as e:
        print(f"Error: Invalid parent ID: {e}", file=sys.stderr)
        return 1

    # Validate child IDs
    child_ids = []
    for child in args.children:
        try:
            child_ids.append(slugify_and_validate(child))
        except InvalidIdError as e:
            print(f"Error: Invalid child ID '{child}': {e}", file=sys.stderr)
            return 1

    # Get repo and worktrees root
    try:
        repo_root = get_repo_root(Path(args.repo) if args.repo else None)
    except NotInGitRepoError as e:
        print(f"Error: {e}", file=sys.stderr)
        return 1

    worktrees_root = get_worktrees_root(repo_root)

    if verbose:
        print(f"Repository root: {repo_root}")
        print(f"Worktrees root: {worktrees_root}")

    # Check tmux availability
    if not no_tmux and shutil.which("tmux") is None:
        print("Error: tmux not found. Install tmux or use --no-tmux.", file=sys.stderr)
        return 1

    # Get base ref info
    base_ref = current_head(repo_root)
    base_branch = current_branch(repo_root)
    base_upstream = current_upstream(repo_root)

    if verbose:
        print(f"Base ref: {base_ref[:8]}")
        if base_branch:
            print(f"Base branch: {base_branch}")
        if base_upstream:
            print(f"Base upstream: {base_upstream}")

    # Ensure worktrees root exists
    if not dry_run:
        worktrees_root.mkdir(parents=True, exist_ok=True)

    # Ensure parent worktree + branch
    parent_wt_path = parent_worktree_path(worktrees_root, parent_id)
    parent_branch = parent_branch_name(parent_id)

    if verbose:
        print(f"Parent worktree: {parent_wt_path}")
        print(f"Parent branch: {parent_branch}")

    if not dry_run:
        # Ensure parent directory exists
        parent_dir(worktrees_root, parent_id).mkdir(parents=True, exist_ok=True)

        created = ensure_worktree_add(repo_root, parent_wt_path, parent_branch, base_ref)
        if created and verbose:
            print(f"Created parent worktree at {parent_wt_path}")

        # Set upstream if available
        if base_upstream and created:
            try:
                set_branch_upstream(repo_root, parent_branch, base_upstream)
                if verbose:
                    print(f"Set upstream for {parent_branch} to {base_upstream}")
            except GitError:
                pass  # Skip if upstream setting fails

    # Discover existing children
    existing_child_worktrees = set()
    existing_child_branches = set()
    existing_child_windows = set()

    # From worktrees
    worktrees = worktree_list_porcelain(repo_root)
    parent_dir_path = parent_dir(worktrees_root, parent_id)
    for wt in worktrees:
        if wt.path.parent == parent_dir_path and wt.path.name != "parent":
            existing_child_worktrees.add(wt.path.name)

    # From branches
    branch_prefix = f"{parent_id}/"
    for branch in list_branches_with_prefix(repo_root, branch_prefix):
        suffix = branch[len(branch_prefix) :]
        if suffix != "parent" and "/" not in suffix:
            existing_child_branches.add(suffix)

    # From tmux windows (if tmux enabled)
    if not no_tmux:
        session = get_session(parent_id)
        if session and is_session_managed(session):
            existing_child_windows = {w.name for w in session.windows if w.name != "parent"}

    # Union of all known children
    all_child_ids = sorted(
        set(child_ids) | existing_child_worktrees | existing_child_branches | existing_child_windows
    )

    if verbose:
        print(f"Known children: {all_child_ids}")

    # Ensure tmux session
    session = None
    if not no_tmux and not dry_run:
        try:
            session = ensure_managed_session(parent_id, parent_wt_path)
            ensure_parent_window(session, parent_wt_path)
            if verbose:
                print(f"Ensured tmux session: {parent_id}")
        except SessionNotManagedError as e:
            print(f"Error: {e}", file=sys.stderr)
            return 1

    # Ensure each child
    parent_head = None
    if not dry_run and all_child_ids:
        parent_head = rev_parse(repo_root, parent_branch)

    for child_id in all_child_ids:
        child_wt_path = child_worktree_path(worktrees_root, parent_id, child_id)
        child_branch = child_branch_name(parent_id, child_id)

        if verbose:
            print(f"Processing child: {child_id}")

        if not dry_run:
            # Ensure worktree + branch
            created = ensure_worktree_add(repo_root, child_wt_path, child_branch, parent_head)
            if created and verbose:
                print(f"  Created worktree at {child_wt_path}")

            # Ensure tmux window
            if session:
                window = ensure_child_window(session, child_id, child_wt_path)

                # Ensure agent running
                if agent_type:
                    status, pid = get_agent_status(child_wt_path)
                    if status != AgentStatus.RUNNING:
                        cmd = launch_command(agent_type, parent_id, child_id, child_wt_path)
                        send_keys_to_window(window, cmd)
                        if verbose:
                            print(f"  Launched {agent_type.value} agent")

    # Reorder windows and apply colors
    if session and not dry_run:
        reorder_windows(session, all_child_ids)
        apply_colors(session, all_child_ids)

    print(f"Initialized parent '{parent_id}' with {len(all_child_ids)} children")
    return 0


def cmd_remove(args: argparse.Namespace) -> int:
    """Handle the 'remove' command."""
    verbose = args.verbose
    dry_run = args.dry_run
    force = args.force

    # Validate parent ID
    try:
        parent_id = slugify_and_validate(args.parent)
    except InvalidIdError as e:
        print(f"Error: Invalid parent ID: {e}", file=sys.stderr)
        return 1

    # Validate child IDs
    child_ids_to_remove = []
    for child in args.children:
        try:
            child_ids_to_remove.append(slugify_and_validate(child))
        except InvalidIdError as e:
            print(f"Error: Invalid child ID '{child}': {e}", file=sys.stderr)
            return 1

    # Get repo root
    try:
        repo_root = get_repo_root(Path(args.repo) if args.repo else None)
    except NotInGitRepoError as e:
        print(f"Error: {e}", file=sys.stderr)
        return 1

    worktrees_root = get_worktrees_root(repo_root)

    # If no children specified, remove all children then parent
    remove_parent = len(child_ids_to_remove) == 0

    if remove_parent:
        # Discover all children
        parent_dir_path = parent_dir(worktrees_root, parent_id)
        worktrees = worktree_list_porcelain(repo_root)
        for wt in worktrees:
            if wt.path.parent == parent_dir_path and wt.path.name != "parent":
                child_ids_to_remove.append(wt.path.name)

    # Get session if exists
    session = get_session(parent_id)
    managed = session and is_session_managed(session)

    # Remove each child
    for child_id in child_ids_to_remove:
        child_wt_path = child_worktree_path(worktrees_root, parent_id, child_id)
        child_branch = child_branch_name(parent_id, child_id)

        if verbose:
            print(f"Removing child: {child_id}")

        if not dry_run:
            # Kill tmux window
            if session and managed:
                kill_window(session, child_id)

            # Kill agent
            kill_agent_if_running(child_wt_path)

            # Check if dirty
            if child_wt_path.exists() and not force and is_worktree_dirty(child_wt_path):
                print(
                    f"Error: Worktree {child_wt_path} has uncommitted changes. "
                    f"Use --force to remove anyway.",
                    file=sys.stderr,
                )
                return 1

            # Remove worktree
            if child_wt_path.exists():
                try:
                    worktree_remove(repo_root, child_wt_path, force=force)
                    if verbose:
                        print(f"  Removed worktree {child_wt_path}")
                except GitError as e:
                    if not force:
                        print(f"Error removing worktree: {e}", file=sys.stderr)
                        return 1

            # Delete branch
            if branch_exists(repo_root, child_branch):
                try:
                    branch_delete(repo_root, child_branch, force=force)
                    if verbose:
                        print(f"  Deleted branch {child_branch}")
                except GitError as e:
                    if not force:
                        print(f"Error deleting branch: {e}", file=sys.stderr)
                        return 1

            # Clean up directory if it still exists
            if child_wt_path.exists():
                if force:
                    shutil.rmtree(child_wt_path)
                else:
                    child_wt_path.rmdir()

    # Remove parent if requested
    if remove_parent:
        parent_wt_path = parent_worktree_path(worktrees_root, parent_id)
        parent_branch = parent_branch_name(parent_id)

        if verbose:
            print(f"Removing parent: {parent_id}")

        if not dry_run:
            # Kill session
            if session and managed:
                kill_session(session)
                if verbose:
                    print(f"  Killed tmux session {parent_id}")

            # Check if dirty
            if parent_wt_path.exists() and not force and is_worktree_dirty(parent_wt_path):
                print(
                    f"Error: Worktree {parent_wt_path} has uncommitted changes. "
                    f"Use --force to remove anyway.",
                    file=sys.stderr,
                )
                return 1

            # Remove worktree
            if parent_wt_path.exists():
                try:
                    worktree_remove(repo_root, parent_wt_path, force=force)
                    if verbose:
                        print(f"  Removed worktree {parent_wt_path}")
                except GitError as e:
                    if not force:
                        print(f"Error removing worktree: {e}", file=sys.stderr)
                        return 1

            # Delete branch
            if branch_exists(repo_root, parent_branch):
                try:
                    branch_delete(repo_root, parent_branch, force=force)
                    if verbose:
                        print(f"  Deleted branch {parent_branch}")
                except GitError as e:
                    if not force:
                        print(f"Error deleting branch: {e}", file=sys.stderr)
                        return 1

            # Clean up parent directory
            parent_dir_path = parent_dir(worktrees_root, parent_id)
            if parent_dir_path.exists():
                if force:
                    shutil.rmtree(parent_dir_path)
                else:
                    with contextlib.suppress(OSError):
                        parent_dir_path.rmdir()

        print(f"Removed parent '{parent_id}' and all children")
    else:
        print(f"Removed {len(child_ids_to_remove)} children from '{parent_id}'")

    return 0


def cmd_list(args: argparse.Namespace) -> int:
    """Handle the 'list' command."""
    verbose = args.verbose

    # Get repo root
    try:
        repo_root = get_repo_root(Path(args.repo) if args.repo else None)
    except NotInGitRepoError as e:
        print(f"Error: {e}", file=sys.stderr)
        return 1

    worktrees_root = get_worktrees_root(repo_root)
    worktrees = worktree_list_porcelain(repo_root)

    # Find all parents
    parents = {}
    for wt in worktrees:
        if not str(wt.path).startswith(str(worktrees_root)):
            continue
        rel_path = wt.path.relative_to(worktrees_root)
        parts = rel_path.parts
        if parts and parts[-1] == "parent":
            parent_id = "/".join(parts[:-1])
            parents[parent_id] = wt

    if args.parent:
        # List children of a specific parent
        try:
            parent_id = slugify_and_validate(args.parent)
        except InvalidIdError as e:
            print(f"Error: Invalid parent ID: {e}", file=sys.stderr)
            return 1

        if parent_id not in parents:
            print(f"Error: Parent '{parent_id}' not found", file=sys.stderr)
            return 1

        parent_wt = parents[parent_id]
        session = get_session(parent_id)
        managed = session and is_session_managed(session)

        print(f"Parent: {parent_id}")
        print(f"  Branch: {parent_wt.branch}")
        print(f"  Worktree: {parent_wt.path}")
        print(f"  Session: {'managed' if managed else ('exists' if session else 'none')}")
        print()

        # Find children
        parent_dir_path = parent_dir(worktrees_root, parent_id)
        children = []
        for wt in worktrees:
            if wt.path.parent == parent_dir_path and wt.path.name != "parent":
                status, pid = get_agent_status(wt.path)
                window_exists = False
                if session:
                    window_exists = any(w.name == wt.path.name for w in session.windows)
                children.append(
                    {
                        "id": wt.path.name,
                        "branch": wt.branch,
                        "path": wt.path,
                        "window": window_exists,
                        "agent_status": status,
                        "agent_pid": pid,
                    }
                )

        children.sort(key=lambda c: c["id"])

        if not children:
            print("  (no children)")
        else:
            print("Children:")
            for child in children:
                agent_str = child["agent_status"].value
                if child["agent_pid"]:
                    agent_str += f" (pid={child['agent_pid']})"
                window_str = "yes" if child["window"] else "no"

                print(f"  {child['id']}")
                if verbose:
                    print(f"    Branch: {child['branch']}")
                    print(f"    Path: {child['path']}")
                    print(f"    Window: {window_str}")
                    print(f"    Agent: {agent_str}")
                else:
                    print(f"    {child['branch']} | window={window_str} | agent={agent_str}")
    else:
        # List all parents
        if not parents:
            print("No parents found")
            return 0

        for parent_id in sorted(parents.keys()):
            parent_wt = parents[parent_id]
            session = get_session(parent_id)
            managed = session and is_session_managed(session)

            # Count children
            parent_dir_path = parent_dir(worktrees_root, parent_id)
            child_count = sum(
                1
                for wt in worktrees
                if wt.path.parent == parent_dir_path and wt.path.name != "parent"
            )

            session_str = "managed" if managed else ("exists" if session else "none")
            print(f"{parent_id} ({child_count} children, session={session_str})")

    return 0


def create_parser() -> argparse.ArgumentParser:
    """Create the argument parser."""
    parser = argparse.ArgumentParser(
        prog="legion",
        description="Orchestrate git worktrees + tmux + LLM CLIs",
    )
    parser.add_argument(
        "--version",
        action="version",
        version=f"%(prog)s {__version__}",
    )

    subparsers = parser.add_subparsers(dest="command", help="Commands")

    # init command
    init_parser = subparsers.add_parser(
        "init",
        help="Initialize a parent with optional children",
    )
    init_parser.add_argument("parent", help="Parent name/ID")
    init_parser.add_argument("children", nargs="*", help="Child names/IDs")
    init_parser.add_argument("--repo", help="Path to git repository (default: current directory)")
    init_parser.add_argument(
        "--agent",
        choices=["claude", "cursor"],
        default="claude",
        help="Agent to launch in child windows (default: claude)",
    )
    init_parser.add_argument(
        "--no-agent",
        action="store_true",
        help="Don't launch agents in child windows",
    )
    init_parser.add_argument(
        "--no-tmux",
        action="store_true",
        help="Skip tmux and agent setup (git only)",
    )
    init_parser.add_argument(
        "--dry-run", action="store_true", help="Print actions without executing"
    )
    init_parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")

    # remove command
    remove_parser = subparsers.add_parser(
        "remove",
        help="Remove parent and/or children",
    )
    remove_parser.add_argument("parent", help="Parent name/ID")
    remove_parser.add_argument(
        "children", nargs="*", help="Child names/IDs (if empty, removes all)"
    )
    remove_parser.add_argument("--repo", help="Path to git repository (default: cwd)")
    remove_parser.add_argument(
        "--force",
        action="store_true",
        help="Force removal of dirty worktrees and unmerged branches",
    )
    remove_parser.add_argument(
        "--dry-run", action="store_true", help="Print actions without executing"
    )
    remove_parser.add_argument("--yes", "-y", action="store_true", help="Skip confirmation prompts")
    remove_parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")

    # list command
    list_parser = subparsers.add_parser(
        "list",
        help="List parents and children",
    )
    list_parser.add_argument("parent", nargs="?", help="Parent to list children for")
    list_parser.add_argument("--repo", help="Path to git repository (default: current directory)")
    list_parser.add_argument("--json", action="store_true", help="Output as JSON")
    list_parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")

    return parser


def main() -> int:
    """Main entry point."""
    parser = create_parser()
    args = parser.parse_args()

    if args.command is None:
        parser.print_help()
        return 1

    if args.command == "init":
        return cmd_init(args)
    elif args.command == "remove":
        return cmd_remove(args)
    elif args.command == "list":
        return cmd_list(args)
    else:
        parser.print_help()
        return 1


if __name__ == "__main__":
    sys.exit(main())
