#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
from collections import defaultdict
from typing import Iterable

from django.db import models


class PageCompletion(models.Model):
    class Meta:
        app_label = "otree"

    app_name = models.CharField(max_length=300)
    page_index = models.PositiveIntegerField()
    page_name = models.CharField(max_length=300)
    # it needs a default, otherwise i get "added a non-nullable field without default"
    # i think eventually i can remove the default.
    unix_time = models.PositiveIntegerField(default=0)
    seconds_on_page = models.PositiveIntegerField()
    subsession_pk = models.PositiveIntegerField()
    participant = models.ForeignKey('otree.Participant', on_delete=models.CASCADE)
    session = models.ForeignKey('otree.Session', on_delete=models.CASCADE)
    auto_submitted = models.BooleanField()


class WaitPagePassage(models.Model):
    participant = models.ForeignKey('otree.Participant', on_delete=models.CASCADE)
    session = models.ForeignKey('otree.Session', on_delete=models.CASCADE)
    # don't set default=time.time because that's harder to patch
    unix_time = models.PositiveIntegerField()
    # if False, means they exit the wait page
    is_enter = models.BooleanField()


class PageTimeout(models.Model):
    class Meta:
        app_label = "otree"
        index_together = ['participant', 'page_index']

    participant = models.ForeignKey('otree.Participant', on_delete=models.CASCADE)
    page_index = models.PositiveIntegerField()
    expiration_time = models.FloatField()


class CompletedGroupWaitPage(models.Model):
    class Meta:
        app_label = "otree"
        index_together = ['page_index', 'session', 'id_in_subsession']

    page_index = models.PositiveIntegerField()
    session = models.ForeignKey('otree.Session', on_delete=models.CASCADE)
    id_in_subsession = models.PositiveIntegerField(default=0)


class CompletedSubsessionWaitPage(models.Model):
    class Meta:
        app_label = "otree"
        index_together = ['page_index', 'session']

    page_index = models.PositiveIntegerField()
    session = models.ForeignKey('otree.Session', on_delete=models.CASCADE)


class ParticipantToPlayerLookup(models.Model):
    class Meta:
        app_label = "otree"
        index_together = ['participant', 'page_index']
        unique_together = ['participant', 'page_index']

    # TODO: add session code and round number, for browser bots?
    participant_code = models.CharField(max_length=20)
    participant = models.ForeignKey('otree.Participant', on_delete=models.CASCADE)
    page_index = models.PositiveIntegerField()
    app_name = models.CharField(max_length=300)
    player_pk = models.PositiveIntegerField()
    # can't store group_pk because group can change!
    subsession_pk = models.PositiveIntegerField()
    session_pk = models.PositiveIntegerField()
    url = models.CharField(max_length=300)


class ParticipantLockModel(models.Model):
    class Meta:
        app_label = "otree"

    participant_code = models.CharField(
        max_length=16, unique=True
    )

    locked = models.BooleanField(default=False)


class UndefinedFormModel(models.Model):
    """To be used as the model for an empty form, so that form_class can be
    omitted. Consider using SingletonModel for this. Right now, I'm not
    sure we need it.

    """

    class Meta:
        app_label = "otree"

    pass


class RoomToSession(models.Model):
    class Meta:
        app_label = "otree"

    room_name = models.CharField(unique=True, max_length=255)
    session = models.ForeignKey('otree.Session', on_delete=models.CASCADE)



class ParticipantRoomVisit(models.Model):
    class Meta:
        app_label = "otree"

    room_name = models.CharField(max_length=50)
    participant_label = models.CharField(max_length=200)
    tab_unique_id = models.CharField(max_length=20, unique=True)
    last_updated = models.FloatField()


class BrowserBotsLauncherSessionCode(models.Model):
    class Meta:
        app_label = "otree"

    code = models.CharField(max_length=10)

    # hack to enforce singleton
    is_only_record = models.BooleanField(unique=True, default=True)


class ChatMessage(models.Model):
    class Meta:
        index_together = ['channel', 'timestamp']

    # the name "channel" here is unrelated to Django channels
    channel = models.CharField(max_length=255)
    # related_name necessary to disambiguate with otreechat add on
    participant = models.ForeignKey(
        'otree.Participant', related_name='chat_messages_core',
        on_delete=models.CASCADE
    )
    nickname = models.CharField(max_length=255)

    # call it 'body' instead of 'message' or 'content' because those terms
    # are already used by channels
    body = models.TextField()
    timestamp = models.FloatField(default=time.time)


def add_time_spent_waiting(participants):
    session_passages_qs = (
        WaitPagePassage.objects
            .filter(participant__in=participants)
            .order_by('id')
    )
    _add_time_spent_waiting_inner(
        participants=participants,
        session_passages_qs=session_passages_qs
    )


def _add_time_spent_waiting_inner(*, participants, session_passages_qs: Iterable[WaitPagePassage]):
    '''adds the attribute to each participant object so it can be shown in the template'''

    session_passages = defaultdict(list)
    for passage in session_passages_qs:
        session_passages[passage.participant_id].append(passage)

    for participant in participants:
        total = 0
        enter_time = None
        passages = session_passages.get(participant.id, [])
        for p in passages:
            if p.is_enter and not enter_time:
                enter_time = p.unix_time
            if not p.is_enter and enter_time:
                total += p.unix_time - enter_time
                enter_time = None
        # means they are still waiting
        if enter_time:
            total += time.time() - enter_time
        participant._is_frozen = False
        participant.waiting_seconds = int(total)