# frozen_string_literal: true

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

require_relative "../../helpers/gradebook_common"
require_relative "../../helpers/groups_common"
require_relative "../pages/gradebook_page"
require_relative "../pages/gradebook_cells_page"

describe "Excuse an Assignment" do
  include_context "in-process server selenium tests"
  include GradebookCommon
  include GroupsCommon

  before do |example|
    unless example.metadata[:group]
      course_with_teacher_logged_in
      course_with_student(course: @course, active_all: true, name: "Student")
    end
  end

  context "Student view details" do
    before do
      @assignment = @course.assignments.create! title: "Excuse Me", submission_types: "online_text_entry", points_possible: 20
      @assignment.grade_student @student, excuse: true, grader: @teacher

      user_session @student
    end

    it "Assignment index displays scores as excused", priority: "1" do
      get "/courses/#{@course.id}/assignments"
      wait_for_ajaximations
      expect(f('[id^="assignment_"] span.non-screenreader').text).to eq "Excused"
    end

    it "Assignment details displays scores as excused", priority: "1" do
      get "/courses/#{@course.id}/assignments/#{@assignment.id}"
      wait_for_ajaximations
      expect(f("#sidebar_content .details .header").text).to eq "Excused!"
    end

    it "Submission details displays scores as excused", priority: "1" do
      get "/courses/#{@course.id}/assignments/#{@assignment.id}/submissions/#{@student.id}"
      wait_for_ajaximations
      expect(f("#content span.entered_grade").text).to eq "Excused"
    end
  end

  it "Gradebook export accounts for excused assignment", priority: "1" do
    assignment = @course.assignments.create! title: "Excuse Me", points_possible: 20
    assignment.grade_student @student, excuse: true, grader: @teacher

    csv = CSV.parse(GradebookExporter.new(@course, @teacher).to_csv)
    _name, _id, _section, _sis_login_id, score = csv[-1]
    expect(score).to eq "EX"
  end

  context "SpeedGrader" do
    it "can excuse complete/incomplete assignments", priority: "1" do
      assignment = @course.assignments.create! title: "Excuse Me", points_possible: 20, grading_type: "pass_fail"

      get "/courses/#{@course.id}/gradebook/speed_grader?assignment_id=#{assignment.id}"
      click_option f("#grading-box-extended"), "Excused"

      get "/courses/#{@course.id}/grades"
      expect(Gradebook::Cells.get_grade(@course.students[0], assignment)).to eq "Excused"
      expect(Gradebook::Cells.grading_cell(@course.students[0], assignment)).to contain_css(".excused")
    end

    it "excuses an assignment properly", priority: "1" do
      a1 = @course.assignments.create! title: "Excuse Me", points_possible: 20
      a2 = @course.assignments.create! title: "Don't Excuse Me", points_possible: 10
      a1.grade_student(@student, grade: 20, grader: @teacher)
      a2.grade_student(@student, grade: 5, grader: @teacher)

      get "/courses/#{@course.id}/gradebook/speed_grader?assignment_id=#{a2.id}"
      replace_content f("#grading-box-extended"), "EX", press_return: true

      get "/courses/#{@course.id}/grades"

      expect(Gradebook::Cells.get_grade(@student, a1)).to eq "20"

      # this should show 'EX' and have excused class
      expect(Gradebook::Cells.get_grade(@student, a2)).to eq "Excused"
      expect(Gradebook::Cells.grading_cell(@student, a2)).to contain_css(".excused")

      expect(Gradebook::Cells.get_total_grade(@student)).to eq "100%"
    end

    it "indicates excused assignment as graded", priority: "1" do
      assignment = @course.assignments.build
      assignment.publish

      assignment.grade_student(@student, excuse: true, grader: @teacher)

      get "/courses/#{@course.id}/gradebook/speed_grader?assignment_id=#{assignment.id}"
      expect(f("#combo_box_container .ui-selectmenu-item-icon i")).to have_class "icon-check"
      expect(f("#combo_box_container .ui-selectmenu-item-header").text).to eq "Student"
    end

    it "excuses a checkpointed discussion correctly", :ignore_js_errors do
      user_session(@teacher)
      @course.account.enable_feature!(:discussion_checkpoints)
      reply_to_topic, reply_to_entry, dt = graded_discussion_topic_with_checkpoints(context: @course)

      get "/courses/#{@course.id}/gradebook/speed_grader?assignment_id=#{dt.assignment.id}&student_id=#{@student.id}"

      expect(reply_to_topic.submissions.find_by(user: @student).excused).to be_nil
      expect(reply_to_entry.submissions.find_by(user: @student).excused).to be_nil

      reply_to_topic_select = f("[data-testid='reply_to_topic-checkpoint-status-select']")
      reply_to_topic_select.click
      fj("span[role='option']:contains('Excused')").click

      reply_to_entry_select = f("[data-testid='reply_to_entry-checkpoint-status-select']")
      reply_to_entry_select.click
      fj("span[role='option']:contains('Excused')").click

      expect(reply_to_topic.submissions.find_by(user: @student).excused).to be true
      expect(reply_to_entry.submissions.find_by(user: @student).excused).to be true

      driver.navigate.refresh

      expect(f("[data-testid='reply_to_topic-checkpoint-status-select']")).to have_attribute("value", "Excused")
      expect(f("[data-testid='reply_to_entry-checkpoint-status-select']")).to have_attribute("value", "Excused")
    end
  end

  shared_examples "Basic Behavior" do
    it "formats excused grade like dropped assignment", priority: "1" do
      assignment = @course.assignments.create! title: "Excuse Me", points_possible: 20
      assignment.grade_student(@student, excuse: true, grader: @teacher)
      user_session(@student)
      get "/courses/#{@course.id}/grades"

      grade_row = f("#submission_#{assignment.id}")
      grade_cell = f(".assignment_score .grade", grade_row)
      grade = grade_cell.text.scan(/\d+|EX/).first

      expect(grade_row).to have_class ".excused"
      expect(grade).to eq "EX"
      expect(grade_row).to have_attribute("title", "This assignment is excused " \
                                                   "and will not be considered in the total calculation")
    end

    %w[percent letter_grade gpa_scale points].each do |type|
      it "is not included in grade calculations with type '#{type}'", priority: "1" do
        a1 = @course.assignments.create! title: "Excuse Me", grading_type: type, points_possible: 20
        a2 = @course.assignments.create! title: "Don't Excuse Me", grading_type: type, points_possible: 20

        if type == "points"
          a1.grade_student(@student, grade: 13.2, grader: @teacher)
          a2.grade_student(@student, grade: 20, grader: @teacher)
        else
          a1.grade_student(@student, grade: "66%", grader: @teacher)
          a2.grade_student(@student, grade: "100%", grader: @teacher)
        end

        get "/courses/#{@course.id}/gradebook/"
        total = Gradebook::Cells.get_total_grade(@course.students[0])
        expect(total).to eq "83%"

        Gradebook::Cells.edit_grade(@course.students[0], a1, "EX")
        wait_for_ajaximations
        total = Gradebook::Cells.get_total_grade(@course.students[0])
        expect(total).to eq "100%"
      end
    end
  end

  context "Gradebook Grid" do
    it_behaves_like "Basic Behavior"

    it "default grade cannot be set to excused", priority: "1" do
      assignment = @course.assignments.create! title: "Test Me!", points_possible: 20
      get "/courses/#{@course.id}/grades"
      Gradebook.click_assignment_header_menu_element(assignment.id, "set default grade")

      %w[EX eX Ex ex].each_with_index do |ex, i|
        replace_content f("#student_grading_#{assignment.id}"), ex, press_return: true
        wait_for_ajaximations
        expect(ff(".ic-flash-error").length).to be i + 1
        expect(f(".ic-flash-error").text).to include "Default grade cannot be set to EX"
      end
    end

    %w[EX ex Ex eX].each do |ex|
      it "'#{ex}' can be used to excuse assignments", priority: "1" do
        assignment = @course.assignments.create! title: "Excuse Me", points_possible: 20

        get "/courses/#{@course.id}/gradebook/"

        Gradebook::Cells.edit_grade(@course.students[0], assignment, ex)
        expect { Gradebook::Cells.get_grade(@course.students[0], assignment) }.to become "Excused"
      end
    end
  end
end
