from django.db import models
from otree.models import Participant
from django.db import transaction
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

EXITTYPES = [(0, 'form submitted'), (1, 'page unloaded'), (2, 'client disconnected')]
FOCUS_EVENT_TYPES = (
(0, 'Page shown'), (1, 'Visibility: off'), (2, 'Focus: off'), (3, 'Visibility: on'), (4, 'Focus: on'), (5,'Form submitted'))


# There are 3 different scenarios how a client may exit the page.
# 1. He can submit the form by clicking next (or in oTree any other button because the entire page
# is wrapped into form tags
# 2. He can refresh the page (clicking F5 or command+R)
# 3. The browser can be closed (by accident or intentionally)

# The Db structure is the following:
# Enter timestamp is linked to participant, and it also contains info on page. All timestamps correspond on time
# when a participant connects to a channel on page load.

# since there are several exit timestamp events that can be linked to one single enter event, that means that they
# should be connected to an enter timestamp via ForeignKey.
# How the connection takes place:
# The Enter event has  a boolean attribute Closed, initially set to false.
# When the client disconnects it looks for all not closed enter events for this participant-page pair, and closes them
# As a safeguard, the same closing happens everytime a client connects to the page, and before a new open Enter event is
# created.

# The aggregation for stats purposes is done the following way:
# * we choose all enter events for the specific participant and page
# * collect the earliest exit event for each enter event (via subquery and aggregation)
# * annotate the query via F and ExpressionWrapper to calculate the difference between this earlist exit and its parent
# Enter event
# ...
# PROFIT!!
class OpenEnterEventManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(closed=False)

    def close_all(self, participant, page_name):
        q = self.get_queryset().filter(participant=participant, page_name=page_name)
        q.update(closed=True)


class GeneralEvent(models.Model):
    class Meta:
        get_latest_by = 'timestamp'
        abstract = True

    page_name = models.CharField(max_length=1000)
    participant = models.ForeignKey(to=Participant, related_name="%(app_label)s_%(class)s_events")
    app_name = models.CharField(max_length=1000)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    player_id = models.PositiveIntegerField()
    player = GenericForeignKey('content_type', 'player_id')
    timestamp = models.DateTimeField()


class EnterEvent(GeneralEvent):
    class Meta(GeneralEvent.Meta):
        base_manager_name = 'objects'

    objects = models.Manager()
    opened = OpenEnterEventManager()
    # do we need closed? the disconnect method does it too late
    closed = models.BooleanField(default=False)

    def __str__(self):
        return 'id: {}, Enter: {}; time: {}; closed: {}'.format(self.pk, self.page_name, self.timestamp, self.closed)


class ExitEvent(models.Model):
    class Meta:
        get_latest_by = 'timestamp'

    enter_event = models.ForeignKey(to=EnterEvent, related_name='exits')
    timestamp = models.DateTimeField()
    exit_type = models.IntegerField(choices=EXITTYPES)

    def __str__(self):
        return 'id: {}, time: {}; for ENRANCE event {}. Type: {} '.format(self.pk,
                                                                          self.timestamp,
                                                                          self.enter_event.timestamp,
                                                                          self.get_exit_type_display())


class FocusEvent(GeneralEvent):
    event_desc_type = models.CharField(max_length=1000)
    event_num_type = models.IntegerField(choices=FOCUS_EVENT_TYPES)
