import json
from collections import OrderedDict
from datetime import datetime

from django.apps import apps
from django.db import models

from paw.constants.base import NAME, URL, IS_SELECTED, MODAL_ID
from schoolyears.constants import SCHOOL_TYPES, SCHOOL_COLORS, SPECIAL_EVENTS, SPECIAL_COLORS, TRAVEL_METHODS, \
    ADD_SCHOOL_YEAR_MODAL_ID, NAME_ADD_SCHOOL_YEAR, ADD_SCHOOL_YEAR, KEY_MTG
from django.utils.translation import get_language

class SchoolYearManager(models.Manager):
    def get_school_years_and_schools(self):
        # Get all school years to be displayed in a dropdown
        # Arranged by is_active, and then end_date
        # Note: Months can be autogenerated in the front-end
        school_year_query = super(SchoolYearManager, self).get_queryset().order_by('is_active', 'start_date').reverse()
        all_school_years = []
        
        # Retrieve all school names and IDs per school year
        # Note: the dropdown should show "All schools as its default"
        # Note: if there are no schools, disable the dropdown.
        school_model = apps.get_model(app_label='schoolyears', model_name='School')
        for school_year in school_year_query:
            school_year_object = {'school_year':school_year, 'schools':[]}
            school_query = school_model.objects.filter(school_year=school_year).values('id', 'name', 'name_kanji')
            schools = []
            for school in school_query:
                schools.append(school)
            school_year_object['schools'] = json.dumps(schools)
            all_school_years.append(school_year_object)

        return all_school_years

    def get_sidebar_and_selected_school_year(self, active_id):
        all_school_years = super(SchoolYearManager, self).get_queryset().filter().order_by('is_active', 'start_date').reverse()
        sidebar = OrderedDict()
        selected_school_year = None
        for schoolyear in all_school_years:
            is_selected = False
            # If we are in the home page, is_selected depends on the is_active flag
            if active_id == 0:
                is_selected = schoolyear.is_active
            # Else, it depends on which id is selected
            elif active_id == schoolyear.id:
                is_selected = True
            # Mark the selected school year
            if is_selected:
                selected_school_year = schoolyear
            # Sidebar
            sidebar[schoolyear.id] = {
                NAME: schoolyear.name,
                URL: 'schoolyears/'+str(schoolyear.id),
                IS_SELECTED: is_selected,
                MODAL_ID: None,
            }
        # If there are no selected school years, and all_schoolyears contains at least one item:
        if selected_school_year is None and len(all_school_years) > 0:
            # Set the first item as the selected school year
            first_element = all_school_years[0]
            active_id = first_element.id
            selected_school_year = first_element
            sidebar[active_id][IS_SELECTED] = True

        sidebar[ADD_SCHOOL_YEAR] = {
            NAME: NAME_ADD_SCHOOL_YEAR,
            URL: '#',
            IS_SELECTED: False,
            MODAL_ID: ADD_SCHOOL_YEAR_MODAL_ID,
        }

        return {'sidebar': sidebar, 'selected': selected_school_year}

