import typing

import chitragupt.client as client
import chitragupt.core.logger as logger
import chitragupt.core.plugin as plugin

GuptLogger = logger.GuptLogger
LogType = logger.LogType

# Create the system-level plugin for internal commands.
system_commands: plugin.ChitraguptPlugin = plugin.ChitraguptPlugin(
    name="root", description="Commands reserved by the internal system."
)


@system_commands.register(command_name="pgms")
def pgms_command(
    framework: client.ChitraGuptClientCLI,
    cmd_str: str,
) -> None:
    """
    Ultimate plugin management command.

    The `pgms` command provides a pip-like interface to manage plugins from the
    Plugin Garden. Supported subcommands include:

        - install <plugin1> [plugin2 ...]    : Install one or more plugins.
        - uninstall <plugin1> [plugin2 ...]  : Uninstall one or more plugins.
        - list                             : List installed plugins.
        - available                        : List available plugins from the remote repository.
        - search <query>                   : Search available plugins by name.
        - update <plugin1> [plugin2 ...]     : Update one or more installed plugins.
        - update-all                       : Update all installed plugins.
        - refresh                          : Refresh the available plugins list from the remote repository.
        - details <plugin>                 : Retrieve and display details of an installed plugin.
        - load <plugin>                    : Dynamically load an installed plugin module.
        - help                             : Display usage information for pgms.

    The command parses the full command string (`cmd_str`) to determine the subcommand
    and its arguments, then calls the corresponding PluginManager method (integrated
    into the framework as `plugin_manager`). All output is logged using the framework's logger.

    Parameters
    ----------
    framework : client.ChitraGuptClientCLI
        The CLI framework instance containing the pre-initialized logger and integrated plugin_manager.
    cmd_str : str
        The full command string used to invoke this command. For example:
        "pgms install audio video" or "pgms search network"

    Returns
    -------
    None

    Raises
    ------
    Exception
        Any exception encountered during processing is logged.

    Examples
    --------
    >>> # To install plugins "audio" and "video":
    >>> pgms_command(framework, "pgms install audio video")
    >>> # To list installed plugins:
    >>> pgms_command(framework, "pgms list")
    >>> # To display help:
    >>> pgms_command(framework, "pgms help")
    """
    # Split the command string into tokens.
    tokens: typing.List[str] = cmd_str.strip().split()
    if len(tokens) < 2:
        framework.logger.log(
            message=(
                "Usage: pgms <subcommand> [arguments]\n"
                "Subcommands: install, uninstall, list, available, search, update, update-all, refresh, details, load, help"
            ),
            log_type=LogType.ERROR,
        )
        return

    subcommand: str = tokens[1].lower()
    args: typing.List[str] = tokens[2:] if len(tokens) > 2 else []

    # Retrieve the integrated PluginManager from the framework.
    pm = framework.plugin_manager

    # For most subcommands refresh available plugins.
    if subcommand in (
        "install",
        "uninstall",
        "update",
        "update-all",
        "search",
        "available",
        "details",
        "load",
        "refresh",
    ):
        try:
            pm.refresh_available_plugins()
        except Exception as refresh_err:
            framework.logger.log(
                message=f"Error refreshing available plugins: {refresh_err}",
                log_type=LogType.ERROR,
            )
            return

    # Process each subcommand.
    if subcommand == "install":
        if not args:
            framework.logger.log(
                message="Usage: pgms install <plugin1> [plugin2 ...]",
                log_type=LogType.ERROR,
            )
            return
        for plugin in args:
            try:
                lodable_plugin = pm.install_plugin(plugin)
                framework.load_plugin(lodable_plugin)
                framework.logger.log(
                    message=f"Installed plugin '{plugin}'.",
                    log_type=LogType.INFO,
                )
            except Exception as e:
                framework.logger.log(
                    message=f"Error installing plugin '{plugin}': {e}",
                    log_type=LogType.ERROR,
                )

    elif subcommand == "uninstall":
        if not args:
            framework.logger.log(
                message="Usage: pgms uninstall <plugin1> [plugin2 ...]",
                log_type=LogType.ERROR,
            )
            return
        for plugin in args:
            try:
                pm.uninstall_plugin(plugin)
                framework.logger.log(
                    message=f"Uninstalled plugin '{plugin}'.",
                    log_type=LogType.INFO,
                )
            except Exception as e:
                framework.logger.log(
                    message=f"Error uninstalling plugin '{plugin}': {e}",
                    log_type=LogType.ERROR,
                )

    elif subcommand == "list":
        try:
            installed = pm.list_installed_plugins()
            framework.logger.log(
                message=f"Installed plugins: {installed}",
                log_type=LogType.DEBUG,
            )
            if installed:
                framework.logger.log(
                    message="Installed plugins: " + ", ".join(installed),
                    log_type=LogType.INFO,
                )
            else:
                framework.logger.log(
                    message="No plugins are currently installed.",
                    log_type=LogType.INFO,
                )
        except Exception as e:
            framework.logger.log(
                message=f"Error listing installed plugins: {e}",
                log_type=LogType.ERROR,
            )

    elif subcommand == "available":
        try:
            available = pm.list_available_plugins()
            framework.logger.log(
                message=f"Available plugins: {available}",
                log_type=LogType.DEBUG,
            )
            if available:
                framework.logger.log(
                    message="Available plugins: " + ", ".join(available),
                    log_type=LogType.INFO,
                )
            else:
                framework.logger.log(
                    message="No plugins are available in the remote repository.",
                    log_type=LogType.INFO,
                )
        except Exception as e:
            framework.logger.log(
                message=f"Error listing available plugins: {e}",
                log_type=LogType.ERROR,
            )

    elif subcommand == "search":
        if not args:
            framework.logger.log(
                message="Usage: pgms search <query>",
                log_type=LogType.ERROR,
            )
            return
        query: str = " ".join(args)
        try:
            results = pm.search_plugins(query)
            framework.logger.log(
                message=f"Search results for '{query}': {results}",
                log_type=LogType.DEBUG,
            )
            if results:
                framework.logger.log(
                    message=f"Search results for '{query}': "
                    + ", ".join(results),
                    log_type=LogType.INFO,
                )
            else:
                framework.logger.log(
                    message=f"No plugins found matching '{query}'.",
                    log_type=LogType.INFO,
                )
        except Exception as e:
            framework.logger.log(
                message=f"Error searching plugins with query '{query}': {e}",
                log_type=LogType.ERROR,
            )

    elif subcommand == "update":
        if not args:
            framework.logger.log(
                message="Usage: pgms update <plugin1> [plugin2 ...]",
                log_type=LogType.ERROR,
            )
            return
        for plugin in args:
            try:
                pm.update_plugin(plugin)
                framework.logger.log(
                    message=f"Updated plugin '{plugin}'.",
                    log_type=LogType.INFO,
                )
            except Exception as e:
                framework.logger.log(
                    message=f"Error updating plugin '{plugin}': {e}",
                    log_type=LogType.ERROR,
                )

    elif subcommand == "update-all":
        try:
            pm.update_all_plugins()
            framework.logger.log(
                message="All installed plugins updated successfully.",
                log_type=LogType.INFO,
            )
        except Exception as e:
            framework.logger.log(
                message=f"Error updating all plugins: {e}",
                log_type=LogType.ERROR,
            )

    elif subcommand == "refresh":
        try:
            pm.refresh_available_plugins()
            framework.logger.log(
                message="Available plugins refreshed successfully.",
                log_type=LogType.INFO,
            )
        except Exception as e:
            framework.logger.log(
                message=f"Error refreshing available plugins: {e}",
                log_type=LogType.ERROR,
            )

    elif subcommand == "details":
        if not args:
            framework.logger.log(
                message="Usage: pgms details <plugin>",
                log_type=LogType.ERROR,
            )
            return
        plugin_name: str = args[0]
        try:
            details: str = pm.get_plugin_details(plugin_name)
            framework.logger.log(
                message=f"Details for plugin '{plugin_name}': {details}",
                log_type=LogType.DEBUG,
            )
        except Exception as e:
            framework.logger.log(
                message=f"Error retrieving details for plugin '{plugin_name}': {e}",
                log_type=LogType.ERROR,
            )

    elif subcommand == "load":
        if not args:
            framework.logger.log(
                message="Usage: pgms load <plugin>",
                log_type=LogType.ERROR,
            )
            return
        plugin_name: str = args[0]
        try:
            module = pm.load_plugin(plugin_name)
            framework.logger.log(
                message=f"Plugin '{plugin_name}' loaded successfully. Module: {module}",
                log_type=LogType.INFO,
            )
        except Exception as e:
            framework.logger.log(
                message=f"Error loading plugin '{plugin_name}': {e}",
                log_type=LogType.ERROR,
            )

    elif subcommand == "help":
        help_message: str = (
            "pgms command usage:\n"
            "  pgms install <plugin1> [plugin2 ...]    - Install one or more plugins.\n"
            "  pgms uninstall <plugin1> [plugin2 ...]  - Uninstall one or more plugins.\n"
            "  pgms list                             - List installed plugins.\n"
            "  pgms available                        - List available plugins from the remote repository.\n"
            "  pgms search <query>                   - Search available plugins by name.\n"
            "  pgms update <plugin1> [plugin2 ...]     - Update one or more installed plugins.\n"
            "  pgms update-all                       - Update all installed plugins.\n"
            "  pgms refresh                          - Refresh the available plugins list from the remote repository.\n"
            "  pgms details <plugin>                 - Show details of an installed plugin.\n"
            "  pgms load <plugin>                    - Dynamically load an installed plugin module.\n"
            "  pgms help                             - Show this help message."
        )
        framework.logger.log(
            message=help_message,
            log_type=LogType.INFO,
        )

    else:
        framework.logger.log(
            message=(
                "Unknown subcommand. Usage: pgms <subcommand> [arguments]\n"
                "Subcommands: install, uninstall, list, available, search, update, update-all, refresh, details, load, help"
            ),
            log_type=LogType.ERROR,
        )


def load(
    framework: client.ChitraGuptClientCLI,
    cmd_str: str,
) -> None:
    """
    Load all registered system commands into the CLI framework.

    Parameters
    ----------
    framework : ChitraGuptClientCLI
        The CLI framework instance into which commands are loaded.
    """
    try:
        for command in system_commands.commands:
            framework.logger.log(
                message=f"Loaded {system_commands.name} command: '{command.name}'",
                log_type=LogType.DEBUG,
            )
            framework.system_commands.append(command)  # type: ignore
        framework.logger.log(
            message="Pgms have been loaded and are working in context.",
            log_type=LogType.DEBUG,
        )
    except Exception as e:
        framework.logger.log(
            message=f"Error loading system commands: {e}",
            log_type=LogType.ERROR,
        )
