#!/usr/bin/python3

# pylint: disable=[C0302]

from __future__ import annotations

import argparse
import logging
import re
import shutil
import textwrap
from typing import Any, NoReturn, Optional

from slpkg.config import config_load

logger = logging.getLogger(__name__)


class SlpkgParser(argparse.ArgumentParser):
    """Custom ArgumentParser that replaces the default argparse error message
    for unknown commands with a concise, user-friendly hint."""

    def error(self, message: str) -> NoReturn:
        if "invalid choice" in message:
            cmd = message.split("'")[1] if "'" in message else message
            self.exit(2, f"slpkg: Unknown command '{cmd}'.\nUse 'slpkg help' for available commands.\n")
        super().error(message)


# Command categories — shared by the formatter and the per-category help printer.
_COMMAND_CATEGORIES: list[tuple[str, list[str]]] = [
    ("Repository", ["update", "upgrade", "repo", "repo-info", "stats", "rollback", "self-check"]),
    ("Packages", ["build", "install", "reinstall", "rebuild", "install-pkg", "remove", "download", "export", "import", "verify"]),
    ("Query", ["list", "info", "search", "file-search", "list-files", "changelog", "history"]),
    ("Dependencies", ["depends", "reverse", "why", "orphans", "mark", "pin", "check-broken"]),
    ("System", ["config", "clean", "clean-system", "health-check", "audit", "blacklist", "version", "help"]),
]

# Epilog shown in per-category / per-command help pages.
_EPILOG_BASE = (
    "For command-specific help: slpkg help <COMMAND> or man slpkg.\n"
    "To manage updated config files (.new): slpkg_new-configs"
)

# Epilog shown only in the main `slpkg help` output (adds the category hint).
_EPILOG_MAIN = (
    _EPILOG_BASE
    + "\nFor category help: slpkg help <"
    + "|".join(cat.lower() for cat, _ in _COMMAND_CATEGORIES)
    + ">"
)


class CustomCommandsFormatter(argparse.RawDescriptionHelpFormatter):
    """
    Formatter that produces exactly the requested output format:
    - Main help shows: slpkg <COMMAND> [PACKAGES] [OPTIONS]
    - Command help shows: slpkg install [PACKAGE ...] [OPTIONS]
    - Clean command listing with proper indentation
    """

    def __init__(
        self, prog: str, indent_increment: int = 1, max_help_position: int = 27, width: Optional[int] = None
    ) -> None:
        super().__init__(prog, indent_increment, max_help_position, width)
        self.custom_usage = f"{self._prog} <COMMAND> [PACKAGES ...] [OPTIONS]"
        logger.debug("CustomCommandsFormatter initialized.")

    def add_usage(
        self,
        usage: Optional[str],
        actions: list[argparse.Action],  # type: ignore[override]
        groups: list[argparse._ArgumentGroup],  # type: ignore[override]
        prefix: Optional[str] = None,
    ) -> None:
        """Custom usage formatting that handles both main and command help"""
        if prefix is None:
            prefix = f"{config_load.bold}Usage{config_load.endc}: "

        # For the main parser's help, always use the custom usage.
        # Subcommand help will be handled by their own print_help() calls via subparsers_map.
        usage = self.custom_usage

        return super().add_usage(usage, actions, groups, prefix)  # type: ignore[arg-type]

    def _format_action(self, action: argparse.Action) -> Optional[str]:  # type: ignore
        """Remove command choices and format subcommands cleanly"""
        if isinstance(action, argparse._SubParsersAction) and action.dest == "command":  # pylint: disable=[W0212]
            # Store original values
            orig_help = action.help
            orig_choices = action.choices

            # Temporarily modify for clean output
            action.help = None
            action.choices = None  # type: ignore

            # Get formatted string
            result = super()._format_action(action)

            # Restore original values
            action.help = orig_help
            action.choices = orig_choices

            # Remove all unwanted artifacts
            result = re.sub(r"^ *command.*\n", "", result, flags=re.MULTILINE)
            result = re.sub(r"\{.*}.*\n", "", result, flags=re.MULTILINE)
            return result

        return super()._format_action(action)

    def _format_action_invocation(self, action: argparse.Action) -> Optional[str]:  # type: ignore
        """Ensure consistent 10-space indentation for commands"""
        if not action.option_strings and not isinstance(action, argparse._SubParsersAction):  # pylint: disable=[W0212]
            metavar = self._format_args(action, self._get_default_metavar_for_positional(action))
            return metavar
        return super()._format_action_invocation(action)

    def format_help(self) -> str:  # pylint: disable=[R0914]
        """Final help text processing with grouped command categories."""
        help_text = super().format_help()

        bold = config_load.bold
        endc = config_load.endc

        # Clean up argparse artifacts.
        help_text = re.sub(r"\{.*}.*\n", "", help_text)
        help_text = re.sub(r"command\n", "", help_text)
        # Capitalise the auto-generated section title.
        help_text = help_text.replace("options:\n", f"{bold}Global options{endc}:\n")

        # Locate the auto-generated "positional arguments:" commands block.
        marker = "positional arguments:\n"
        if marker not in help_text:
            return help_text

        split_idx = help_text.index(marker)
        before = help_text[:split_idx]
        rest = help_text[split_idx + len(marker):]

        # Split command lines from the epilog (separated by a blank line).
        parts = rest.split("\n\n", 1)
        commands_raw = parts[0]
        epilog = "\n\n" + parts[1] if len(parts) > 1 else ""

        # Build a lookup: primary command name → full formatted block (may span lines).
        cmd_lines: dict[str, str] = {}
        current_cmd: Optional[str] = None
        for line in commands_raw.split("\n"):
            m = re.match(r"  (\S+)", line)
            if m and line.strip():
                current_cmd = m.group(1)
                cmd_lines[current_cmd] = line
            elif current_cmd and line.strip():
                # Continuation line belonging to the previous command.
                cmd_lines[current_cmd] += "\n" + line

        # Command categories — order determines display order.
        categories = _COMMAND_CATEGORIES

        grouped: list[str] = []
        for cat_name, cmds in categories:
            grouped.append(f"{bold}{cat_name}:{endc}")
            grouped.extend(cmd_lines[cmd] for cmd in cmds if cmd in cmd_lines)
            grouped.append("")

        # Drop the trailing blank line.
        while grouped and grouped[-1] == "":
            grouped.pop()

        return before + "\n".join(grouped) + epilog