class SchoolManager(models.Manager):
    def get_schools_with_arranged_school_colors(self, selected_school_year):
        schools_raw = super(SchoolManager, self).get_queryset().filter(school_year=selected_school_year)
        for school in schools_raw:
            school.school_colors = school.get_hex_color()
        return schools_raw

    def get_json_school_types(self):
        json_school_types = OrderedDict()
        for item in SCHOOL_TYPES:
            json_school_types[item[0]] = str(item[1])
        return json.dumps(json_school_types)

    def get_rearranged_colors(self):
        school_colors = OrderedDict()
        for item in SCHOOL_COLORS:
            school_colors[item[0]] = item[1] # 'RE' = 'FF0000'
        return school_colors

    def get_json_event_info(self, all_schools):
        # Build a dictionary mapping the school ID and the information of the school
        school_id_info_map = {}
        id_color_map = SpecialYearlyScheduleManager().get_rearranged_colors()

        for school in all_schools:
            school_id = school.id
            if school_id not in school_id_info_map:
                school_id_info_map[school_id] = {}
            school_id_info_map[school_id]['color'] = school.school_colors
            school_id_info_map[school_id]['address'] = school.address
            if get_language() == 'en':
                school_name = school.name
            else:
                school_name = school.name_kanji
            school_id_info_map[school_id]['name'] = school_name

        # Update the dictionary containing the special schedules
        for event in SPECIAL_EVENTS:
            event_id = event[0]
            if event_id not in school_id_info_map:
                school_id_info_map[event_id] = {}
            school_id_info_map[event_id]['color'] = id_color_map[event_id]
            school_id_info_map[event_id]['address'] = 'N/A'
            school_id_info_map[event_id]['name'] = str(event[1])

        return json.dumps(school_id_info_map)

    def get_json_all_schedules(self, selected_school_year):
        # Retrieve all schedule entries for display
        # Build a dictionary containing an array of dates, accessible by the school ID as its key 
        all_schedules = {}
        yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='YearlySchedule')
        for item in yearly_schedule_model.objects.filter(school_year=selected_school_year):
            school_id = item.school.id
            if school_id not in all_schedules:
                all_schedules[school_id] = []
            all_schedules[school_id].append(item.date.strftime("%Y-%m-%d"))

        # Retrieve all the special entries
        # Update the dictionary, this time with special keys like, 'TBA' or 'MTG'
        special_yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='SpecialYearlySchedule')
        for item in special_yearly_schedule_model.objects.filter(school_year=selected_school_year):
            key = item.special_event
            if key not in all_schedules:
                all_schedules[key] = []
            all_schedules[key].append(item.date.strftime('%Y-%m-%d'))

        return json.dumps(all_schedules)

class NodeManager(models.Manager):
    def save_new_node(self, new_node_name):
        return_node = None

        # Check if any node with the same name exists: if it does, get its ID.
        node = super(NodeManager, self).get_queryset().filter(name=new_node_name)
        if node.exists():
            node = node.get()
            return_node = node

        # If it doesn't exist, create a new node with the given name. Get its ID.
        else:
            node_model = apps.get_model(app_label='schoolyears', model_name='Node')
            new_node = node_model(name=new_node_name)
            new_node.save()
            return_node = new_node

        return return_node

class RouteInfoManager(models.Manager):
    def save_new_route_info(self, new_node):
        # Create a new entry in RouteInfo with the given node ID. 
        # Set the other fields to null.
        # Get the route ID
        route_info_model = apps.get_model(app_label='schoolyears', model_name='RouteInfo')
        new_route_info = route_info_model(node=new_node, next_path=None, next_route=None)
        new_route_info.save()
        return new_route_info

class SchoolRouteManager(models.Manager):
    def get_json_travel_method_types(self):
        json_travel_method_types = OrderedDict()
        for travel_method_type in TRAVEL_METHODS:
            json_travel_method_types[travel_method_type[0]] = travel_method_type[1]
        return json.dumps(json_travel_method_types)

    def get_alt_meeting_routes_with_school_year(self, school_year):
        alt_meeting_routes = []

        # Check if a round-trip ALT Route exists
        school_route_model = apps.get_model(app_label='schoolyears', model_name='SchoolRoute')
        alt_meeting_round_trip = school_route_model.objects.filter(is_alt_meeting=True, school_year=school_year, is_round_trip=True)
        alt_meeting_single_trips = school_route_model.objects.filter(is_alt_meeting=True, school_year=school_year, is_round_trip=False)

        if alt_meeting_round_trip.exists():
            for route in alt_meeting_round_trip:
                alt_meeting_routes.append(route)

        # If not, check if two ALT Routes exist, both are not round trip
        elif alt_meeting_single_trips.exists() and alt_meeting_single_trips.count() == 2:
            for route in alt_meeting_single_trips:
                alt_meeting_routes.append(route)

        return alt_meeting_routes

    def get_all_routes_of_day_with_school_year(self, datetime_object, school_year):
        # Check what school you go to in that specific day, if there is any
        # Check if there are any ALT meetings for that specific day
        yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='YearlySchedule')
        special_yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='SpecialYearlySchedule')
        school_route_model = apps.get_model(app_label='schoolyears', model_name='SchoolRoute')

        school = yearly_schedule_model.objects.filter(school_year=school_year, date=datetime_object).values('school')
        alt_meeting = special_yearly_schedule_model.objects.filter(school_year=school_year, date=datetime_object, special_event=KEY_MTG)
        school_routes = super(SchoolRouteManager, self).get_queryset().filter(school_year=school_year, school=school)
        alt_meeting_routes = school_route_model.objects.get_alt_meeting_routes_with_school_year(school_year)
        routes_current_day = []

        # Check if an existing route exists for that school. If it does, push it in the main array
        if school.exists() and school_routes.exists():
            for route in school_routes:
                routes_current_day.append(route)

        # Check if an ALT route exists. If it does, push it in the main array
        if alt_meeting.exists() and len(alt_meeting_routes) > 0:
            for route in alt_meeting_routes:
                routes_current_day.append(route)

        return routes_current_day


