from django.apps import apps
from django.db.models.signals import post_save
from django.dispatch import Signal, receiver
from notifications.signals import notify
import logging

logger = logging.getLogger(__name__)

# Signal sent during method Annotation.validate_existing_revision to update stats
revision_accepted = Signal(providing_args=['instance'])
revision_rejected = Signal(providing_args=['instance'])
revision_created = Signal(providing_args=['instance'])


def increment_stats_on_new_revision(sender, instance, **kwargs):
    from iconolab.models import AnnotationRevision
    if sender == AnnotationRevision:
        if instance.parent_revision:
            # Annotation stats
            annotation = instance.annotation
            annotation.stats.submitted_revisions_count += 1
            if instance.state in [AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]:
                annotation.stats.accepted_revisions_count += 1
            if instance.state == AnnotationRevision.ACCEPTED and instance.merge_parent_revision is not None and instance.merge_parent_revision.state == AnnotationRevision.STUDIED:
                annotation.stats.awaiting_revisions_count -= 1
            if instance.state in [AnnotationRevision.AWAITING]:
                annotation.stats.awaiting_revisions_count += 1
            annotation.stats.set_tags_stats()
            annotation.stats.save()
            # Image stats
            image = instance.annotation.image
            image.stats.submitted_revisions_count += 1
            image.stats.set_tags_stats()
            image.stats.save()  
    
def increment_stats_on_new_comment(sender, instance, created, **kwargs):
    from iconolab.models import IconolabComment
    if created and sender == IconolabComment:
        model = apps.get_model(instance.content_type.app_label,instance.content_type.model)
        object_pk = instance.object_pk
        annotation = model.objects.get(pk=object_pk)
        annotation.stats.comments_count +=1
        annotation.stats.save()
        annotation.image.stats.comments_count +=1
        annotation.image.stats.save()

def increment_stats_on_new_metacategory(sender, instance, created, **kwargs):
    from iconolab.models import MetaCategoryInfo, MetaCategoriesCountInfo
    if created and sender == MetaCategoryInfo:
        metacategory = instance.metacategory
        comment = instance.comment
        annotation = comment.annotation
        if metacategory not in annotation.stats.metacategories.all():
            MetaCategoriesCountInfo.objects.create(annotation_stats_obj=annotation.stats, metacategory=metacategory, count=1)
        else:
            m2m_object = MetaCategoriesCountInfo.objects.get(annotation_stats_obj=annotation.stats, metacategory=metacategory)
            m2m_object.count += 1
            m2m_object.save()
        logger.debug("NEW METACATEGORY %r on comment %r on annotation %r", metacategory, comment, annotation)

def increment_stats_on_accepted_revision(sender, instance, **kwargs):
    from iconolab.models import AnnotationRevision
    if sender == AnnotationRevision:
        annotation = instance.annotation
        annotation.stats.accepted_revisions_count += 1
        annotation.stats.awaiting_revisions_count -= 1
        annotation.stats.save()

def increment_stats_on_rejected_revision(sender, instance, **kwargs):
    from iconolab.models import AnnotationRevision
    if sender == AnnotationRevision:
        annotation = instance.annotation
        annotation.stats.awaiting_revisions_count -= 1
        annotation.stats.save()

def increment_annotations_count(sender, instance, created, **kwargs):
    from iconolab.models import Annotation
    if created and sender == Annotation:
        image = instance.image
        image.stats.annotations_count += 1
        image.stats.submitted_revisions_count += 1
        image.stats.set_tags_stats()
        image.stats.save()


def notify_users_on_new_comment(sender, instance, **kwargs):
    from iconolab.models import IconolabComment, Annotation, MetaCategory, MetaCategoryInfo
    if sender == IconolabComment and instance.content_type.app_label == 'iconolab' and instance.content_type.model == 'annotation':
        comment_annotation = Annotation.objects.get(id=instance.object_pk)
        # Notifying new user comment
        if instance.thread_id:
            notified_author = False
            if instance.level > 0: # We check parent_id as django comment xtd saves comments in two steps and only set the information we need in the second step
                parent_comment = IconolabComment.objects.get(id=instance.parent_id)
                if parent_comment.user != instance.user:
                    notify.send(instance.user, recipient=parent_comment.user, verb='a répondu à votre commentaire', action_object=instance, target=comment_annotation)
                    if parent_comment.user == comment_annotation.author:
                        notified_author = True
            if instance.user != comment_annotation.author and not notified_author:
                notify.send(instance.user, recipient=comment_annotation.author, verb='a écrit un commentaire sur votre annotation', action_object=instance, target=comment_annotation)         

def notify_users_on_metacategory(sender, instance, created, **kwargs):
    from iconolab.models import MetaCategory, MetaCategoryInfo, Annotation, UserProfile
    if sender == MetaCategoryInfo and created:
        related_metacategory = instance.metacategory
        related_comment = instance.comment
        if related_comment.content_type.app_label == "iconolab" and related_comment.content_type.model == "annotation":
            comment_annotation = Annotation.objects.prefetch_related("image__item__collection").get(id=related_comment.object_pk)
            if related_metacategory.triggers_notifications == MetaCategory.COMMENTERS:
                for commenter in comment_annotation.stats.commenters.exclude(id=related_comment.user.id).all():
                    notify.send(related_comment.user, recipient=commenter, verb='a fait un appel à contribution', action_object=related_comment, target=comment_annotation)
            elif related_metacategory.triggers_notifications == MetaCategory.CONTRIBUTORS:
                for contributor in comment_annotation.stats.contributors.exclude(id=related_comment.user.id).all():
                    notify.send(related_comment.user, recipient=contributor, verb='a fait un appel à contribution', action_object=related_comment, target=comment_annotation)
            if related_metacategory.triggers_notifications == MetaCategory.COLLECTION_ADMINS:
                for collection_admin in comment_annotation.image.item.collection.admins.all():
                    notify.send(related_comment.user, recipient=collection_admin.user, verb='a fait un appel à expertise', action_object=related_comment, target=comment_annotation)
        
def notify_users_on_new_revision(sender, instance, **kwargs):
    from iconolab.models import AnnotationRevision
    if sender == AnnotationRevision:
        if instance.author != instance.annotation.author:
            notify.send(instance.author, recipient=instance.annotation.author, verb='a proposé une révision sur votre annotation', action_object=instance, target=instance.annotation)       
        
def notify_users_on_accepted_revision(sender, instance, **kwargs):
    from iconolab.models import AnnotationRevision
    if sender == AnnotationRevision:
        if instance.author != instance.annotation.author and instance.state in [AnnotationRevision.ACCEPTED, AnnotationRevision.STUDIED]:
            notify.send(instance.annotation.author, recipient=instance.author, verb='a étudié votre révision', action_object=instance, target=instance.annotation)       

def create_user_profile(sender, instance, created, **kwargs):
    from iconolab.models import UserProfile
    from django.contrib.auth.models import User
    if sender == User and created:
        UserProfile.objects.create(user=instance)

# User profile connect
post_save.connect(create_user_profile)

# Stats handlers connect
post_save.connect(increment_annotations_count)
post_save.connect(increment_stats_on_new_comment)
post_save.connect(increment_stats_on_new_metacategory)
revision_created.connect(increment_stats_on_new_revision)
revision_accepted.connect(increment_stats_on_accepted_revision)
revision_rejected.connect(increment_stats_on_rejected_revision)
# Notifications handlers connect
post_save.connect(notify_users_on_new_comment)
post_save.connect(notify_users_on_metacategory)
revision_created.connect(notify_users_on_new_revision)
revision_accepted.connect(notify_users_on_accepted_revision)
        