class EdFi::Indiana::Sandbox::CourseSectionStudentService <
  EdFi::Indiana::Sandbox::ApplicationService
  def call
    ed_fi_classes.each { |c| props(c) }
    class_subjects.each { |c| props(c) }
  end

  def endpoint
    'ed-fi/studentsectionassociations'
  end

  def find(id)
    records = rest_client_request(
      :get,
      url,
      nil,
      header_params: { params: { studentUniqueId: id } }
    )
    records ? JSON.parse(records).map { |r| record_props(r) } : []
  end

  private
    def ed_fi_classes
      school.classrooms
        .preload(:class_periods, students: :ed_fi_ids)
        .joins(:school_state_classroom)
        .by_type([:period_long])
        .where.not(course_number: ['', ' '])
    end

    def class_subjects
      school.class_subjects
        .preload(classroom: [:class_periods, :employees, students: :ed_fi_ids])
        .joins(classroom: :school_state_classroom)
        .merge(Classroom.by_type([:day_long, :preschool]))
        .where.not(code: ['', ' '])
    end

    def school_year_students
      @school_year_students ||= school_year.school_year_students
        .where(current: true)
        .where.not(grade_id: grades_mapped_ungraded)
        .index_by(&:student_id)
    end

    def periods
      @periods ||= school.periods.index_by(&:id)
    end

    def begin_and_end_days_by_semester
      @begin_and_end_days_by_semester ||= school_year.begin_and_end_days_by_semester
    end

    def student_in_semester?(record, semester)
      return false if record.nil?
      return true if semester == :year_round

      semester_start = begin_and_end_days_by_semester[semester][:begin].date
      semester_end = begin_and_end_days_by_semester[semester][:end].date
      return true if (semester_start..semester_end).cover?(record.entry_date)
      return true if (semester_start..semester_end).cover?(record.exit_date)

      record.entry_date < semester_start &&
        (record.exit_date.nil? || record.exit_date > semester_end)
    end

    def terms
      @terms ||= if school.school_config.semesters?
        { 1 => 'Fall Semester', 2 => 'Spring Semester', year_round: 'Year Round' }
      else
        {
          1 => 'First Trimester',
          2 => 'Second Trimester',
          3 => 'Third Trimester',
          year_round: 'Year Round'
        }
      end
    end

    # Classroom is Year Round if it is not credit bearing and
    # is either a preschool or day long classroom.
    def year_round_classroom?(classroom)
      return false if classroom.credit.positive?

      ['preschool', 'day_long'].include?(classroom.type)
    end

    def props(object)
      if object.is_a?(Classroom)
        classroom = object
        code = object.course_number
      else
        classroom = object.classroom
        code = object.code
      end

      period_id = classroom.class_periods.min_by(&:day)&.period_id
      period = classroom.preschool? ? periods.values.first : periods[period_id]

      classroom.students.each do |student|
        terms.each_key do |semester|
          # Send record once for year round classrooms
          next if year_round_classroom?(classroom) && semester != :year_round
          # Skip year_round semester when classroom is not year round
          next if semester == :year_round && !year_round_classroom?(classroom)

          full_year_or_year_round = semester == :year_round || classroom.semester == :full_year
          next unless full_year_or_year_round || classroom.semester == semester
          next unless student_in_semester?(school_year_students[student.id], semester)

          if semester == :year_round
            # Grab first and last day of entire school year
            begin_dates = begin_and_end_days_by_semester[school_config.semesters.keys.first]
            end_dates = begin_and_end_days_by_semester[school_config.semesters.keys.last]
            semester_code = 0
          else
            begin_dates = begin_and_end_days_by_semester[semester]
            end_dates = begin_and_end_days_by_semester[semester]
          end

          term_begin_date = begin_dates[:begin].date
          term_end_date = end_dates[:end].date
          student_begin_date = school_year_students[student.id]&.entry_date || term_begin_date
          student_end_date = school_year_students[student.id]&.exit_date || term_end_date

          data = {
            sectionReference: {
              schoolId: state_id.number,
              localCourseCode: code,
              sessionName: "#{school_year.academic_year} #{terms[semester]}",
              schoolYear: school_year.academic_year,
              sectionIdentifier: classroom.period_long? ? classroom.id : object.id
            },
            studentReference: {
              studentUniqueId: student.state_number&.number
            },
            beginDate: student_begin_date || term_begin_date,
            endDate: student_end_date || term_end_date
          }

          # Use the index of the semester to keep track of which ed_fi_id belongs to which semester
          semester_code ||= semester.to_i
          ed_fi_id = student.ed_fi_ids.find do |id|
            id.descriptor == "#{semester_code}#{code}".to_i && id.school_year == school_year
          end

          if ed_fi_id
            method = :put
            path = "#{url}/#{ed_fi_id.number}"
          else
            method = :post
            path = url
          end

          response = rest_client_request(method, path, student, body: data.to_json)
          next if ed_fi_id || response.nil?

          edfi_id = response.headers[:location].split('/').last
          student.ed_fi_ids.create(
            number: edfi_id,
            endpoint: endpoint,
            descriptor: "#{semester_code}#{code}".to_i,
            school_year: school_year
          )
        end
      end
    end

    def record_props(response)
      {
        id: response['id'],
        sessionName: response['sectionReference']['sessionName'],
        courseCode: response['sectionReference']['localCourseCode'],
        attemptStatusDescriptor: response['attemptStatusDescriptor']
      }
    end
end
