from collections import defaultdict

from django.conf import settings
from django.core.cache import cache
from django.core.exceptions import PermissionDenied
from django.http import JsonResponse
from django.utils import timezone
from django.utils.crypto import constant_time_compare
from django.views.generic import View

from bakery.views import BuildableTemplateView
from wafer.talks.models import Talk, TalkType, Track
from wafer.schedule.views import ScheduleView
from wafer.schedule.models import Venue, ScheduleItem, Slot

from register.models import Attendee
from front_desk.models import CheckIn


class DCScheduleArrived(View):
    def arrived_users(self):
        queryset = Attendee.objects.filter(announce_me=True).select_related(
            'user',
            'user__userprofile',
            'check_in',
        )
        now = timezone.now()
        for attendee in queryset:
            user = attendee.user
            try:
                arrived = attendee.check_in is not None
            except CheckIn.DoesNotExist:
                arrived = False

            departed = False
            if attendee.departure:
                departed = attendee.departure < now

            yield {
                'username': user.username,
                'arrived': arrived,
                'departed': departed,
                'name': user.userprofile.display_name(),
                'nick': attendee.nametag_3,
            }

    def get(self, request, *args, **kwargs):
        auth_header = request.META.get('HTTP_AUTHORIZATION', '')
        auth = auth_header.split(None, 1)
        if not constant_time_compare(auth, ['Bearer', settings.DCSCHEDULE_TOKEN]):
            raise PermissionDenied('Missing/Invalid Authorization token')
        return JsonResponse({
            'arrived': list(self.arrived_users())
        })


class RobotsView(BuildableTemplateView):
    build_path = 'robots.txt'
    template_name = 'debconf/robots.txt'
    content_type = 'text/plain; charset=UTF-8'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['SANDBOX'] = settings.SANDBOX
        return context


class DebConfScheduleView(ScheduleView):
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['tracks'] = Track.objects.all()
        return context


def get_current_slot():
    now = timezone.now()

    for slot in Slot.objects.all():
        start = slot.start_time
        end = slot.end_time
        if start <= now and now < end:
            return slot


class IndexView(BuildableTemplateView):
    build_path = 'index.html'
    template_name = 'wafer/index.html'

    def get_context_data(self, *args, **kwargs):
        context_data = super().get_context_data(*args, **kwargs)

        venues = Venue.objects.filter(video=True)

        venue_blocks = [{'venue': venue} for venue in venues]

        current_slot = get_current_slot()
        if current_slot:
            events = ScheduleItem.objects.filter(slots=current_slot)
            for event in events:
                for blk in venue_blocks:
                    if event.venue == blk['venue']:
                        slots = list(event.slots.all())
                        blk['event'] = event
                        blk['start_time'] = slots[0].get_formatted_start_time()
                        blk['end_time'] = slots[-1].get_formatted_end_time()

        context_data['venue_blocks'] = venue_blocks

        return context_data


class StatisticsView(BuildableTemplateView):
    build_path = 'statistics/index.html'
    template_name = 'debconf/statistics.html'


class ContentStatisticsView(BuildableTemplateView):
    build_path = 'talks/statistics/index.html'
    template_name = 'debconf/content_statistics.html'
    cache_key = 'debconf:content_statistics'
    cache_timeout = 30*60 if not settings.DEBUG else 10

    def get_context_data(self, **kwargs):
        retval = cache.get(self.cache_key)
        if retval:
            return retval

        talks_submitted = Talk.objects.count()
        talks_reviewed = Talk.objects.filter(
            reviews__isnull=False).distinct().count()
        talks_scheduled = Talk.objects.filter(
            scheduleitem__isnull=False).distinct().count()

        minutes_of_content = 0
        for si in ScheduleItem.objects.filter(talk__isnull=False):
            duration = si.get_duration()
            minutes_of_content += duration['minutes'] + duration['hours'] * 60
        hours_of_content = minutes_of_content / 60

        concurrency_by_hour = defaultdict(int)
        for slot in Slot.objects.all():
            hour = slot.start_time.replace(
                minute=0, second=0, microsecond=0)
            concurrency_by_hour[hour] = max(concurrency_by_hour[hour],
                                            slot.scheduleitem_set.count())

        hours_of_concurrency = []
        if concurrency_by_hour:
            hours_of_concurrency = [
                (concurrency, sum(
                    1 for hour, hour_concurrency in concurrency_by_hour.items()
                    if hour_concurrency == concurrency))
                for concurrency in range(max(concurrency_by_hour.values()) + 1)]

        talks_by_track = {}
        for track in Track.objects.all():
            talks_by_track[track.name] = {
                'submitted': track.talk_set.count(),
                'scheduled': track.talk_set.filter(
                    scheduleitem__isnull=False).count(),
            }

        talks_by_type = {}
        for type_ in TalkType.objects.all():
            talks_by_type[type_.name] = {
                'submitted': type_.talk_set.count(),
                'scheduled': type_.talk_set.filter(
                    scheduleitem__isnull=False).count(),
            }

        countries = {}
        for talk in Talk.objects.filter(status__in=('A', 'P')).prefetch_related('authors'):
            for author in talk.authors.all():
                try:
                    country = author.attendee.country_name
                except Attendee.DoesNotExist:
                    country = 'Not registered yet'
                countries.setdefault(country, 0)
                countries[country] += 1
        speakers_by_country = sorted(countries.items(), key=lambda i: -i[1])


        retval = {
            'talks_submitted': talks_submitted,
            'talks_reviewed': talks_reviewed,
            'talks_scheduled': talks_scheduled,
            'hours_of_content': hours_of_content,
            'hours_of_concurrency': hours_of_concurrency,
            'talks_by_track': talks_by_track,
            'talks_by_type': talks_by_type,
            'speakers_by_country': speakers_by_country,
        }

        cache.set(self.cache_key, retval, self.cache_timeout)
        return retval
