import logging

from landscape.client import IS_CORE
from landscape.client.amp import ComponentConnector
from landscape.client.amp import ComponentPublisher
from landscape.client.amp import remote
from landscape.client.manager.plugin import ManagerPlugin
from landscape.client.monitor.usermonitor import RemoteUserMonitorConnector
from landscape.client.user.management import SnapdUserManagement
from landscape.client.user.management import UserManagement


class UserManager(ManagerPlugin):

    name = "usermanager"

    def __init__(self, management=None, shadow_file=None):
        if IS_CORE:
            management = management or SnapdUserManagement()
            shadow_file = shadow_file or "/var/lib/extrausers/shadow"
        else:
            management = management or UserManagement()
            shadow_file = shadow_file or "/etc/shadow"

        self._management = management
        self._shadow_file = shadow_file
        self._message_types = {
            "add-user": self._add_user,
            "edit-user": self._edit_user,
            "lock-user": self._lock_user,
            "unlock-user": self._unlock_user,
            "remove-user": self._remove_user,
            "add-group": self._add_group,
            "edit-group": self._edit_group,
            "remove-group": self._remove_group,
            "add-group-member": self._add_group_member,
            "remove-group-member": self._remove_group_member,
        }
        self._publisher = None

    def register(self, registry):
        """
        Schedule reactor events for generic L{Plugin} callbacks, user
        and group management operations, and resynchronization.
        """
        super().register(registry)
        self._registry = registry

        self._publisher = ComponentPublisher(
            self,
            self.registry.reactor,
            self.registry.config,
        )
        self._publisher.start()

        for message_type in self._message_types:
            self._registry.register_message(
                message_type,
                self._message_dispatch,
            )

    def stop(self):
        """Stop listening for incoming AMP connections."""
        if self._publisher:
            self._publisher.stop()
            self._publisher = None

    @remote
    def get_locked_usernames(self):
        """Return a list of usernames with locked system accounts."""
        locked_users = []
        if self._shadow_file:
            try:
                shadow_file = open(self._shadow_file, "r")
                for line in shadow_file:
                    parts = line.split(":")
                    if len(parts) > 1:
                        if parts[1].startswith("!"):
                            locked_users.append(parts[0].strip())
            except OSError as e:
                logging.error(f"Error reading shadow file. {e}")
        return locked_users

    def _message_dispatch(self, message):
        """Dispatch the given user-change request to the correct handler.

        @param message: The request we got from the server.
        """
        user_monitor_connector = RemoteUserMonitorConnector(
            self.registry.reactor,
            self.registry.config,
        )

        def detect_changes(user_monitor):
            self._user_monitor = user_monitor
            return user_monitor.detect_changes()

        result = user_monitor_connector.connect()
        result.addCallback(detect_changes)
        result.addCallback(self._perform_operation, message)
        result.addCallback(self._send_changes, message)
        result.addCallback(lambda x: user_monitor_connector.disconnect())
        return result

    def _perform_operation(self, result, message):
        message_type = message["type"]
        message_method = self._message_types[message_type]
        return self.call_with_operation_result(
            message,
            message_method,
            message,
        )

    def _send_changes(self, result, message):
        return self._user_monitor.detect_changes(message["operation-id"])

    def _add_user(self, message):
        """Run an C{add-user} operation."""
        return self._management.add_user(message)

    def _edit_user(self, message):
        """Run an C{edit-user} operation."""
        return self._management.set_user_details(
            message["username"],
            password=message["password"],
            name=message["name"],
            location=message["location"],
            work_number=message["work-number"],
            home_number=message["home-number"],
            primary_group_name=message["primary-group-name"],
        )

    def _lock_user(self, message):
        """Run a C{lock-user} operation."""
        return self._management.lock_user(message["username"])

    def _unlock_user(self, message):
        """Run an C{unlock-user} operation."""
        return self._management.unlock_user(message["username"])

    def _remove_user(self, message):
        """Run a C{remove-user} operation."""
        return self._management.remove_user(message)

    def _add_group(self, message):
        """Run an C{add-group} operation."""
        return self._management.add_group(message["groupname"])

    def _edit_group(self, message):
        """Run an C{edit-group} operation."""
        return self._management.set_group_details(
            message["groupname"],
            message["new-name"],
        )

    def _add_group_member(self, message):
        """Run an C{add-group-member} operation."""
        return self._management.add_group_member(
            message["username"],
            message["groupname"],
        )

    def _remove_group_member(self, message):
        """Run a C{remove-group-member} operation."""
        return self._management.remove_group_member(
            message["username"],
            message["groupname"],
        )

    def _remove_group(self, message):
        """Run an C{remove-group} operation."""
        return self._management.remove_group(message["groupname"])


class RemoteUserManagerConnector(ComponentConnector):

    component = UserManager
