module OneRoster::AcademicSessionScoped
  extend ActiveSupport::Concern

  private
    def decode_session_source(id=nil)
      decoded = decode_source_id(id || params[:id])
      model = decoded['model'].split(/_(?=\d+$)/)
      session = decoded['id'].to_i
      @school_year_id = model.last

      {
        type: model.first,
        year: school_year,
        session: session,
        name: if model.first == 'semester_grading_period'
                school_config.semesters[session]
              else
                school_config.quarters[session]
        end
      }
    end

    def school_year
      current_school.school_years.find_by(id: @school_year_id)
    end

    def school_years
      @school_years ||= current_school.school_years.joins(:school_days).distinct
    end

    def school_config
      @school_config ||= current_school.school_config
    end

    def begin_and_end_by_year
      @begin_and_end_by_year ||= (@school_year_id ? [school_year] : school_years)
        .map { |y| [y.id, y.begin_and_end_days_by_quarter] }.to_h
    end

    def find_session(session)
      if ['semesters', 'semester'].include?(school_config.school_year_type)
        terms = school_config.quarters.keys
        return 1 if ([1, 2] & terms).include?(session)

        2
      else
        session
      end
    end

    def semester_quarters(semester)
      terms = school_config.quarters.keys
      semester == 1 ? ([1, 2] & terms) : ([3, 4] & terms)
    end

    def academic_sessions(session_year=nil)
      years = session_year ? [session_year] : school_years

      academic_sessions = []
      years.map do |year|
        school_config.quarters.map do |key, name|
          academic_sessions << session_props(key, name, year)
        end

        school_config.semesters.map do |key, name|
          if ['semesters', 'semester'].include?(school_config.school_year_type)
            academic_sessions << session_props(key, name, year, 'semester_grading_period')
            academic_sessions << session_props(key, name, year, 'semester')
          end
          academic_sessions << term_props(key, name, year)
        end

        academic_sessions << school_year_props(year)
      end

      bind_and_parse(academic_sessions)
    end

    def school_year_props(year)
      terms = school_config.semesters? ? school_config.semesters : school_config.quarters

      {}.tap do |props|
        props[:children] = terms.map { |k, _| session_references(k, year) }
        props[:dateLastModified] = year.updated_at&.iso8601
        props[:endDate] = begin_and_end_by_year[year.id].values.last[:end]&.date
        props[:startDate] = begin_and_end_by_year[year.id].values.first[:begin]&.date
        props[:schoolYear] = year.academic_year.to_s
        props[:sourcedId] = encode_source_id(:school_year, year.id)
        props[:status] =  year.current? ? :active : :tobedeleted
        props[:title] = year.name
        props[:type] = :schoolYear
      end
    end

    def term_props(session, name, year)
      {}.tap do |props|
        props[:children] = []
        if ['semesters', 'semester'].include?(school_config.school_year_type)
          semester_quarters(session)&.each do |k|
            props[:children] << session_references(k, year, 'gradingPeriod')
          end

          props[:children] << session_references(session, year, 'semester_grading_period')
          props[:children] << session_references(session, year, 'semester')
        else
          props[:children] << session_references(session, year, 'gradingPeriod')
        end

        sessions = begin_and_end_by_year[year.id]
        props[:dateLastModified] = sessions[session][:end]&.updated_at&.iso8601
        props[:endDate] = sessions[session][:end]&.date
        props[:startDate] = sessions[session][:begin]&.date
        props[:parent] = school_year_references(year)
        props[:schoolYear] = year.academic_year.to_s
        props[:sourcedId] = encode_source_id("term_#{year.id}", session)
        props[:status] =  year.current? ? :active : :tobedeleted
        props[:title] = name
        props[:type] = 'term'
      end
    end

    def session_props(session, name, year, type='gradingPeriod')
      sessions = begin_and_end_by_year[year.id]
      source = type

      {}.tap do |props|
        if ['semester', 'semester_grading_period'].include?(type)
          term = session
          type = 'gradingPeriod' if source == 'semester_grading_period'

          quarters = semester_quarters(session)
          props[:dateLastModified] = sessions[quarters.last][:end]&.updated_at&.iso8601
          props[:endDate] = sessions[quarters.last][:end]&.date
          props[:startDate] = sessions[quarters.first][:begin]&.date
        else
          term = find_session(session)

          props[:dateLastModified] = sessions[session][:end]&.updated_at&.iso8601
          props[:endDate] = sessions[session][:end]&.date
          props[:startDate] = sessions[session][:begin]&.date
        end

        props[:parent] = session_references(term, year)

        props[:schoolYear] = year.academic_year.to_s
        props[:sourcedId] = encode_source_id("#{source.underscore}_#{year.id}", session)
        props[:status] =  year.current? ? :active : :tobedeleted
        props[:title] = name
        props[:type] = type
      end
    end

    def school_year_references(year)
      {}.tap do |props|
        props[:sourcedId] = encode_source_id(:school_year, year.id)
        props[:href] = "#{site_url}/academicSessions/#{props[:sourcedId]}"
        props[:type] = :schoolYear
      end
    end

    def session_references(session, year, source='term')
      {}.tap do |props|
        props[:sourcedId] = encode_source_id("#{source.underscore}_#{year.id}", session)
        props[:href] = "#{site_url}/academicSessions/#{props[:sourcedId]}"
        props[:type] = source == 'semester_grading_period' ? 'gradingPeriod' : source
      end
    end

    def endpoint_props
      [
        :children,
        :dateLastModified,
        :endDate,
        :startDate,
        :schoolYear,
        :sourcedId,
        :status,
        :title,
        :type,
        :parent
      ]
    end
end
