import datetime

from logilab.common.registry import objectify_predicate

from cubicweb import _
from cubicweb.predicates import is_instance
from cubicweb.server.hook import Hook, match_rtype, Operation, DataOperationMixIn
from cubicweb.sobjects.notification import (
    RecipientsFinder,
    NotificationView,
    StatusChangeMixIn,
)

from cubes.conference.utils import normalize_name


def get_unique_url_id(execute, conference):
    url_id = normalize_name(conference.title)
    suffix = 0
    new_url_id = url_id
    while True:
        rset = execute("Any C WHERE C url_id %(url)s", {"url": new_url_id})
        if not rset:
            return new_url_id
        suffix += 1
        new_url_id = "%s_%s" % (url_id, suffix)


class ConferenceUrlIdentifierHook(Hook):
    """
    If the conference has no url_id attribute, generate it automatically.
    """

    __regid__ = "conference-url-identifier-hook"
    __select__ = Hook.__select__ & is_instance("Conference")
    events = ("before_add_entity",)

    def __call__(self):
        if not self.entity.url_id:
            url_id = get_unique_url_id(self._cw.execute, self.entity)
            self.entity.cw_edited["url_id"] = url_id


class AddTalkLeadHook(Hook):
    __regid__ = "add-talk-lead-hook"
    __select__ = is_instance("Talk")
    events = ("after_add_entity",)

    def __call__(self):
        if not self._cw.is_internal_session:
            asession = self._cw
            asession.execute(
                "SET U leads X WHERE X eid %(x)s, U eid %(u)s, NOT U attend X",
                {"x": self.entity.eid, "u": self._cw.user.eid},
            )


class LeadsHook(Hook):
    """
    Create relation attend on CWUser who leads a Talk:
    U leads T => U attend T
    """

    __regid__ = "lead-entails-attend-hook"
    __select__ = Hook.__select__ & match_rtype("leads")
    events = ("after_add_relation",)

    def __call__(self):
        if not self._cw.is_internal_session:
            asession = self._cw
            asession.execute(
                "SET U attend X WHERE X eid %(x)s, U eid %(u)s, NOT U attend X",
                {"x": self.eidto, "u": self.eidfrom},
            )


class SyncInConfOp(DataOperationMixIn, Operation):
    def precommit_event(self):
        for talk, track in self.get_data():
            self.cnx.execute(
                "SET T in_conf C WHERE T eid %(t)s, T in_track K, K in_conf C, K eid %(k)s, "
                "NOT T in_conf C",
                {"t": talk, "k": track},
            )


class InTrackHook(Hook):
    """
    When a Talk is added to a Track, automatically add it to the Conference:
    T in_track TR => T in_conf C
    """

    __regid__ = "in_track-entails-in_conf-hook"
    __select__ = Hook.__select__ & match_rtype("in_track")
    events = ("before_add_relation",)

    def __call__(self):
        if not self._cw.is_internal_session:
            SyncInConfOp.get_instance(self._cw).add_data((self.eidfrom, self.eidto))


# -------------------
# Email notification
# -------------------


class TalkNotificationView(NotificationView):
    __select__ = NotificationView.__select__ & is_instance(
        "Talk",
    )

    def context(self, **kwargs):
        context = super(TalkNotificationView, self).context(**kwargs)
        entity = self.cw_rset.complete_entity(0, 0)
        context.update(
            {
                "talk_name": entity.title,
                "date": datetime.date.today().isoformat(),
                "firstname": entity.reverse_leads[0].firstname,
                "lastname": entity.reverse_leads[0].surname,
                "conference": entity.in_conf[0].title,
            }
        )
        return context

    def recipients(self):
        recipients = []
        for finderid in self.recipients_finder:
            finder = self._cw.vreg["components"].select(
                finderid, self._cw, rset=self.cw_rset
            )
            recipients += finder.recipients()
        return recipients


class TalkAddRecipientsFinder(RecipientsFinder):
    __select__ = RecipientsFinder.__select__ & is_instance(
        "Talk",
    )
    __regid__ = "manager_finder"

    @property
    def user_rql(self):
        return (
            'Any U, E, A WHERE U is CWUser, U in_group N, N name "managers", U primary_email E,'
            " E address A"
        )


class TalkUpdateRecipientsFinder(RecipientsFinder):
    __select__ = RecipientsFinder.__select__ & is_instance(
        "Talk",
    )
    __regid__ = "interested_by_finder"

    @property
    def user_rql(self):
        return (
            "Any U, E, A WHERE X eid %(eid)s, EXISTS(U reviews X) "
            "OR EXISTS(U leads X), U primary_email E, E address A"
            % {"eid": self.cw_rset[0][0]}
        )


class TalkAdd(TalkNotificationView):
    """Send an email to managers after a talk creation"""

    __regid__ = "notif_after_add_entity"

    recipients_finder = ("manager_finder",)

    content = _(
        u""
        u"Dear %(firstname)s %(lastname)s,\n"
        u"\n"
        u"The talk '%(talk_name)s' was added on %(date)s.\n"
        u"\n"
        u"Sincerely,\n"
        u"\n"
        u"--\n"
        u"The %(conference)s organizing committee.\n"
    )

    def subject(self):
        entity = self.cw_rset.get_entity(0, 0)
        return self._cw._("[%s] a talk was added") % entity.in_conf[0].title


# XXX move to framework ?
# XXX why metaattrsupdate is updated at before_update_entity ?
@objectify_predicate
def entity_really_edited(cls, req, rset=None, **kwargs):
    # when the status of an entity changes, the modification_date of the entity changes too
    # in case of talks, don't call talkupdate if only modification_date attribute has changed
    entity = rset.get_entity(0, 0)
    if getattr(entity, "cw_edited", None) is None:
        return 0
    if len(entity.cw_edited) == 1 and "modification_date" in entity.cw_edited:
        return 0
    return 1


class TalkUpdate(TalkNotificationView):
    __regid__ = "notif_after_update_entity"
    recipients_finder = (
        "manager_finder",
        "interested_by_finder",
    )
    __select__ = TalkNotificationView.__select__ & entity_really_edited()

    content = _(
        u""
        u"Dear %(firstname)s %(lastname)s,\n"
        u"\n"
        u"The talk '%(talk_name)s' was modified on %(date)s.\n"
        u"\n"
        u"Sincerely,\n"
        u"\n"
        u"--\n"
        u"The %(conference)s organizing committee.\n"
    )

    def subject(self):
        entity = self.cw_rset.get_entity(0, 0)
        return self._cw._("[%s] a talk was updated") % entity.in_conf[0].title


class TalkStatusUpdate(StatusChangeMixIn, TalkNotificationView):
    recipients_finder = ("manager_finder", "interested_by_finder")

    content = _(
        u""
        u"Dear user,\n\n"
        u"The '%(title)s' status was updated from '%(previous_state)s' to '%(current_state)s'.\n\n"
        u"Sincerely,\n"
        u"\n"
        u"--\n"
        u"The conference organizing committee.\n"
    )

    def subject(self):
        entity = self.cw_rset.get_entity(0, 0)
        return _(u"[%s] talk status was updated" % entity.in_track[0].in_conf[0].title)
