from model import (
    EventPublishDetail,
    NewEventTopic,
    AsyncTarget,
)
from model.repository import EventRepository
from opentelemetry import trace
from opentelemetry.trace import Span, SpanKind
from timeutil import Clock
from .dto import (
    PublishEventRequest,
    CannotProcessSyncEvent,
)

tracer = trace.get_tracer(__name__)


class PublishEventInteractor(object):
    def __init__(
        self,
        event_repository: EventRepository,
        new_event_topic: NewEventTopic,
        clock: Clock,
    ) -> None:

        self.event_repository = event_repository
        self.new_event_topic = new_event_topic
        self.clock = clock

    def publish_event(self, request: PublishEventRequest):
        if request.event.synchronous:
            raise CannotProcessSyncEvent()

        if request.external_code is not None:
            publish_detail = self.event_repository.get_by_external_code(external_code=request.external_code)
            if publish_detail is not None:
                return publish_detail

        with tracer.start_as_current_span(
            name="Publish",
            kind=SpanKind.SERVER,
        ) as span:  # type: Span
            if request.async_target is None:
                request.event.synchronous = True

            async_target = None
            if request.target is None and request.async_target is not None:
                async_target = AsyncTarget(
                    reply_subject=request.async_target.reply_subject,
                    id_field=request.async_target.id_field,
                )

            span.set_attribute(key="subject", value=request.event.subject)
            if request.target is not None:
                span.set_attribute(key="type", value="TargetedEvent")
                span.set_attribute(key="target", value=request.target)
            elif async_target is not None:
                span.set_attribute(key="type", value="AsyncTargetEvent")
                span.set_attribute(key="replySubject", value=async_target.reply_subject)
            else:
                span.set_attribute(key="type", value="SyncEvent")

            now = self.clock.utc_now()
            publish_detail = EventPublishDetail(
                id=None,
                created_at=now,
                external_code=request.external_code,
                event=request.event,
                target=request.target,
                async_target=async_target,
                next_retry_at=now,
                fifo=request.fifo,
            )
            span.set_attribute("definitionId", str(publish_detail.id))
            self.new_event_topic.notify(publish_detail=publish_detail)
            return publish_detail
