from datetime import date
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from ces_fall_rest_api.models.course import Course
from ces_fall_rest_api.models.semester import Semester

from ces_fall_rest_api.models.faculty import Faculty
from ces_fall_rest_api.models.responses import Responses
from ces_fall_rest_api.models.subject_area import SubjectArea
from ces_fall_rest_api.serializers.course_serializer import CourseSerializer
from ces_fall_rest_api.serializers.faculty_serializer import FacultySerializer

from ces_fall_rest_api.models.section import Section
from ces_fall_rest_api.serializers.section_serializer import SectionSerializer
from rest_framework.permissions import IsAuthenticated

from ces_fall_rest_api.serializers.responses_serializer import ResponseSerializer
from ces_fall_rest_api.models.student_in_section import StudentInSection
from ces_fall_rest_api.serializers.student_in_section_serializer import StudentInSectionSerializer

from django.http import HttpResponse


class FacultyViewSet(viewsets.ModelViewSet):
    """A viewset to get the all the Faculty objects
    This class has a token authentication.
    I also has several actions to retrive reports from database:
    1- is_area_coordinator
    2- view_faculty_curriculum
    3- view_all_responses
    4- view_individual_responses
    5- view_semester_responses
    6- view_annual_responses
    7- get_sections
    8- current_courses

    Args:
        viewsets (_type_): viewsets class

    Returns:
        _type_: Response in JSON format
    """
    queryset = Faculty.objects.all()
    serializer_class = FacultySerializer
    permission_classes = [IsAuthenticated]

    # An action to check if the faculty is an area coordinator
    @action(detail=True, url_path='is_area_coordinator')
    def is_area_coordinator(self, request, pk=None):
        try:
            SubjectArea.objects.get(coordinator=pk)
            return Response(True)
        except SubjectArea.DoesNotExist:
            return Response(False)

    @action(detail=True, url_path='subject_area_course_curriculum')
    def subject_area_course_curriculum(self, request, pk=None):
        try:
            faculty_instance = self.get_object()
            coordinator_instance = SubjectArea.objects.get(
                coordinator=faculty_instance)

            sections_in_area = StudentInSection.objects.filter(
                student__major__subject_area=coordinator_instance.title)

            response = dict()

            for section in sections_in_area:
                if section.section.semester.year in response:
                    response[section.section.semester.year][section.section.semester.month].append(
                        section.section.reference)
                else:
                    response[section.section.semester.year] = {section.section.semester.month: [
                        str(section.section)]}

            return Response(response)

        except SubjectArea.DoesNotExist:
            return Response({'Error': 'Not a subject area coordinator'})

    @action(detail=True, url_path='subject_area_degree_curriculum')
    def subject_area_degree_curriculum(self, request, pk=None):
        try:
            faculty_instance = self.get_object()
            coordinator_instance = SubjectArea.objects.get(
                coordinator=faculty_instance)

            students_in_area = StudentInSection.objects.filter(
                student__major__subject_area=coordinator_instance.title)

            # store the degrees in a set
            available_degrees = {
                student_in_section.student.major.degree for student_in_section in students_in_area}

            return Response(available_degrees)
        except SubjectArea.DoesNotExist:
            return Response({'Error': 'Not a subject area coordinator'})

    @action(detail=True, url_path='subject_area_year_responses')
    def subject_area_year_responses(self, request, year, degree, pk=None):
        try:
            faculty_instance = self.get_object()
            coordinator_instance = SubjectArea.objects.get(
                coordinator=faculty_instance)

            # get responses that belong to a specific degree in a year
            students_in_area_in_year = StudentInSection.objects.filter(
                student__major__subject_area=coordinator_instance.title, student__major__degree=degree.strip(), section__semester__year=year)

            # combine all responses
            responses = [ResponseSerializer(
                student.section.responses_set.all(), many=True).data for student in students_in_area_in_year]

            if len(responses) > 0:
                return Response(responses[0])
            else:
                return Response([])

        except SubjectArea.DoesNotExist:
            return Response({'error': 'Not a subject area coordinator'})

    @action(detail=True, url_path='subject_area_semester_responses')
    def subject_area_semester_responses(self, request, year, month, degree, pk=None):
        try:
            faculty_instance = self.get_object()
            coordinator_instance = SubjectArea.objects.get(
                coordinator=faculty_instance)

            # get responses that belong to a specific degree in a year
            students_in_area_in_year = StudentInSection.objects.filter(
                student__major__subject_area=coordinator_instance.title, student__major__degree=degree.strip(), section__semester__year=year, section__semester__month=month)

            # combine all responses
            responses = [ResponseSerializer(
                student.section.responses_set.all(), many=True).data for student in students_in_area_in_year]

            if len(responses) > 0:
                return Response(responses[0])
            else:
                return Response([])

        except SubjectArea.DoesNotExist:
            return Response({'error': 'Not a subject area coordinator'})

    @action(detail=True, url_path='subject_area_individual_responses')
    def subject_area_individual_responses(self, request, reference, year, month, degree, pk=None):
        try:
            faculty_instance = self.get_object()
            coordinator_instance = SubjectArea.objects.get(
                coordinator=faculty_instance)

            # get responses that belong to a specific degree in a year
            students_in_area_in_year = StudentInSection.objects.filter(
                student__major__subject_area=coordinator_instance.title, student__major__degree=degree.strip(), section__semester__year=year, section__semester__month=month, section__reference=reference)

            # combine all responses
            responses = [ResponseSerializer(
                student.section.responses_set.all(), many=True).data for student in students_in_area_in_year]

            if len(responses) > 0:
                return Response(responses[0])
            else:
                return Response([])

        except SubjectArea.DoesNotExist:
            return Response({'error': 'Not a subject area coordinator'})

    @action(detail=True, url_path='view_faculty_curriculum')
    def view_faculty_curriculum(self, request, pk=None):
        faculty_instance = self.get_object()
        sections_taught = faculty_instance.section_set.all()
        if len(sections_taught):
            response_builder = dict()
            response_builder['course_key'] = {}

            for i in range(0, len(sections_taught)):
                # if the year is not present, create it
                year = sections_taught[i].semester.year
                month = sections_taught[i].semester.month
                course = sections_taught[i].course
                course_id = sections_taught[i].course.id
                reference = sections_taught[i].reference
                reference_name = sections_taught[i].name

                if year not in response_builder:
                    response_builder[year] = {course_id: {
                        month: {str(reference) + " " + reference_name}}}
                else:
                    #  if the course is not present in the year, create it
                    if course_id not in response_builder[year]:
                        response_builder[year][course_id] = {
                            month: {str(reference) + " " + reference_name}}
                    else:
                        # if the semester is not present in the course, create it
                        if month not in response_builder[year][course_id]:
                            response_builder[year][course_id][month] = {
                                str(reference) + " " + reference_name}
                        else:
                            # if the semester is present, then just add the reference to the set
                            response_builder[year][course_id][month].add(
                                str(reference) + " " + reference_name)

                if course_id not in response_builder['course_key']:
                    response_builder['course_key'][course_id] = str(course)

            return Response(response_builder)
        else:
            return Response({})

    # A GET action to retrive all the responses for a faculty
    @action(detail=True, url_path='view_all_responses')
    def view_all_responses(self, request, pk=None):
        faculty_instance = self.get_object()
        all_sections = faculty_instance.section_set.all()
        all_responses = Responses.objects.none()
        for sections in all_sections:
            all_responses = sections.responses_set.all().union(all_responses)

        return Response(ResponseSerializer(all_responses, many=True).data)

    # A GET action to retrive an individual report of one course for a particular faculty and semester
    @action(detail=True, url_path='view_individual_responses')
    def view_individual_responses(self, request, reference, year, month, pk=None):
        faculty_instance = self.get_object()
        selected_section = faculty_instance.section_set.all().filter(
            reference=reference, semester__year=year, semester__month=month)

        responses = Responses.objects.none()
        for section in selected_section:
            responses = section.responses_set.all().union(responses)

        return Response(ResponseSerializer(responses, many=True).data)

    # A GET action that retrive the semester report of all the courses for a particular semester
    @action(detail=True, url_path='view_semester_responses')
    def view_semester_responses(self, request, year, month, pk=None):
        faculty_instance = self.get_object()
        current_sections = faculty_instance.section_set.all()
        try:
            current_sections = current_sections.filter(
                semester__year=year, semester__month=month)
            current_responses = current_sections[0].responses_set.all()
            for section in current_sections:
                current_responses = section.responses_set.all().union(current_responses)

            serialized_response = ResponseSerializer(
                current_responses, many=True)
            return Response(serialized_response.data)
        except:
            return HttpResponse('')

    # A GET action to retrive annual report of all the courses for a specific year
    @action(detail=True, url_path='view_annual_responses')
    def view_annual_responses(self, request, year, course, pk=None):
        faculty_instance = self.get_object()
        selected_sections = faculty_instance.section_set.all().filter(
            semester__year=year, course=course)

        if len(selected_sections) > 0:
            responses = selected_sections[0].responses_set.all()

            for section in selected_sections:
                responses = section.responses_set.all().union(responses)

            return Response(ResponseSerializer(responses, many=True).data)
        else:
            return Response([])

    # A GET action to retrive a report for a specific sections

    @action(methods=['get'], detail=True, url_path='get_sections')
    def get_sections(self, request, year, month, pk=None):
        faculty_instance = self.get_object()
        # get all courses that this instructor is imparting
        selected_sections = faculty_instance.section_set.all().filter(
            semester__year=year, semester__month=month)
        serialized_sections = SectionSerializer(
            selected_sections, many=True).data

        for section in serialized_sections:
            section['course'] = CourseSerializer(
                Course.objects.get(pk=section['course'])).data
        return Response(serialized_sections)

    # A GET action to retrive a report for the courses of current year
    @action(methods=['get'], detail=True)
    def current_courses(self, request, *args, **kwargs):
        instructor = self.get_object()
        current_year = date.today().year
        section_set = instructor.section_set.all().filter(semester__year=current_year)
        serializer = SectionSerializer(section_set, many=True)
        return Response(serializer.data)
