class WorkerInstance:
    def __init__(self, handler: MessageHandler):
        self.__handler = handler
        self.__message_queue = List("message_queue")

        # status sets
        self.__message_in_queue_status = Set(message_in_queue_status)
        self.__messages_processing_status = Set(message_processing_status)
        self.__messages_send_status = Set(message_send_status)
        self.__messages_delivered_status = Set(message_delivered_status)
        self.__messages_blocked_status = Set(message_blocked_status)

        self.__journal = PubSub("activity_journal")
        self.__sent_message_journal_prefix = "sent_message:"

        self.__active_users = ZSet("most_active_users")
        self.__spamers = ZSet("spamers")

        self.__incoming_message_prefix = "incoming_message:"

    def run(self):
        while True:
            message_id = self.__message_queue.remove_blocking()
            self.__message_in_queue_status.move_to("message_processing_status",
                                                   message_id)
            message = Hash(message_id)
            sender, message_body, receiver = WorkerInstance.__get_message_data(
                message)

            if self.__handler.is_message_valid(message_body):
                self.__messages_processing_status.move_to(
                    "message_send_status", message_id)
                List(self.__incoming_message_prefix + receiver).add(message_id)
                PubSub(self.__sent_message_journal_prefix +
                       receiver).publish(receiver)
                self.__messages_send_status.move_to("message_delivered_status",
                                                    message_id)
                self.__active_users.add(sender, 1)
            else:
                self.__messages_processing_status.move_to(
                    "message_blocked_status", message_id)
                self.__spamers.add(sender, 1)
                self.__journal.publish(
                    "Client `%s` tried to send SPAM `%s` to user `%s`" %
                    (sender, message_body, receiver))

    @staticmethod
    def __get_message_data(message: Hash):
        sender = message.get('from')
        message_body = message.get('body')
        receiver = message.get('to')
        return sender, message_body, receiver
class Client:
    def __init__(self):
        self.__users = Set("users")
        self.__admins = Set("admins")
        self.__online_users = Set("online_users")
        self.__journal = PubSub("activity_journal")
        self.__active_users = ZSet("most_active_users")
        self.__spamers = ZSet("spamers")

    def is_admin(self, username: str):
        return self.__admins.contains(username)

    def is_user(self, username: str):
        return self.__users.contains(username)

    def is_registered(self, username: str):
        return self.is_admin(username) or self.is_user(username)

    def register(self, username: str, is_admin=False):
        set_to_save: Set = self.__admins if is_admin else self.__users
        if not self.is_registered(username):
            set_to_save.add(username)
        else:
            raise Exception(
                "Client with username '%s' has already registered" % username)

    def login(self, username: str):
        if not self.is_registered(username):
            return None

        self.__online_users.add(username)
        self.__journal.publish("Client `%s` login in chat" % username)
        return True

    def logout(self, username: str):
        self.__online_users.remove(username)
        self.__journal.publish("Client `%s` logout in chat" % username)

    def get_all_users(self):
        return self.__users.get_all()

    def get_all_online_users(self):
        return self.__online_users.get_all()

    def get_spamers(self, n: int):
        return self.__spamers.get_all_descending(0, n - 1)

    def get_active_users(self, n: int):
        return self.__active_users.get_all_descending(0, n - 1)