"""
Define classes for wrapping different package managers
"""
import os
import subprocess

import distro

# Define dictionary matching linux distros to their package manager
DISTROS_PACKAGE_MANAGERS = {
    "manjaro linux": "pacman",
    "arch": "pacman",
    "ubuntu": "apt",
    "debian": "apt",
}

# Define list of supported package managers
PACKAGE_MANAGERS = {
    "apt": dict(
        package_manager="apt",
        update_option="update",
        upgrade_option="upgrade",
        install_option="install",
        needs_sudo=True,
    ),
    "pacman": dict(
        package_manager="pacman",
        update_option="-Sy",
        upgrade_option="-Su",
        install_option="-S",
        needs_sudo=True,
    ),
    "yay": dict(
        package_manager="yay",
        update_option="-Sy",
        upgrade_option="-Su",
        install_option="-S",
        needs_sudo=False,
    ),
}


def get_package_manager(package_manager=None):
    """
    Get the package manager class that will manage the installations

    Parameters
    ----------
    package_manager : str (optional)
        Package manager name. If None, the better suited package manager will
        be chosen based on the distro.

    Returns
    -------
    package_manager_obj : instance of :class:`PackageManager`
        Package manager instance
    """
    # Guess package manager if None
    if package_manager is None:
        package_manager = guess_package_manager()
    # Raise error if invalid package manager
    if package_manager not in PACKAGE_MANAGERS:
        raise ValueError("Invalid package manager '{}'".format(package_manager))
    # Initialize a PackageManager object and return it
    return PackageManager(**PACKAGE_MANAGERS[package_manager])


def guess_package_manager():
    """
    Guess which package manager class is suited for your Linux distribution

    Return
    ------
    package_manager : str
        Name of the package manager
    """
    distribution = distro.linux_distribution()[0]
    distribution = distribution.lower()
    if distribution not in DISTROS_PACKAGE_MANAGERS:
        raise ValueError(
            "Couldn't choose a package manager for your distro. "
            + "This may be caused because your distro is not supported or "
            + "hornero cannot identify it properly. "
            + "Try specifying the package manager you want to use through the "
            + "--package-manager option."
        )
    return DISTROS_PACKAGE_MANAGERS[distribution]


class PackageManager(object):
    """
    Class that wraps package managers

    Parameters
    ----------
    package_manager : str
        Package manager name (the name of the command to run it).
    update_option : str
        Option to pass to the package manager to update the list of packages
        from the repositories.
    upgrade_option : str
        Option to pass to the package manager to upgrade the installed
        packages.
    install_option : str
        Option to pass to the package manager to install the packages that will
        be passed as arguments.
    needs_sudo : bool
        If True, the package manager will be run with super user privileges.
        Default to True.
    """

    def __init__(
        self,
        package_manager,
        update_option,
        upgrade_option,
        install_option,
        needs_sudo=True,
    ):
        self.needs_sudo = needs_sudo
        self.package_manager = package_manager
        self.update_option = update_option
        self.upgrade_option = upgrade_option
        self.install_option = install_option

    @property
    def sudo(self):
        """
        Check if we have sudo privileges
        """
        return os.getuid() != 0

    def get_update_command(self):
        """
        Return command for updating package lists
        """
        return self._build_command(self.update_option)

    def get_upgrade_command(self):
        """
        Return command for upgrading installed package
        """
        return self._build_command(self.upgrade_option)

    def get_install_command(self, packages):
        """
        Return command for installing a set of package
        """
        return self._build_command(self.install_option, packages=packages)

    def run(self, command):
        """
        Run the given command
        """
        subprocess.run(command, shell=True)

    def _build_command(self, options, packages=None):
        """
        Return a command line to run the package manager from the shell

        Concatenates the package manager command with the options and the given
        packages in a single string. Adds a heading "sudo" if needed.

        Parameters
        ----------
        options : str or None
            Options for the package manager. If None, no option is appended.
        packages : list or None (optional)
            List of arguments for the command as strings. If None, no argument
            is appended.

        Returns
        -------
        full_line : str
            Concatenation of the package manager, its options and the list of
            packages, with a heading "sudo" if needed.
        """
        command_line = []

        # Append sudo if needed
        if self.needs_sudo and self.sudo:
            command_line.append("sudo")

        # Append the package manager
        command_line.append(self.package_manager)

        # Append the options
        if options is not None:
            command_line.append(options)

        # Add the packages
        if packages is not None:
            command_line += packages

        # Return the command line as a single string
        return " ".join(command_line)

"""
Define classes for wrapping different package managers
"""
import os
import subprocess

import distro

# Define dictionary matching linux distros to their package manager
DISTROS_PACKAGE_MANAGERS = {
    "manjaro linux": "pacman",
    "arch": "pacman",
    "ubuntu": "apt",
    "debian": "apt",
}

# Define list of supported package managers
PACKAGE_MANAGERS = {
    "apt": dict(
        package_manager="apt",
        update_option="update",
        upgrade_option="upgrade",
        install_option="install",
        needs_sudo=True,
    ),
    "pacman": dict(
        package_manager="pacman",
        update_option="-Sy",
        upgrade_option="-Su",
        install_option="-S",
        needs_sudo=True,
    ),
    "yay": dict(
        package_manager="yay",
        update_option="-Sy",
        upgrade_option="-Su",
        install_option="-S",
        needs_sudo=False,
    ),
}