class SubCommandFormatter(argparse.HelpFormatter):
    """Formatter for subcommand help pages with a wider option column."""

    def __init__(
        self, prog: str, indent_increment: int = 2, max_help_position: int = 36, width: Optional[int] = None
    ) -> None:
        super().__init__(prog, indent_increment, max_help_position, width)


def _collect_subparser_info(parser: argparse.ArgumentParser) -> dict[str, tuple[str, str]]:
    """Return {dest: (metavar, help_text)} for all subcommand pseudo-actions."""
    for action in parser._actions:  # pylint: disable=protected-access
        if isinstance(action, argparse._SubParsersAction):  # pylint: disable=protected-access
            return {
                pseudo.dest: (pseudo.metavar or pseudo.dest, pseudo.help or "")
                for pseudo in getattr(action, "_choices_actions", [])
            }
    return {}


def _print_category_help(category_lower: str, parser: argparse.ArgumentParser) -> None:
    """Print usage line + one named command category."""
    cat_display = next(cat for cat, _ in _COMMAND_CATEGORIES if cat.lower() == category_lower)
    cat_cmds = next(cmds for cat, cmds in _COMMAND_CATEGORIES if cat.lower() == category_lower)
    cmd_info = _collect_subparser_info(parser)

    col = 27  # Match CustomCommandsFormatter max_help_position.
    help_width = max(shutil.get_terminal_size().columns - col, 20)
    indent = " " * col

    print(f"{config_load.bold}Usage{config_load.endc}: {parser.prog} <COMMAND> [PACKAGES ...] [OPTIONS]\n")
    print(f"{config_load.bold}{cat_display}:{config_load.endc}")
    for cmd in cat_cmds:
        if cmd in cmd_info:
            meta, help_text = cmd_info[cmd]
            left = f"  {meta}"
            padding = max(col - len(left), 2)
            lines = textwrap.wrap(help_text, width=help_width)
            if lines:
                print(f"{left}{' ' * padding}{lines[0]}")
                for cont in lines[1:]:
                    print(f"{indent}{cont}")
            else:
                print(left)
    print(f"\n{_EPILOG_BASE}")


