from datetime import timedelta
from django.db.models import Sum
from .models import Table, Booking

def book_restaurant_table(restaurant, booking_date_time, people, minutes_slot=90):
    """
    This method uses get_first_table_available to get the first table available, then it
    creates a Booking on the database.
    """
    table = get_first_table_available(restaurant, booking_date_time, people, minutes_slot)

    if table:
        delta = timedelta(seconds=60*minutes_slot)
        booking = Booking(table=table, people=people,
            booking_date_time_start=booking_date_time, booking_date_time_end=booking_date_time + delta)
        booking.save()
        return {'booking': booking.id, 'table': table.id}
    else:
        return None

def get_first_table_available(restaurant, booking_date_time, people, minutes_slot=90):
    """
    This method returns the first available table of a restaurant, given a specific number of
    people and a booking date/time.
    """
    # I make sure to check if the tables are not already booked within the time slot required
    # by the new booking
    delta = timedelta(seconds=60*minutes_slot)
    l_bound_time = booking_date_time
    u_bound_time = booking_date_time + delta

    tables_booked_ids = []

    # Exclude tables which start and end booking date includes requested initial booking date_time
    tables_booked = Booking.objects.filter(table__restaurant=restaurant,
        booking_date_time_start__lt=l_bound_time,
        booking_date_time_end__gt=l_bound_time).values('table')
    tables_booked_ids_temp = [x['table'] for x in tables_booked]
    tables_booked_ids = tables_booked_ids + tables_booked_ids_temp

    # Exclude tables which start and end booking date includes requested ending booking date_time
    tables_booked = Booking.objects.filter(
        booking_date_time_start__lt=u_bound_time,
        booking_date_time_end__gt=u_bound_time).values('table')
    tables_booked_ids_temp = [x['table'] for x in tables_booked]
    tables_booked_ids = tables_booked_ids + tables_booked_ids_temp

    # Exclude tables which booking slots is inside requested booking slot
    tables_booked = Booking.objects.filter(
        booking_date_time_start__gt=l_bound_time,
        booking_date_time_end__lt=u_bound_time).values('table')
    tables_booked_ids_temp = [x['table'] for x in tables_booked]
    tables_booked_ids = tables_booked_ids + tables_booked_ids_temp

    # Exclude tables which include requested booking slot
    tables_booked = Booking.objects.filter(
        booking_date_time_start__lt=l_bound_time,
        booking_date_time_end__gt=u_bound_time).values('table')
    tables_booked_ids_temp = [x['table'] for x in tables_booked]
    tables_booked_ids = tables_booked_ids + tables_booked_ids_temp

    # Then I get a list of all the tables, of the needed size, available in that restaurant and
    # I exclude the previous list of unavailable tables. I order the list from the smaller table
    # to the bigger one and I return the first, smaller one, available.
    tables = Table.objects.filter(restaurant=restaurant,
        restaurant__opening_time__lte=booking_date_time.hour,
        restaurant__closing_time__gte=booking_date_time.hour+(minutes_slot / float(60)),
        size__gte=people).exclude(id__in=tables_booked_ids).order_by('size')

    if tables.count() == 0:
        return None
    else:
        return tables[0]

def get_expected_diners(restaurant, booking_date):
    """
    Return the expected number of diners of a restaurant for a specific date.
    """
    diners = Booking.objects.filter(
        table__restaurant=restaurant,
        booking_date_time_start__year=booking_date.year,
        booking_date_time_start__month=booking_date.month,
        booking_date_time_start__day=booking_date.day).aggregate(Sum('people'))
    return diners['people__sum']