import logging

from django.contrib.contenttypes.models import ContentType
from django.db.models import CASCADE
from django.db.models.signals import post_save, post_delete

from river.core.riverobject import RiverObject
from river.core.workflowregistry import workflow_registry
from river.models import OnApprovedHook, OnTransitHook, OnCompleteHook

try:
    from django.contrib.contenttypes.fields import GenericRelation
except ImportError:
    from django.contrib.contenttypes.generic import GenericRelation

from river.models.state import State
from river.models.transitionapproval import TransitionApproval
from river.models.transition import Transition

from django.db import models

LOGGER = logging.getLogger(__name__)


class classproperty(object):
    def __init__(self, getter):
        self.getter = getter

    def __get__(self, instance, owner):
        return self.getter(instance) if instance else self.getter(owner)


class StateField(models.ForeignKey):
    def __init__(self, *args, **kwargs):
        self.field_name = None
        kwargs['null'] = True
        kwargs['blank'] = True
        kwargs['to'] = '%s.%s' % (State._meta.app_label, State._meta.object_name)
        kwargs['on_delete'] = kwargs.get('on_delete', CASCADE)
        kwargs['related_name'] = "+"
        super(StateField, self).__init__(*args, **kwargs)

    def contribute_to_class(self, cls, name, *args, **kwargs):
        @classproperty
        def river(_self):
            return RiverObject(_self)

        self.field_name = name

        self._add_to_class(cls, self.field_name + "_transition_approvals",
                           GenericRelation('%s.%s' % (TransitionApproval._meta.app_label, TransitionApproval._meta.object_name)))
        self._add_to_class(cls, self.field_name + "_transitions", GenericRelation('%s.%s' % (Transition._meta.app_label, Transition._meta.object_name)))

        if id(cls) not in workflow_registry.workflows:
            self._add_to_class(cls, "river", river)

        super(StateField, self).contribute_to_class(cls, name, *args, **kwargs)

        if id(cls) not in workflow_registry.workflows:
            post_save.connect(_on_workflow_object_saved, self.model, False, dispatch_uid='%s_%s_riverstatefield_post' % (self.model, name))
            post_delete.connect(_on_workflow_object_deleted, self.model, False, dispatch_uid='%s_%s_riverstatefield_post' % (self.model, name))

        workflow_registry.add(self.field_name, cls)

    @staticmethod
    def _add_to_class(cls, key, value, ignore_exists=False):
        if ignore_exists or not hasattr(cls, key):
            cls.add_to_class(key, value)


def _on_workflow_object_saved(sender, instance, created, *args, **kwargs):
    for instance_workflow in instance.river.all(instance.__class__):
        if created:
            instance_workflow.initialize_approvals()
            if not instance_workflow.get_state():
                init_state = getattr(instance.__class__.river, instance_workflow.field_name).initial_state
                instance_workflow.set_state(init_state)
                instance.save()


def _on_workflow_object_deleted(sender, instance, *args, **kwargs):
    OnApprovedHook.objects.filter(object_id=instance.pk, content_type=ContentType.objects.get_for_model(instance.__class__)).delete()
    OnTransitHook.objects.filter(object_id=instance.pk, content_type=ContentType.objects.get_for_model(instance.__class__)).delete()
    OnCompleteHook.objects.filter(object_id=instance.pk, content_type=ContentType.objects.get_for_model(instance.__class__)).delete()