import uuid

from eventsourcing.domain.model.decorators import attribute, mutator
from eventsourcing.domain.model.entity import (AbstractEntityRepository,
                                               TimestampedVersionedEntity,
                                               mutate_entity)
from eventsourcing.domain.model.events import publish


class Region(TimestampedVersionedEntity):

    class Event(TimestampedVersionedEntity.Event):
        """Layer supertype."""

    class Created(Event, TimestampedVersionedEntity.Created):
        """Published when an Example is created."""

    class AttributeChanged(Event, TimestampedVersionedEntity.AttributeChanged):
        """Published when an Example is created."""

    class Discarded(Event, TimestampedVersionedEntity.Discarded):
        """Published when an Example is discarded."""

    class Heartbeat(Event, TimestampedVersionedEntity.Event):
        """Published when a heartbeat in the entity occurs (see below)."""

    def __init__(self, abbreviation='', name='', **kwargs):
        super(Region, self).__init__(**kwargs)
        self._abbreviation = abbreviation
        self._name = name
        self._count_heartbeats = 0

    @attribute
    def abbreviation(self):
        """An example attribute."""

    @attribute
    def name(self):
        """An example attribute."""

    def beat_heart(self, number_of_beats=1):
        self._assert_not_discarded()
        while number_of_beats > 0:
            event = self.Heartbeat(originator_id=self._id, originator_version=self._version)
            self._apply_and_publish(event)
            number_of_beats -= 1

    def count_heartbeats(self):
        return self._count_heartbeats

    @classmethod
    def _mutate(cls, initial, event):
        return example_mutator(initial or cls, event)


@mutator
def example_mutator(initial, event):
    return mutate_entity(initial, event)


@example_mutator.register(Region.Heartbeat)
def heartbeat_mutator(self, event):
    self._validate_originator(event)
    assert isinstance(self, Region), self
    self._count_heartbeats += 1
    self._increment_version()
    return self


class AbstractRegionRepository(AbstractEntityRepository):
    pass


def create_new_region(abbreviation='', name=''):
    """
    Factory method for example entities.
    :rtype: Example
    """
    entity_id = uuid.uuid4()
    event = Region.Created(originator_id=entity_id, abbreviation=abbreviation, name=name)
    entity = Region._mutate(initial=None, event=event)
    publish(event=event)
    return entity
