from typing import Optional

from django.db.models import Q
from .messages import message_thread_factory
from .transports import transport_factory


class Notification(object):
    template: Optional[str] = None

    def __init__(
        self,
        target,
        context,
        department=None,
        office=None,
        staff=None,
        template=None,
    ):
        super(Notification, self).__init__()
        self.target = target
        self.context = context
        self.department = department
        self.office = office
        self.staff = staff
        if template is not None:
            self.template = template

    def get_route(self):
        from staff.django_intranet_notifications.models import Route

        q = Q(target=self.target) | Q(target=None)

        if self.department is not None:
            q &= Q(
                department__lft__lte=self.department.lft,
                department__rght__gte=self.department.rght,
                department__tree_id=self.department.tree_id,
            ) | Q(department=None)

        if self.office is not None:
            q &= Q(office=self.office) | Q(office=None)
        if self.staff is not None:
            q &= Q(staff=self.staff) | Q(staff=None)

        route = (
            Route.objects
            .filter(q)
            .order_by('target', 'staff', 'office', '-department__lft')
        )[0]
        return route

    def get_transport(self, route):
        thread = self.get_thread(route)
        transport = transport_factory(route.transport_id)(
            department=self.department,
            office=self.office,
            staff=self.staff,
            route_params=route.params,
            thread=thread
        )
        return transport

    def get_thread(self, route):
        return message_thread_factory(route.transport_id)().get_or_create(
            subj_id=self.get_subj_id(), target=self.target
        )

    def send(self, **params):
        route = self.get_route()
        transport = self.get_transport(route)
        transport.prepare_message(context=self.context, template=self.template)
        return transport.deliver_message(**params)

    def get_subj_id(self):
        raise NotImplementedError
