"""Command-line interface for Sympath."""

from __future__ import annotations

import argparse
import sys
import textwrap

from sympath import __version__
from sympath.config import load_config, save_config
from sympath.manager import SymlinkManager

# ── Formatter that preserves newlines in descriptions ──────────────────────

class RawDefaultsHelpFormatter(
    argparse.RawDescriptionHelpFormatter,
    argparse.ArgumentDefaultsHelpFormatter,
):
    """Combines raw description formatting with default-value display."""


# ── Parser construction ───────────────────────────────────────────────────

def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="sympath",
        formatter_class=RawDefaultsHelpFormatter,
        description=textwrap.dedent("""\
            Sympath — a powerful symlink-based path manager

            Manage executable symlinks in a centralized directory so you only
            need a single PATH entry.  Register link folders, create and remove
            symlinks, bulk-link entire directories, and diagnose broken links.

            Quick start:
              sympath add-folder tools C:\\tools\\bin   # register a folder
              sympath link "C:\\Neovim\\bin\\nvim.exe"    # create a symlink
              sympath list                              # see all links
              sympath repair                            # find broken links

            Run 'sympath <command> --help' for details on any command.
        """),
        epilog=textwrap.dedent("""\
            examples:
              sympath add-folder tools C:\\tools\\bin
              sympath link "C:\\MinGW\\bin\\gcc.exe" --name gcc.exe
              sympath link-folder "C:\\LLVM\\bin" --force
              sympath list --folder dev
              sympath repair

            configuration:
              Sympath stores its state in ~/.sympath.json.  The file is created
              automatically when you register your first folder.

            more info:
              https://github.com/your-user/sympath
        """),
    )
    parser.add_argument(
        "-v", "--version",
        action="version",
        version=f"%(prog)s {__version__}",
    )

    sub = parser.add_subparsers(
        dest="command",
        title="commands",
        metavar="<command>",
    )

    # --- add-folder --------------------------------------------------------
    p_add = sub.add_parser(
        "add-folder",
        formatter_class=RawDefaultsHelpFormatter,
        help="Register a directory to hold symlinks",
        description=textwrap.dedent("""\
            Register a new link directory.

            The directory does not need to exist yet — it will be created the
            first time a symlink is added.  The first folder you register
            automatically becomes the default.

            examples:
              sympath add-folder tools C:\\tools\\bin
              sympath add-folder dev   C:\\dev\\bin
        """),
    )
    p_add.add_argument(
        "name",
        help="friendly name for the folder (used with --folder elsewhere)",
    )
    p_add.add_argument(
        "path",
        help="absolute path to the directory that will hold symlinks",
    )

    # --- set-default -------------------------------------------------------
    p_def = sub.add_parser(
        "set-default",
        formatter_class=RawDefaultsHelpFormatter,
        help="Set the default link directory",
        description=textwrap.dedent("""\
            Change which registered folder is used when --folder is omitted.

            example:
              sympath set-default dev
        """),
    )
    p_def.add_argument(
        "name",
        help="name of a previously registered folder",
    )

    # --- link --------------------------------------------------------------
    p_link = sub.add_parser(
        "link",
        formatter_class=RawDefaultsHelpFormatter,
        help="Create a symlink to an executable",
        description=textwrap.dedent("""\
            Create a symlink inside the default (or specified) link directory
            pointing to the given target executable.

            By default the symlink uses the same filename as the target.  Use
            --name to choose a different name, and --force to overwrite an
            existing link.

            examples:
              sympath link "C:\\Neovim\\bin\\nvim.exe"
              sympath link "C:\\Neovim\\bin\\nvim.exe" --name vim.exe
              sympath link "C:\\Python313\\python.exe" --folder dev --force
        """),
    )
    p_link.add_argument(
        "target",
        help="path to the target executable",
    )
    p_link.add_argument(
        "--folder",
        metavar="NAME",
        help="place the link in this registered folder instead of the default",
    )
    p_link.add_argument(
        "--name",
        metavar="ALIAS",
        help="custom filename for the symlink (e.g. vim.exe)",
    )
    p_link.add_argument(
        "--force", action="store_true",
        help="overwrite an existing symlink without prompting",
    )

    # --- link-folder -------------------------------------------------------
    p_lf = sub.add_parser(
        "link-folder",
        formatter_class=RawDefaultsHelpFormatter,
        help="Link all executables inside a directory",
        description=textwrap.dedent("""\
            Scan a directory and create symlinks for every executable found.

            Executable detection:
              Windows  — .exe, .cmd, .bat, .ps1, .sh
              Linux    — files with the executable permission bit

            examples:
              sympath link-folder "C:\\Program Files\\LLVM\\bin"
              sympath link-folder /usr/local/go/bin --folder dev --force
        """),
    )
    p_lf.add_argument(
        "path",
        help="directory to scan for executables",
    )
    p_lf.add_argument(
        "--folder",
        metavar="NAME",
        help="target link folder",
    )
    p_lf.add_argument(
        "--force", action="store_true",
        help="overwrite existing links",
    )

    # --- remove ------------------------------------------------------------
    p_rm = sub.add_parser(
        "remove",
        formatter_class=RawDefaultsHelpFormatter,
        help="Remove a managed symlink",
        description=textwrap.dedent("""\
            Delete a symlink from a link directory.

            examples:
              sympath remove gcc.exe
              sympath remove python.exe --folder dev
        """),
    )
    p_rm.add_argument(
        "name",
        help="filename of the symlink to remove (e.g. gcc.exe)",
    )
    p_rm.add_argument(
        "--folder",
        metavar="NAME",
        help="folder containing the link",
    )

    # --- list --------------------------------------------------------------
    p_ls = sub.add_parser(
        "list",
        formatter_class=RawDefaultsHelpFormatter,
        help="List all managed symlinks",
        description=textwrap.dedent("""\
            Display every symlink in a link directory along with its target.
            Broken links (targets that no longer exist) are flagged.

            examples:
              sympath list
              sympath list --folder dev
        """),
    )
    p_ls.add_argument(
        "--folder",
        metavar="NAME",
        help="folder to list (default: the default folder)",
    )

    # --- repair ------------------------------------------------------------
    p_rep = sub.add_parser(
        "repair",
        formatter_class=RawDefaultsHelpFormatter,
        help="Find and report broken symlinks",
        description=textwrap.dedent("""\
            Scan a link directory for symlinks whose targets no longer exist
            and report them.

            examples:
              sympath repair
              sympath repair --folder dev
        """),
    )
    p_rep.add_argument(
        "--folder",
        metavar="NAME",
        help="folder to check (default: the default folder)",
    )

    return parser


def main(argv: list[str] | None = None) -> None:
    parser = build_parser()
    args = parser.parse_args(argv)

    if args.command is None:
        parser.print_help()
        sys.exit(0)

    cfg = load_config()
    manager = SymlinkManager(cfg)

    try:
        if args.command == "add-folder":
            manager.add_folder(args.name, args.path)

        elif args.command == "set-default":
            manager.set_default(args.name)

        elif args.command == "link":
            manager.create_link(
                args.target,
                folder=args.folder,
                name=args.name,
                force=args.force,
            )

        elif args.command == "link-folder":
            manager.link_folder(
                args.path,
                folder=args.folder,
                force=args.force,
            )

        elif args.command == "remove":
            manager.remove_link(args.name, folder=args.folder)

        elif args.command == "list":
            manager.list_links(folder=args.folder)

        elif args.command == "repair":
            manager.repair(folder=args.folder)

    except ValueError as exc:
        print(f"Error: {exc}")
        sys.exit(1)

    save_config(cfg)
