# frozen_string_literal: true

#
# Copyright (C) 2012 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/>.

require_relative "../api_spec_helper"

describe "Favorites API", type: :request do
  before :once do
    user_factory(active_all: true)
    @courses = create_courses(6, enroll_user: @user, enrollment_type: "StudentEnrollment", return_type: :record)
  end

  context "implicit favorites" do
    it "lists favorite courses" do
      json = api_call(:get, "/api/v1/users/self/favorites/courses", controller: "favorites", action: "list_favorite_courses", format: "json")
      expect(json.size).to be(6)
      expect(json[0]["id"]).to eql @courses[0].id
      expect(json[0]["name"]).to eql @courses[0].name
      expect(json[0]["course_code"]).to eql @courses[0].course_code
      expect(json[0]["enrollments"][0]["type"]).to eql "student"
      expect(json.pluck("id").sort).to eql(@user.menu_courses.pluck(:id).sort)

      expect(@user.favorites.size).to be_zero
    end

    context "observed users" do
      before :once do
        @observer_enrollment = course_with_observer(active_all: true)
        @observer = @user
        @courses << @course
        @observer_course = @course
        @observed_student = create_users(1, return_type: :record).first
        @student_enrollment =
          @observer_course.enroll_student(@observed_student,
                                          enrollment_state: "active")
        @assigned_observer_enrollment =
          @observer_course.enroll_user(@observer,
                                       "ObserverEnrollment",
                                       associated_user_id: @observed_student.id)
        @assigned_observer_enrollment.accept
      end

      it "includes observed users" do
        json = api_call_as_user(@observer,
                                :get,
                                "/api/v1/users/self/favorites/courses?include[]=observed_users",
                                controller: "favorites",
                                include: ["observed_users"],
                                action: "list_favorite_courses",
                                format: "json")

        expect(json[0]["enrollments"]).to match_array [{
          "type" => "observer",
          "role" => @assigned_observer_enrollment.role.name,
          "role_id" => @assigned_observer_enrollment.role.id,
          "user_id" => @assigned_observer_enrollment.user_id,
          "enrollment_state" => "active",
          "limit_privileges_to_course_section" => false,
          "associated_user_id" => @observed_student.id
        },
                                                       {
                                                         "type" => "observer",
                                                         "role" => @observer_enrollment.role.name,
                                                         "role_id" => @observer_enrollment.role.id,
                                                         "user_id" => @observer_enrollment.user_id,
                                                         "enrollment_state" => "active",
                                                         "limit_privileges_to_course_section" => false,
                                                       },
                                                       {
                                                         "type" => "student",
                                                         "role" => @student_enrollment.role.name,
                                                         "role_id" => @student_enrollment.role.id,
                                                         "user_id" => @student_enrollment.user_id,
                                                         "enrollment_state" => "active",
                                                         "limit_privileges_to_course_section" => false,
                                                       }]
      end
    end
  end

  context "explicit favorites" do
    before :once do
      @courses[0...3].each do |course|
        @user.favorites.build(context: course)
      end
      @user.save
    end

    it "lists favorite courses" do
      json = api_call(:get, "/api/v1/users/self/favorites/courses", controller: "favorites", action: "list_favorite_courses", format: "json")
      expect(json.size).to be(3)
      expect(json[0]["id"]).to eql @courses[0].id
      expect(json[0]["name"]).to eql @courses[0].name
      expect(json[0]["course_code"]).to eql @courses[0].course_code
      expect(json[0]["enrollments"][0]["type"]).to eql "student"
      expect(json.pluck("id").sort).to eql(@user.favorites.by("Course").pluck(:context_id).sort)
    end

    it "adds a course to favorites" do
      @user.favorites.by("Course").destroy_all

      # add some new courses, and fave one
      course6 = course_with_student(course_name: "Course 6", user: @user, active_all: true).course
      course_with_student(course_name: "Course 7", user: @user, active_all: true).course
      json = api_call(:post,
                      "/api/v1/users/self/favorites/courses/#{course6.id}",
                      { controller: "favorites", action: "add_favorite_course", format: "json", id: course6.id.to_s })
      expect(json["context_id"]).to eql(course6.id)

      # now favorites should include the implicit courses from before, plus the one we faved
      @user.reload
      expect(@user.favorites.size).to be(1)
    end

    it "creates favorites from implicit favorites when removing a course" do
      @user.favorites.by("Course").destroy_all

      # remove a course from favorites
      json = api_call(:delete,
                      "/api/v1/users/self/favorites/courses/#{@courses[0].id}",
                      { controller: "favorites", action: "remove_favorite_course", format: "json", id: @courses[0].id.to_s })
      expect(json["context_id"]).to eql(@courses[0].id)

      # now favorites should include the implicit courses from before, minus the one we removed
      @user.reload
      expect(@user.favorites.size).to be(5)
    end

    it "removes a course from favorites" do
      expect(@user.favorites.size).to be(3)

      # remove a course from favorites
      json = api_call(:delete,
                      "/api/v1/users/self/favorites/courses/#{@courses[0].id}",
                      { controller: "favorites", action: "remove_favorite_course", format: "json", id: @courses[0].id.to_s })
      expect(json["context_id"]).to eql(@courses[0].id)

      # now favorites should include the implicit courses from before, minus the one we removed
      @user.reload

      expect(@user.favorites.size).to be(2)
    end

    it "does not create a duplicate by fav'ing an already faved course" do
      expect(@user.favorites.size).to be(3)
      json = api_call(:post,
                      "/api/v1/users/self/favorites/courses/#{@courses[0].id}",
                      { controller: "favorites", action: "add_favorite_course", format: "json", id: @courses[0].id.to_s })
      expect(json["context_id"]).to eql(@courses[0].id)
      @user.reload
      expect(@user.favorites.size).to be(3)
    end

    it "returns an empty hash when removing a non-faved course" do
      json = api_call(:delete,
                      "/api/v1/users/self/favorites/courses/#{@courses[5].id}",
                      { controller: "favorites", action: "remove_favorite_course", format: "json", id: @courses[5].id.to_s })
      expect(json.size).to be_zero
    end

    it "resets favorites" do
      expect(@user.favorites.size).not_to be_zero
      api_call(:delete,
               "/api/v1/users/self/favorites/courses",
               { controller: "favorites", action: "reset_course_favorites", format: "json" })
      @user.reload
      expect(@user.favorites.size).to be_zero
    end
  end

  context "group favorites" do
    before do
      @user = user_model
      @context = course_model
      @group_fave = Group.create!(name: "group1", context: @context)
      @group_not_fave = Group.create!(name: "group2", context: @context)
      @group_not_yet_fave = Group.create!(name: "group3", context: @context)
      @group_fave.add_user(@user)
      @group_not_fave.add_user(@user)
      @group_not_yet_fave.add_user(@user)
      @user.favorites.build(context: @group_fave)
      @user.save
    end

    it "add favorite group" do
      api_call(:post,
               "/api/v1/users/self/favorites/groups/#{@group_not_yet_fave.id}",
               controller: "favorites",
               action: "add_favorite_groups",
               format: "json",
               id: @group_not_yet_fave.id)
      expect(@user.favorites.size).to be(2) # Added one in before method, one in test
    end

    it "lists favorite groups" do
      json = api_call(:get,
                      "/api/v1/users/self/favorites/groups",
                      controller: "favorites",
                      action: "list_favorite_groups",
                      format: "json")
      expect(json[0]["id"]).to eql @group_fave.id
      expect(@user.favorites.size).to be(1)
    end

    it "clears favorite groups" do
      expect(@user.favorites.size).to be(1)
      group_fave_2 = Group.create!(name: "new_fave", context: @context)
      group_fave_2.add_user(@user)
      api_call(:post,
               "/api/v1/users/self/favorites/groups/#{group_fave_2.id}",
               controller: "favorites",
               action: "add_favorite_groups",
               format: "json",
               id: group_fave_2.id)
      expect(@user.favorites.size).to be(2)
      api_call(:delete,
               "/api/v1/users/self/favorites/groups",
               controller: "favorites",
               action: "reset_groups_favorites",
               format: "json")
      expect(@user.favorites.size).to be(0)
    end

    it "deletes one favorite group" do
      json = api_call(:delete,
                      "/api/v1/users/self/favorites/groups/#{@group_fave.id}",
                      controller: "favorites",
                      action: "remove_favorite_groups",
                      format: "json",
                      id: @group_fave.id)
      expect(json["context_type"]).to eql("Group")
    end

    it "lists all groups if none are favorited" do
      api_call(:delete,
               "/api/v1/users/self/favorites/groups",
               controller: "favorites",
               action: "reset_groups_favorites",
               format: "json")
      expect(@user.favorites.size).to be(0)
      json = api_call(:get,
                      "/api/v1/users/self/favorites/groups",
                      controller: "favorites",
                      action: "list_favorite_groups",
                      format: "json")
      expect(json.any?).to be
    end
  end

  it "ignores master courses if requested" do
    MasterCourses::MasterTemplate.set_as_master_course(@courses[0])
    json = api_call(:get, "/api/v1/users/self/favorites/courses", controller: "favorites", action: "list_favorite_courses", format: "json")
    expect(json.size).to be(6)

    json = api_call(:get,
                    "/api/v1/users/self/favorites/courses?exclude_blueprint_courses=1",
                    controller: "favorites",
                    action: "list_favorite_courses",
                    format: "json",
                    exclude_blueprint_courses: "1")
    expect(json.size).to be(5)
  end
end