def build_parser() -> tuple[argparse.ArgumentParser, dict[str, argparse.ArgumentParser]]:  # pylint: disable=[R0914,R0915]
    """Build and return the main argument parser and the subparsers map."""
    # --- Argument Parsers for common options ---
    # Each common option gets its own parser to be reused as a parent.
    # Arguments are added directly (no add_argument_group) so they merge
    # into the child parser's default "options" group without blank lines.
    yes_parser = argparse.ArgumentParser(add_help=False)
    yes_parser.add_argument("-y", "--yes", action="store_true", dest="option_yes", help="Answer Yes to all questions.")

    check_parser = argparse.ArgumentParser(add_help=False)
    check_parser.add_argument(
        "-c", "--check", "--dry-run", action="store_true", dest="option_check",
        help="Show what would happen without making changes (dry run).",
    )

    resolve_off_parser = argparse.ArgumentParser(add_help=False)
    resolve_off_parser.add_argument(
        "-N", "--no-resolve", action="store_true", dest="option_resolve_off", help="Turns off dependency resolving."
    )

    reinstall_parser = argparse.ArgumentParser(add_help=False)
    reinstall_parser.add_argument(
        "-r", "--reinstall", action="store_true", dest="option_reinstall", help="Upgrade packages of the same version."
    )

    skip_install_parser = argparse.ArgumentParser(add_help=False)
    skip_install_parser.add_argument(
        "-I",
        "--skip-installed",
        action="store_true",
        dest="option_skip_installed",
        help="Skip installed packages during the building or installation progress.",
    )

    fetch_parser = argparse.ArgumentParser(add_help=False)
    fetch_parser.add_argument(
        "-b",
        "--benchmark",
        action="store_true",
        dest="option_fetch",
        help="Benchmark available mirrors and report their response times.",
    )

    full_reverse_parser = argparse.ArgumentParser(add_help=False)
    full_reverse_parser.add_argument(
        "-F",
        "--full-reverse",
        action="store_true",
        dest="option_full_reverse",
        help="Display the full reverse dependency tree.",
    )

    select_parser = argparse.ArgumentParser(add_help=False)
    select_parser.add_argument(
        "-S",
        "--select",
        action="store_true",
        dest="option_select",
        help="Matching and select packages with selector or dialog.",
    )

    pkg_version_parser = argparse.ArgumentParser(add_help=False)
    pkg_version_parser.add_argument(
        "-p",
        "--pkg-version",
        action="store_true",
        dest="option_pkg_version",
        help="Print the package version.",
    )

    parallel_parser = argparse.ArgumentParser(add_help=False)
    parallel_parser.add_argument(
        "-P", "--parallel", action="store_true", dest="option_parallel", help="Enable download files in parallel."
    )

    no_case_parser = argparse.ArgumentParser(add_help=False)
    no_case_parser.add_argument(
        "-i", "--ignore-case", action="store_true", dest="option_no_case", help="Case-insensitive pattern matching."
    )

    lines_parser = argparse.ArgumentParser(add_help=False)
    lines_parser.add_argument(
        "-n",
        "--lines",
        type=int,
        default=0,
        dest="option_lines",
        metavar="N",
        help="Limit output to the first N lines (changelog) or last N entries (history). Default: all.",
    )

    short_parser = argparse.ArgumentParser(add_help=False)
    short_parser.add_argument(
        "-s",
        "--short",
        action="store_true",
        dest="option_short",
        help="Show a compact one-line-per-package summary ([DATE] package: Action).",
    )

    color_parser = argparse.ArgumentParser(add_help=False)
    color_parser.add_argument(
        "-C",
        "--color",
        choices=["on", "off", "ON", "OFF"],
        metavar="<ON/OFF>",
        dest="option_color",
        help="Switch on or off color output.",
    )

    dialog_parser = argparse.ArgumentParser(add_help=False)
    dialog_parser.add_argument(
        "-D",
        "--dialog",
        action="store_true",
        dest="option_dialog",
        help="Enable dialog-based interface instead, terminal selector.",
    )

    description_parser = argparse.ArgumentParser(add_help=False)
    description_parser.add_argument(
        "-t", "--description", action="store_true", dest="option_pkg_description", help="Print the package description."
    )

    edit_parser = argparse.ArgumentParser(add_help=False)
    edit_parser.add_argument(
        "-e", "--edit", action="store_true", dest="option_edit", help="Open the file with the system's default editor."
    )

    edit_sbo_parser = argparse.ArgumentParser(add_help=False)
    edit_sbo_parser.add_argument(
        "-e",
        "--edit",
        metavar="NAME",
        dest="option_sbo_edit",
        help="Open a specified SBo file for editing using the system's default editor, typically before a build operation. This functionality is applicable only to SBo repositories. Use '*' to select all relevant SBo files.",
    )

    repo_tag_parser = argparse.ArgumentParser(add_help=False)
    repo_tag_parser.add_argument(
        "-T",
        "--repo-tag",
        metavar="TAG",
        dest="option_repo_tag",
        help="Sets a specific repository tag (TAG) for SlackBuild scripts, used to distinguish your custom builds. Provide the desired tag value (e.g., _custom). This is typically used before a build operation and applies exclusively to SlackBuild repositories.",
    )

    pager_parser = argparse.ArgumentParser(add_help=False)
    pager_parser.add_argument(
        "-g", "--pager", action="store_true", dest="option_pager", help="Enables viewing output through a pager."
    )

    repository_parser = argparse.ArgumentParser(add_help=False)
    repository_parser.add_argument(
        "-o",
        "--repository",
        metavar="<NAME>",
        dest="option_repository",
        help="Sets the active repository for package management operations. Overrides the default repository from the configuration file.",
    )

    directory_parser = argparse.ArgumentParser(add_help=False)
    directory_parser.add_argument(
        "-w", "--output-dir", metavar="<PATH>", dest="option_directory", help="Download files to a specific path."
    )

    query_parser = argparse.ArgumentParser(add_help=False)
    query_parser.add_argument(
        "-Q", "--query", metavar="<QUERY>", dest="option_query", help="Filter results based on a search query."
    )

    date_parser = argparse.ArgumentParser(add_help=False)
    date_parser.add_argument(
        "--date",
        metavar="<YYYY-MM-DD>",
        dest="option_date",
        default="",
        help="Show only entries for the given date (format: YYYY-MM-DD). Used with changelog.",
    )

    json_parser = argparse.ArgumentParser(add_help=False)
    json_parser.add_argument(
        "-j",
        "--json",
        action="store_true",
        dest="option_json",
        help="Output results in JSON format, suitable for scripting.",
    )

    quiet_parser = argparse.ArgumentParser(add_help=False)
    quiet_parser.add_argument(
        "-q",
        "--quiet",
        action="store_true",
        dest="option_quiet",
        help="Suppress verbose output and headers. Depending on the command, it prints only names (packages, files, etc.) or shows a progress bar, suitable for scripting.",
    )

    transitive_parser = argparse.ArgumentParser(add_help=False)
    transitive_parser.add_argument(
        "-t",
        "--transitive",
        action="store_true",
        dest="option_transitive",
        help="Follow the dependency chain upward recursively.",
    )

    unmark_parser = argparse.ArgumentParser(add_help=False)
    unmark_parser.add_argument(
        "-u",
        "--unmark",
        action="store_true",
        dest="option_unmark",
        help="Unmark package(s) — treat as auto-installed again (candidate for orphans).",
    )

    unpin_parser = argparse.ArgumentParser(add_help=False)
    unpin_parser.add_argument(
        "-u",
        "--unpin",
        action="store_true",
        dest="option_unpin",
        help="Unpin package(s) — allow upgrades again.",
    )

    blacklist_remove_parser = argparse.ArgumentParser(add_help=False)
    blacklist_remove_parser.add_argument(
        "-r",
        "--remove",
        action="store_true",
        dest="option_blacklist_remove",
        help="Remove package(s) from the blacklist.",
    )

    local_parser = argparse.ArgumentParser(add_help=False)
    local_parser.add_argument(
        "-L",
        "--local",
        action="store_true",
        dest="option_local",
        help="Search for files in locally installed packages instead of repositories.",
    )

    extra_args_parser = argparse.ArgumentParser(add_help=False)
    extra_args_parser.add_argument(
        "-X",
        "--extra-args",
        dest="option_extra_args",
        metavar="ARGS",
        default=None,
        help='Pass extra arguments directly to installpkg/upgradepkg (e.g. -X "--root /mnt").',
    )

    # debug_parser only for the command line menu,
    # the task of changing the logging level is achieved
    # at the beginning of the file through the sys.argv module.
    debug_parser = argparse.ArgumentParser(add_help=False)
    debug_parser.add_argument(
        "-d", "--debug", action="store_true", dest="option_debug", help="Sets the logging level to DEBUG."
    )

    no_lock_parser = argparse.ArgumentParser(add_help=False)
    no_lock_parser.add_argument(
        "-k", "--no-lock", action="store_true", dest="option_no_lock", help="Disable the process lock."
    )

    # --- Main Parser ---
    parser: argparse.ArgumentParser = SlpkgParser(
        prog="slpkg",
        description="Package manager for Slackware Linux.",
        formatter_class=CustomCommandsFormatter,
        epilog=_EPILOG_MAIN,
        add_help=False,
    )
    logger.debug("Main argparse parser created.")

    parser.add_argument(
        "-o",
        "--repository",
        metavar="<NAME>",
        dest="option_repository_global",
        help="Override the default repository for the current command.",
    )
    parser.add_argument(
        "-d",
        "--debug",
        action="store_true",
        dest="option_debug",
        help="Sets the logging level to DEBUG.",
    )
    parser.add_argument(
        "-k",
        "--no-lock",
        action="store_true",
        dest="option_no_lock",
        help="Disable the process lock.",
    )

    subparsers = parser.add_subparsers(dest="command")
    _real_add_parser = subparsers.add_parser

    def _add_parser_with_formatter(name: str, **kwargs: Any) -> argparse.ArgumentParser:
        kwargs.setdefault("formatter_class", SubCommandFormatter)
        return _real_add_parser(name, **kwargs)

    subparsers.add_parser = _add_parser_with_formatter  # type: ignore[method-assign]
    subparsers.required = True  # Makes subcommand selection mandatory.
    logger.debug("Subparsers created and set as required.")

    # Dictionary to store references to subparsers for easy access by the 'help' command
    subparsers_map: dict[str, argparse.ArgumentParser] = {}

    # --- Subcommand Parsers ---
    # Store each subparser in the map after creation
    subparsers_map["update"] = subparsers.add_parser(
        "update",
        aliases=["u", "upd"],
        parents=[check_parser, color_parser, repository_parser, debug_parser, no_lock_parser],
        help="Sync remote repositories and update the local package database.",
    )
    subparsers_map["upgrade"] = subparsers.add_parser(
        "upgrade",
        aliases=["U", "upg"],
        parents=[
            yes_parser,
            check_parser,
            resolve_off_parser,
            quiet_parser,
            parallel_parser,
            reinstall_parser,
            no_case_parser,
            select_parser,
            dialog_parser,
            color_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Upgrade all installed packages that have newer versions available.",
    )
    subparsers_map["upgrade"].add_argument(
        "--repos-priority",
        nargs="+",
        metavar="NAME",
        dest="option_repos_priority",
        help="Override all-repos processing order for this run only (transient).",
    )
    subparsers_map["config"] = subparsers.add_parser(
        "config",
        aliases=["C", "conf"],
        parents=[edit_parser, dialog_parser, debug_parser, no_lock_parser],
        help="Edit the slpkg.toml configuration file using dialog or $EDITOR.",
    )
    subparsers_map["config"].add_argument(
        "--validate",
        action="store_true",
        dest="option_validate",
        help="Validate slpkg.toml for syntax and value errors without editing.",
    )
    subparsers_map["repo-info"] = subparsers.add_parser(
        "repo-info",
        aliases=["ri", "rinf"],
        parents=[fetch_parser, json_parser, color_parser, repository_parser, debug_parser, no_lock_parser],
        help="Show active repositories, last update time, and package counts.",
    )
    repo_parser = subparsers.add_parser(
        "repo",
        aliases=["rp", "rep"],
        parents=[quiet_parser, json_parser, color_parser, debug_parser, no_lock_parser],
        help="List, enable, or disable repositories.",
    )
    repo_parser.add_argument(
        "--enable",
        nargs="+",
        metavar="NAME",
        dest="option_repo_enable",
        help="Enable one or more repositories.",
    )
    repo_parser.add_argument(
        "--disable",
        nargs="+",
        metavar="NAME",
        dest="option_repo_disable",
        help="Disable one or more repositories.",
    )
    repo_parser.add_argument(
        "--mirror",
        nargs=2,
        metavar=("NAME", "URL"),
        dest="option_repo_mirror",
        help="Set a new mirror URL for a repository.",
    )
    repo_parser.add_argument(
        "--default",
        metavar="NAME",
        dest="option_repo_default",
        help="Set the default repository.",
    )
    repo_parser.add_argument(
        "--priority",
        nargs="*",
        metavar="NAME",
        dest="option_repo_priority",
        help="Set the repository priority order or show it when called with no arguments.",
    )
    repo_parser.add_argument(
        "--clear-priority",
        action="store_true",
        dest="option_repo_clear_priority",
        help="Clear the repository priority order.",
    )
    subparsers_map["repo"] = repo_parser

    subparsers_map["stats"] = subparsers.add_parser(
        "stats",
        aliases=["st", "stt"],
        parents=[json_parser, color_parser, debug_parser, no_lock_parser],
        help="Display installation statistics and repository distribution.",
    )
    subparsers_map["stats"].add_argument(
        "-t",
        "--top",
        type=int,
        metavar="N",
        dest="option_top",
        help="Display the top N largest installed packages.",
    )
    subparsers_map["rollback"] = subparsers.add_parser(
        "rollback",
        aliases=["rb", "und"],
        parents=[yes_parser, color_parser, debug_parser, no_lock_parser],
        help="Undo the last package transaction based on history.",
    )
    subparsers_map["clean"] = subparsers.add_parser(
        "clean",
        aliases=["c", "cln"],
        parents=[debug_parser, no_lock_parser],
        help="Remove all cached SlackBuild scripts, sources, and built packages.",
    )
    subparsers_map["clean-system"] = subparsers.add_parser(
        "clean-system",
        aliases=["cs", "csy"],
        parents=[yes_parser, quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="Detect and remove packages not found in any enabled repository.",
    )
    subparsers_map["clean-system"].add_argument(
        "-A",
        "--autoremove",
        action="store_true",
        dest="option_autoremove",
        help="Automatically remove all foreign packages.",
    )
    subparsers_map["health-check"] = subparsers.add_parser(
        "health-check",
        aliases=["hc", "hchk"],
        parents=[quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="Scan system for broken shared library links (ldd check).",
    )
    subparsers_map["health-check"].add_argument(
        "packages",
        nargs="*",
        metavar="PACKAGE",
        help="Optional package names to scan. If omitted, all packages are checked.",
    )
    subparsers_map["audit"] = subparsers.add_parser(
        "audit",
        aliases=["ad", "adt"],
        parents=[color_parser, debug_parser, no_lock_parser],
        help="Audit installed packages for security vulnerabilities (OSV API).",
    )
    subparsers_map["audit"].add_argument(
        "packages",
        nargs="*",
        metavar="PACKAGE",
        help="Optional package names to audit. If omitted, all packages are checked.",
    )
    subparsers_map["self-check"] = subparsers.add_parser(
        "self-check",
        aliases=["sc", "schk"],
        parents=[debug_parser, no_lock_parser],
        help="Check if a newer version of slpkg is available.",
    )

    verify_pkgs_parser = subparsers.add_parser(
        "verify",
        aliases=["V", "vfy"],
        parents=[yes_parser, quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="Check all installed packages for missing or removed files.",
    )
    verify_pkgs_parser.add_argument(
        "packages",
        nargs="*",
        metavar="PACKAGE",
        help="Package names to verify. If omitted, all installed packages are verified.",
    )
    verify_pkgs_parser.add_argument(
        "-f", "--fix",
        dest="option_fix",
        action="store_true",
        default=False,
        help="Reinstall packages with missing files after verification.",
    )

    subparsers_map["verify"] = verify_pkgs_parser
    build_cmd_parser = subparsers.add_parser(
        "build",
        aliases=["b", "bld"],
        parents=[
            yes_parser,
            resolve_off_parser,
            skip_install_parser,
            quiet_parser,
            parallel_parser,
            no_case_parser,
            select_parser,
            dialog_parser,
            edit_sbo_parser,
            repo_tag_parser,
            color_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Compile packages from SlackBuilds scripts without installing them.",
    )
    build_cmd_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to build.")
    subparsers_map["build"] = build_cmd_parser

    reinstall_cmd_parser = subparsers.add_parser(
        "reinstall",
        aliases=["re", "rein"],
        parents=[
            yes_parser,
            resolve_off_parser,
            quiet_parser,
            parallel_parser,
            no_case_parser,
            select_parser,
            dialog_parser,
            edit_sbo_parser,
            repo_tag_parser,
            color_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Reinstall installed packages from their source repository.",
    )
    reinstall_cmd_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to reinstall.")
    subparsers_map["reinstall"] = reinstall_cmd_parser

    install_parser = subparsers.add_parser(
        "install",
        aliases=["i", "inst"],
        parents=[
            yes_parser,
            check_parser,
            reinstall_parser,
            resolve_off_parser,
            skip_install_parser,
            quiet_parser,
            parallel_parser,
            no_case_parser,
            select_parser,
            dialog_parser,
            edit_sbo_parser,
            repo_tag_parser,
            color_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Resolve dependencies, then build and install packages in order.",
    )
    install_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to install.")
    install_parser.add_argument(
        "--repos-priority",
        nargs="+",
        metavar="NAME",
        dest="option_repos_priority",
        help="Override all-repos processing order for this run only (transient).",
    )
    subparsers_map["install"] = install_parser

    rebuild_parser = subparsers.add_parser(
        "rebuild",
        aliases=["rbd", "rbld"],
        parents=[
            yes_parser,
            check_parser,
            quiet_parser,
            parallel_parser,
            select_parser,
            dialog_parser,
            repo_tag_parser,
            color_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Rebuild all installed SlackBuilds packages from a SBo/Ponce repository.",
    )
    subparsers_map["rebuild"] = rebuild_parser

    install_pkg_parser = subparsers.add_parser(
        "install-pkg",
        aliases=["ip", "ipkg"],
        parents=[
            yes_parser,
            reinstall_parser,
            quiet_parser,
            extra_args_parser,
            color_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Install one or more local Slackware package files (.txz, .tgz, .tlz, .tbz).",
    )
    install_pkg_parser.add_argument("packages", nargs="+", metavar="FILE", help="Local package file paths to install.")
    subparsers_map["install-pkg"] = install_pkg_parser

    remove_parser = subparsers.add_parser(
        "remove",
        aliases=["R", "rmv"],
        parents=[
            yes_parser,
            check_parser,
            resolve_off_parser,
            select_parser,
            dialog_parser,
            quiet_parser,
            color_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Remove packages and their tracked dependency chain.",
    )
    remove_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to remove.")
    subparsers_map["remove"] = remove_parser

    download_parser = subparsers.add_parser(
        "download",
        aliases=["dl", "dwn"],
        parents=[
            yes_parser,
            no_case_parser,
            select_parser,
            dialog_parser,
            color_parser,
            directory_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Download SlackBuild scripts and source files without building.",
    )
    download_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to download.")
    subparsers_map["download"] = download_parser

    export_parser = subparsers.add_parser(
        "export",
        aliases=["ex", "exp"],
        parents=[color_parser, debug_parser, no_lock_parser],
        help="Export a list of manually installed packages to a file.",
    )
    export_parser.add_argument(
        "filename",
        metavar="FILE",
        help="The name of the file to save the package list (e.g., packages.pkgs).",
    )
    export_parser.add_argument(
        "-n",
        "--no-deps",
        action="store_true",
        dest="option_no_deps",
        help="Do not include dependencies as comments in the export file.",
    )
    subparsers_map["export"] = export_parser

    import_parser = subparsers.add_parser(
        "import",
        aliases=["im", "imp"],
        parents=[
            yes_parser,
            reinstall_parser,
            resolve_off_parser,
            skip_install_parser,
            quiet_parser,
            parallel_parser,
            edit_sbo_parser,
            repo_tag_parser,
            color_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Import and install packages from a .pkgs file after validation.",
    )
    import_parser.add_argument(
        "filename",
        metavar="FILE",
        help="The name of the file to import packages from.",
    )
    subparsers_map["import"] = import_parser

    list_parser = subparsers.add_parser(
        "list",
        aliases=["l", "lst"],
        parents=[no_case_parser, description_parser, select_parser, dialog_parser, json_parser, color_parser, debug_parser, no_lock_parser],
        help="List installed packages, optionally filtered by name pattern.",
    )
    list_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to display.")
    subparsers_map["list"] = list_parser

    info_parser = subparsers.add_parser(
        "info",
        aliases=["inf", "show"],
        parents=[no_case_parser, select_parser, dialog_parser, json_parser, color_parser, repository_parser, debug_parser, no_lock_parser],
        help="Show detailed package information from the repository.",
    )
    info_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to display information for.")
    subparsers_map["info"] = info_parser

    search_parser = subparsers.add_parser(
        "search",
        aliases=["s", "ser"],
        parents=[
            no_case_parser,
            pkg_version_parser,
            description_parser,
            select_parser,
            dialog_parser,
            quiet_parser,
            json_parser,
            color_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Search for packages by name across all enabled repositories.",
    )
    search_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to search for.")
    subparsers_map["search"] = search_parser

    file_search_parser = subparsers.add_parser(
        "file-search",
        aliases=["f", "fs"],
        parents=[no_case_parser, quiet_parser, local_parser, color_parser, repository_parser, debug_parser, no_lock_parser],
        help="Find which repository package provides a specific file or path.",
    )
    file_search_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to search for.")
    subparsers_map["file-search"] = file_search_parser

    list_files_parser = subparsers.add_parser(
        "list-files",
        aliases=["lf", "lfl"],
        parents=[no_case_parser, quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="List all files installed by a package.",
    )
    list_files_parser.add_argument("packages", nargs="+", metavar="PACKAGE", help="Package names to list files for.")
    subparsers_map["list-files"] = list_files_parser

    reverse_parser = subparsers.add_parser(
        "reverse",
        aliases=["r", "rdeps"],
        parents=[
            no_case_parser,
            quiet_parser,
            pkg_version_parser,
            full_reverse_parser,
            select_parser,
            dialog_parser,
            color_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Show which repository packages depend on the given package.",
    )
    reverse_parser.add_argument(
        "packages", nargs="+", metavar="PACKAGE", help="Package names to look up reverse dependencies for."
    )
    subparsers_map["reverse"] = reverse_parser

    depends_parser = subparsers.add_parser(
        "depends",
        aliases=["d", "dep"],
        parents=[
            no_case_parser,
            quiet_parser,
            pkg_version_parser,
            select_parser,
            dialog_parser,
            color_parser,
            repository_parser,
            debug_parser,
            no_lock_parser,
        ],
        help="Show the full forward dependency tree for a package.",
    )
    depends_parser.add_argument(
        "packages", nargs="+", metavar="PACKAGE", help="Package names to display dependencies for."
    )
    subparsers_map["depends"] = depends_parser

    changelog_parser = subparsers.add_parser(
        "changelog",
        aliases=["n", "chg"],
        parents=[edit_parser, lines_parser, short_parser, date_parser, pager_parser, repository_parser, query_parser, color_parser, debug_parser, no_lock_parser],
        help="Show the changelog for a repository with optional text filter.",
    )
    subparsers_map["changelog"] = changelog_parser

    history_parser = subparsers.add_parser(
        "history",
        aliases=["hi", "hst"],
        parents=[lines_parser, json_parser, color_parser, debug_parser, no_lock_parser],
        help="Show a log of all past install, upgrade, and remove operations.",
    )
    history_parser.add_argument(
        "-x",
        "--clear",
        action="store_true",
        dest="option_clear_history",
        help="Delete the history log file.",
    )
    history_parser.add_argument(
        "--package",
        metavar="NAME",
        dest="option_history_package",
        default="",
        help="Filter history to show only entries for the given package name.",
    )
    subparsers_map["history"] = history_parser

    orphans_parser = subparsers.add_parser(
        "orphans",
        aliases=["or", "orp"],
        parents=[yes_parser, quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="Find installed dependency packages no longer needed by any package.",
    )
    orphans_parser.add_argument(
        "-A",
        "--autoremove",
        action="store_true",
        dest="option_autoremove",
        help="Automatically remove all orphaned packages.",
    )
    subparsers_map["orphans"] = orphans_parser

    why_parser = subparsers.add_parser(
        "why",
        aliases=["w", "wy"],
        parents=[quiet_parser, transitive_parser, pkg_version_parser, color_parser, debug_parser, no_lock_parser],
        help="Show which installed packages have the given package as a dependency.",
    )
    why_parser.add_argument(
        "packages",
        nargs="+",
        metavar="PACKAGE",
        help="Package name(s) to check.",
    )
    subparsers_map["why"] = why_parser

    mark_parser = subparsers.add_parser(
        "mark",
        aliases=["mk", "mrk"],
        parents=[unmark_parser, quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="Mark a package as manually installed to exclude it from orphans.",
    )
    mark_parser.add_argument(
        "packages",
        nargs="*",
        metavar="PACKAGE",
        help="Package name(s) to mark. Omit to list currently marked packages.",
    )
    subparsers_map["mark"] = mark_parser

    pin_parser = subparsers.add_parser(
        "pin",
        aliases=["pn", "pnh"],
        parents=[unpin_parser, quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="Pin a package to exclude it from upgrades.",
    )
    pin_parser.add_argument(
        "packages",
        nargs="*",
        metavar="PACKAGE",
        help="Package name(s) to pin. Omit to list currently pinned packages.",
    )
    subparsers_map["pin"] = pin_parser

    check_broken_parser = subparsers.add_parser(
        "check-broken",
        aliases=["cb", "cbr"],
        parents=[quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="Find installed packages with missing tracked dependencies.",
    )
    subparsers_map["check-broken"] = check_broken_parser

    blacklist_parser = subparsers.add_parser(
        "blacklist",
        aliases=["bl", "blk"],
        parents=[blacklist_remove_parser, quiet_parser, color_parser, debug_parser, no_lock_parser],
        help="Display, add to, or remove from the blacklist.",
    )
    blacklist_parser.add_argument(
        "packages",
        nargs="*",
        metavar="PACKAGE",
        help="Package name(s) to add. Omit to list blacklisted packages.",
    )
    subparsers_map["blacklist"] = blacklist_parser

    version_parser = subparsers.add_parser("version", aliases=["v", "ver"], parents=[debug_parser, no_lock_parser], help="Show version and exit.")
    subparsers_map["version"] = version_parser

    help_parser = subparsers.add_parser(
        "help", aliases=["h", "hlp"], parents=[debug_parser, no_lock_parser], help="Show this help message and exit."
    )
    help_parser.add_argument(
        "command_for_help",
        nargs="?",
        help="Show help for a specific command or category (repository, packages, query, dependencies, system).",
    )
    subparsers_map["help"] = help_parser  # Store reference for 'help' itself

    logger.debug("All subcommand parsers defined and stored in subparsers_map.")

    return parser, subparsers_map
