import jsonpickle
from keven_core.kafka.events.event import Event, EventNames


class CommandEvent(Event):
    """
    class CommandEvent is employed to publish the occurrence of 3 phases of a
    Command's execution. They are: STARTED, COMPLETED, and ERRORED. These
    CommandEvents are automatically generated by the CommandHandler when:
    B{STARTED}: just prior to calling the Command's handle_command()
    B{COMPLETED}: just after handle_command() successfully completes
    B{ERRORED}: if and only if handle_command() throws an exception

    It is important to note, however, that some Developers may code or have
    coded the publication of the these Events directly in code. The assumption
    is that such CommandEvents use the naming pattern:
    CMD_{command_name}_START
    CMD_{command_name}_COMPLETE
    CMD_{command_name}_ERROR

    If these Events are found, then CommandEvents are not automatically
    published, assuming that they are published via directly coded processing.

    Automated CommandEvents will populated the attribute B{compute_time} in
    number of microseconds, otherwise this will likely not be set.
    """

    event_name = EventNames.COMMAND_EVENT
    attributes = []

    def __init__(self):
        super().__init__()
        self.source_command = None
        self.compute_time = None
        for attribute in self.__class__.attributes:
            if not hasattr(self, attribute):
                setattr(self, attribute, None)

    @property
    def source_command(self):
        return self.__source_command

    @source_command.setter
    def source_command(self, source_command):
        self.__source_command = source_command
        if source_command:
            self._source_command_uuid = source_command._uuid
        else:
            self._source_command_uuid = None

    def serialize_payload(self) -> bytes:
        to_pickle = {
            "source_command": self.source_command.serialize().SerializeToString()  # noqa
            if self.source_command is not None
            else None,
            "compute_time": str.encode(str(self.compute_time)),
        }
        for attribute in self.__class__.attributes:
            to_pickle[attribute] = getattr(self, attribute)

        return jsonpickle.encode(to_pickle).encode()

    def parse_payload(self, payload: bytes):
        from keven_core.kafka.commands.command import Command

        tmp = jsonpickle.decode(payload.decode())
        self.source_command = Command.from_bytes(tmp["source_command"])

        for attribute in self.__class__.attributes:
            setattr(self, attribute, tmp[attribute])


class CommandErrorEvent(CommandEvent):
    """
    aliased CommandErrorEvent to be CommandEvent with command_phase =
    CommandPhase.ERRORED. However, it is not fully conformant with
    CommandEvent pattern as would need to have event_name = COMMAND_EVENT
    """

    event_name = EventNames.COMMAND_ERROR_EVENT
    attributes = ["error_message", "traceback"]