def get_package_manager(package_manager=None):
    """
    Get the package manager class that will manage the installations

    Parameters
    ----------
    package_manager : str (optional)
        Package manager name. If None, the better suited package manager will
        be chosen based on the distro.

    Returns
    -------
    package_manager_obj : instance of :class:`PackageManager`
        Package manager instance
    """
    # Guess package manager if None
    if package_manager is None:
        package_manager = guess_package_manager()
    # Raise error if invalid package manager
    if package_manager not in PACKAGE_MANAGERS:
        raise ValueError("Invalid package manager '{}'".format(package_manager))
    # Initialize a PackageManager object and return it
    return PackageManager(**PACKAGE_MANAGERS[package_manager])


def guess_package_manager():
    """
    Guess which package manager class is suited for your Linux distribution

    Return
    ------
    package_manager : str
        Name of the package manager
    """
    distribution = distro.linux_distribution()[0]
    distribution = distribution.lower()
    if distribution not in DISTROS_PACKAGE_MANAGERS:
        raise ValueError(
            "Couldn't choose a package manager for your distro. "
            + "This may be caused because your distro is not supported or "
            + "hornero cannot identify it properly. "
            + "Try specifying the package manager you want to use through the "
            + "--package-manager option."
        )
    return DISTROS_PACKAGE_MANAGERS[distribution]


class PackageManager(object):
    """
    Class that wraps package managers

    Parameters
    ----------
    package_manager : str
        Package manager name (the name of the command to run it).
    update_option : str
        Option to pass to the package manager to update the list of packages
        from the repositories.
    upgrade_option : str
        Option to pass to the package manager to upgrade the installed
        packages.
    install_option : str
        Option to pass to the package manager to install the packages that will
        be passed as arguments.
    needs_sudo : bool
        If True, the package manager will be run with super user privileges.
        Default to True.
    """

    def __init__(
        self,
        package_manager,
        update_option,
        upgrade_option,
        install_option,
        needs_sudo=True,
    ):
        self.needs_sudo = needs_sudo
        self.package_manager = package_manager
        self.update_option = update_option
        self.upgrade_option = upgrade_option
        self.install_option = install_option

    @property
    def sudo(self):
        """
        Check if we have sudo privileges
        """
        return os.getuid() != 0

    def get_update_command(self):
        """
        Return command for updating package lists
        """
        return self._build_command(self.update_option)

    def get_upgrade_command(self):
        """
        Return command for upgrading installed package
        """
        return self._build_command(self.upgrade_option)

    def get_install_command(self, packages):
        """
        Return command for installing a set of package
        """
        return self._build_command(self.install_option, packages=packages)

    def run(self, command):
        """
        Run the given command
        """
        subprocess.run(command, shell=True)

    def _build_command(self, options, packages=None):
        """
        Return a command line to run the package manager from the shell

        Concatenates the package manager command with the options and the given
        packages in a single string. Adds a heading "sudo" if needed.

        Parameters
        ----------
        options : str or None
            Options for the package manager. If None, no option is appended.
        packages : list or None (optional)
            List of arguments for the command as strings. If None, no argument
            is appended.

        Returns
        -------
        full_line : str
            Concatenation of the package manager, its options and the list of
            packages, with a heading "sudo" if needed.
        """
        command_line = []

        # Append sudo if needed
        if self.needs_sudo and self.sudo:
            command_line.append("sudo")

        # Append the package manager
        command_line.append(self.package_manager)

        # Append the options
        if options is not None:
            command_line.append(options)

        # Add the packages
        if packages is not None:
            command_line += packages

        # Return the command line as a single string
        return " ".join(command_line)

Define classes for wrapping different package managers # Define dictionary matching linux distros to their package manager # Define list of supported package managers Get the package manager class that will manage the installations      Parameters     ----------     package_manager : str (optional)         Package manager name. If None, the better suited package manager will         be chosen based on the distro.      Returns     -------     package_manager_obj : instance of :class:`PackageManager`         Package manager instance # Guess package manager if None # Raise error if invalid package manager # Initialize a PackageManager object and return it Guess which package manager class is suited for your Linux distribution      Return     ------     package_manager : str         Name of the package manager Class that wraps package managers      Parameters     ----------     package_manager : str         Package manager name (the name of the command to run it).     update_option : str         Option to pass to the package manager to update the list of packages         from the repositories.     upgrade_option : str         Option to pass to the package manager to upgrade the installed         packages.     install_option : str         Option to pass to the package manager to install the packages that will         be passed as arguments.     needs_sudo : bool         If True, the package manager will be run with super user privileges.         Default to True. Check if we have sudo privileges Return command for updating package lists Return command for upgrading installed package Return command for installing a set of package Run the given command Return a command line to run the package manager from the shell          Concatenates the package manager command with the options and the given         packages in a single string. Adds a heading "sudo" if needed.          Parameters         ----------         options : str or None             Options for the package manager. If None, no option is appended.         packages : list or None (optional)             List of arguments for the command as strings. If None, no argument             is appended.          Returns         -------         full_line : str             Concatenation of the package manager, its options and the list of             packages, with a heading "sudo" if needed. # Append sudo if needed # Append the package manager # Append the options # Add the packages # Return the command line as a single string