"""Find executable path for commands."""

from __future__ import annotations

import argparse
import logging
import os
import platform
import subprocess
from dataclasses import dataclass
from functools import cached_property

logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)

IS_WINDOWS = platform.system() == "Windows"
EXTENSION = ".exe" if IS_WINDOWS else ""


@dataclass
class WindowsBuiltinCommands:
    """Manage Windows built-in shell commands."""

    _commands: set[str] | None = None

    @cached_property
    def commands(self) -> set[str]:
        """Get the set of Windows built-in commands - computed once and cached."""
        if self._commands is None:
            self._commands = {
                "assoc",
                "attrib",
                "break",
                "bcdedit",
                "cacls",
                "call",
                "cd",
                "chcp",
                "chdir",
                "chkdsk",
                "chkntfs",
                "cls",
                "cmd",
                "color",
                "comp",
                "compact",
                "convert",
                "copy",
                "date",
                "del",
                "dir",
                "diskpart",
                "doskey",
                "driverquery",
                "echo",
                "endlocal",
                "erase",
                "exit",
                "fc",
                "find",
                "findstr",
                "for",
                "format",
                "fsutil",
                "ftype",
                "goto",
                "gpresult",
                "help",
                "icacls",
                "if",
                "label",
                "md",
                "mkdir",
                "mklink",
                "mode",
                "more",
                "move",
                "openfiles",
                "path",
                "pause",
                "popd",
                "print",
                "prompt",
                "pushd",
                "rd",
                "recover",
                "rem",
                "ren",
                "rename",
                "replace",
                "rmdir",
                "robocopy",
                "set",
                "setlocal",
                "sc",
                "schtasks",
                "shift",
                "shutdown",
                "sort",
                "start",
                "subst",
                "systeminfo",
                "tasklist",
                "taskkill",
                "time",
                "title",
                "tree",
                "type",
                "ver",
                "verify",
                "vol",
                "xcopy",
                "wmic",
            }
        return self._commands

    def is_builtin(self, command: str) -> bool:
        """Check if a command is a Windows built-in command.

        Returns
        -------
            bool: True if the command is a built-in command, False otherwise.
        """
        return command.lower() in self.commands

    @cached_property
    def cmd_exe_path(self) -> str:
        """Get the path to cmd.exe for built-in commands."""
        return "C:\\Windows\\System32\\cmd.exe (built-in)"


# Global instance for Windows built-in command management
WINDOWS_BUILTINS = WindowsBuiltinCommands()


def find_executable(name: str, *, fuzzy: bool) -> tuple[str, list[str] | str | None]:
    """Find executable path for commands cross-platform.

    Args:
        name: Name of the command to find
        fuzzy: Whether to use fuzzy matching

    Returns
    -------
        Tuple containing the command name and its path if found, None otherwise.
    """
    try:
        # Select command based on system
        match_name = name if not fuzzy else f"*{name}*{EXTENSION}"
        cmd = ["where" if IS_WINDOWS else "which", match_name]

        # Run command and capture output
        result = subprocess.run(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,
            text=True,
            check=True,
        )

        # Handle Windows multiple results case
        paths = [path.strip() for path in result.stdout.strip().split("\n") if path.strip()]
        if IS_WINDOWS:
            # Return list of paths for Windows
            return (name, paths)
        # Return single path string for Unix
        return (name, result.stdout.strip())

    except (subprocess.CalledProcessError, FileNotFoundError):
        # For Windows, check if it's a built-in command
        if IS_WINDOWS and WINDOWS_BUILTINS.is_builtin(name):
            return name, WINDOWS_BUILTINS.cmd_exe_path

        # Check direct executable path on UNIX systems
        if not IS_WINDOWS and os.access(f"/usr/bin/{name}", os.X_OK):
            return name, f"/usr/bin/{name}"

        return name, None


def main() -> None:
    """Run entry point for the command-line interface."""
    parser = argparse.ArgumentParser(description="Find executable path for commands.")
    parser.add_argument("commands", type=str, nargs="+", help="Commands to query")
    parser.add_argument(
        "-f",
        "--fuzzy",
        action="store_true",
        help="Enable fuzzy matching",
    )
    parser.add_argument(
        "-q",
        "--quiet",
        action="store_true",
        help="Only print paths, no status symbols",
    )
    parser.add_argument("--debug", "-d", action="store_true", help="Enable debug mode")

    args = parser.parse_args()

    if args.debug:
        logger.setLevel(logger.DEBUG)

    found_count = 0
    fuzzy = args.fuzzy
    quiet = args.quiet

    logger.debug("Searching for executable paths...")
    for command in args.commands:
        name, path = find_executable(command, fuzzy=fuzzy)
        if path:
            found_count += 1
            if quiet:
                if isinstance(path, list):
                    for p in path:
                        logger.info(p)
                else:
                    logger.info(path)
            else:
                # Use ASCII characters for better compatibility
                if isinstance(path, list):
                    for p in path:
                        logger.info(f"[OK] {name} -> {p}")
                else:
                    logger.info(f"[OK] {name} -> {path}")
        elif not quiet:
            logger.info(f"[NOT FOUND] {name} -> not found")

    if not quiet and len(args.commands) > 1:
        logger.info(f"\nFound {found_count}/{len(args.commands)} commands")
