# frozen_string_literal: true

#
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

class ObserverEnrollment < Enrollment
  def observer?
    true
  end

  def self.observed_enrollments_for_courses(contexts, user)
    contexts = Array(contexts)
    observed_students = []
    Shard.partition_by_shard(contexts) do |sharded_contexts|
      observer_enrollment_data = user.observer_enrollments.where(course_id: sharded_contexts)
                                     .where.not(associated_user_id: nil).pluck(:course_id, :associated_user_id)

      observer_enrollment_data.group_by(&:first).each do |course_id, course_enroll_data|
        associated_user_ids = course_enroll_data.map(&:last)
        students = StudentEnrollment.active.where(user_id: associated_user_ids, course_id:)
        observed_students.concat(students)
      end
    end
    observed_students
  end

  def self.observed_enrollments_for_enrollments(observer_enrollments, active_by_date: false)
    # if we already have the enrollments why go and fetch them again
    observer_enrollments = Array(observer_enrollments)
    observed_student_enrollments = []
    Shard.partition_by_shard(observer_enrollments) do |sharded_observer_enrollments|
      sharded_observer_enrollments.group_by(&:course_id).each do |course_id, enrollments|
        associated_user_ids = enrollments.map(&:associated_user_id)
        student_enrolls = StudentEnrollment.active.where(user_id: associated_user_ids, course_id:)
        student_enrolls = student_enrolls.active_by_date if active_by_date
        observed_student_enrollments.concat(student_enrolls.to_a)
      end
    end
    observed_student_enrollments
  end

  # returns a hash mapping students to arrays of enrollments
  def self.observed_students(context, current_user, include_restricted_access: true, grade_summary: false)
    RequestCache.cache(:observed_students, context, current_user) do
      context.shard.activate do
        associated_user_ids = context.observer_enrollments.where(user_id: current_user).where.not(associated_user_id: nil).select(:associated_user_id)
        students = grade_summary ? context.student_enrollments_including_completed.where(user_id: associated_user_ids) : context.student_enrollments.where(user_id: associated_user_ids)
        unless include_restricted_access
          students = students.joins(:enrollment_state).where(enrollment_states: { restricted_access: false })
        end
        students.group_by(&:user)
      end
    end
  end

  # NOTE: naively finding users by these ID's may not work due to sharding
  def self.observed_student_ids(context, current_user)
    context.shard.activate do
      context.observer_enrollments.where("user_id=? AND associated_user_id IS NOT NULL", current_user).pluck(:associated_user_id)
    end
  end

  def self.observed_student_ids_by_observer_id(course, observers)
    # select_all allows plucking multiplecolumns without instantiating AR objects
    obs_hash = {}

    ObserverEnrollment.where(course_id: course, user_id: observers)
                      .pluck(:user_id, :associated_user_id).each do |user_id, associated_user_id|
      obs_hash[user_id] ||= []
      obs_hash[user_id] << associated_user_id if associated_user_id
    end

    # should look something like this: {10 => [11,12,13], 20 => [11,24,32]}
    obs_hash
  end
end