class YearlyScheduleManager(models.Manager):
    def get_schedule_information_for_day(self, date):
        yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='YearlySchedule')
        special_yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='SpecialYearlySchedule')
        school_period_type_model = apps.get_model(app_label='schedules', model_name='SchoolPeriodType')
        
        schedules = yearly_schedule_model.objects.filter(date=date)
        for schedule in schedules:
            schedule.information = school_period_type_model.objects.get_period_type_and_periods(schedule.school, date)
        events = special_yearly_schedule_model.objects.filter(date=date)

        weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        return {'schedules': schedules, 'events': events, 'date': date, 'weekday': weekdays[date.weekday()]}

    def get_school_schedule(self, school, year, month):
        yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='YearlySchedule')
        school_period_type_model = apps.get_model(app_label='schedules', model_name='SchoolPeriodType')
        
        all_schedules = yearly_schedule_model.objects.filter(
            school=school,
            date__year=year,
            date__month=month
        ).order_by('date')
        
        for schedule in all_schedules:
            schedule.day = schedule.date.strftime("%d")
            schedule.periods = school_period_type_model.objects.get_period_type_and_periods_view_schedule(school, schedule.date)
        
        return all_schedules

    def save_schedule(self, data, school_id, school_year):
        school_model = apps.get_model(app_label='schoolyears', model_name='School')
        yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='YearlySchedule')
            
        for date in data[school_id]:
            datetime_object = datetime.strptime(date, "%Y-%m-%d")
            school = school_model.objects.get(id=school_id)

            # SELECT * FROM schedules WHERE id=school_id AND date = date
            # If no such result exists, insert it into the database (else, do nothing)
            if not yearly_schedule_model.objects.filter(
                    school_year=school_year, 
                    school=school_id, 
                    date=datetime_object).exists():
                new_entry = yearly_schedule_model(
                    school_year=school_year, 
                    school=school, 
                    date=datetime_object)
                new_entry.save()

        # SELECT * FROM schedules WHERE school_year=school_year AND school_id=school_id
        deletable_dates = yearly_schedule_model.objects.filter(
            school_year=school_year, 
            school=school_id)
        for deletable_date_entry in deletable_dates:
            if deletable_date_entry.date.strftime("%Y-%m-%d") not in data[str(deletable_date_entry.school.id)]:
                deletable_date_entry.delete()

class SpecialYearlyScheduleManager(models.Manager):
    def get_rearranged_colors(self):
        id_color_map = {}
        for item in SPECIAL_COLORS:
            id_color_map[item[0]] = item[1]
        return id_color_map

    def get_special_events_key_value_color(self):
        all_special_events = []
        id_color_map = SpecialYearlyScheduleManager().get_rearranged_colors()

        for item in SPECIAL_EVENTS:
            all_special_events.append({'key':item[0], 
                'value':item[1], 
                'color':id_color_map[item[0]]})
        return all_special_events

    def save_schedule(self, data, event_id, school_year):
        for date in data[event_id]:
            datetime_object = datetime.strptime(date, "%Y-%m-%d")
            special_yearly_schedule_model = apps.get_model(app_label='schoolyears', model_name='SpecialYearlySchedule')
            if not special_yearly_schedule_model.objects.filter(
                    school_year=school_year, 
                    special_event=event_id, 
                    date=datetime_object).exists():
                new_entry = special_yearly_schedule_model(
                    school_year=school_year, 
                    special_event=event_id, 
                    date=datetime_object)
                new_entry.save()

        special_deletable_dates = special_yearly_schedule_model.objects.filter(
            school_year=school_year, 
            special_event=event_id)
        for deletable_date_entry in special_deletable_dates:
            if deletable_date_entry.date.strftime("%Y-%m-%d") not in data[str(deletable_date_entry.special_event)]:
                deletable_date_entry.delete()
