# frozen_string_literal: true

#
# Copyright (C) 2014 - 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 "course_copy_helper"

describe ContentMigration do
  context "course copy outcomes" do
    include_context "course copy"

    it "copies all learning outcomes and their groups if selected" do
      default = @copy_from.root_outcome_group
      log = @copy_from.learning_outcome_groups.new
      log.context = @copy_from
      log.title = "outcome group"
      log.description = "<p>Groupage</p>"
      log.save!
      default.adopt_outcome_group(log)

      lo = @copy_from.created_learning_outcomes.new
      lo.context = @copy_from
      lo.short_description = "outcome1"
      lo.workflow_state = "active"
      lo.data = { rubric_criterion: { mastery_points: 2, ratings: [{ description: "e", points: 50 }, { description: "me", points: 2 }, { description: "Does Not Meet Expectations", points: 0.5 }], description: "First outcome", points_possible: 5 } }
      lo.save!

      log.add_outcome(lo)

      @cm.copy_options = {
        all_learning_outcomes: "1"
      }
      @cm.save!

      run_course_copy

      expect(@copy_to.created_learning_outcomes.where(migration_id: mig_id(lo)).first).not_to be_nil
      expect(@copy_to.learning_outcome_groups.where(migration_id: mig_id(log)).first).not_to be_nil
    end

    it "copies learning outcome alignments with question banks" do
      skip unless Qti.qti_enabled?
      default = @copy_from.root_outcome_group
      lo = @copy_from.created_learning_outcomes.new
      lo.context = @copy_from
      lo.short_description = "outcome1"
      lo.workflow_state = "active"
      lo.data = { rubric_criterion: { mastery_points: 2, ratings: [{ description: "e", points: 50 }, { description: "me", points: 2 }, { description: "Does Not Meet Expectations", points: 0.5 }], description: "First outcome", points_possible: 5 } }
      lo.save!
      default.add_outcome(lo)

      bank = @copy_from.assessment_question_banks.create!(title: "bank")
      bank.assessment_questions.create!(question_data: { "name" => "test question", "question_type" => "essay_question" })

      lo.align(bank, @copy_from, { mastery_type: "points", mastery_score: 50.0 })

      run_course_copy

      new_lo = @copy_to.learning_outcomes.where(migration_id: mig_id(lo)).first
      new_bank = @copy_to.assessment_question_banks.where(migration_id: mig_id(bank)).first

      expect(new_lo.alignments.count).to eq 1
      new_alignment = new_lo.alignments.first

      expect(new_alignment.content).to eq new_bank
      expect(new_alignment.context).to eq @copy_to

      expect(new_alignment.tag).to eq "points_mastery"
      expect(new_alignment.mastery_score).to eq 50.0
    end

    it "copies learning outcomes into the new course" do
      old_root = @copy_from.root_outcome_group

      lo = create_outcome(@copy_from, old_root)
      lo.calculation_method = "n_mastery"
      lo.calculation_int = 2
      lo.save!

      log = @copy_from.learning_outcome_groups.new
      log.context = @copy_from
      log.title = "An outcome group"
      log.description = "<p>Groupage</p>"
      log.save!
      old_root.adopt_outcome_group(log)

      lo2 = create_outcome(@copy_from, log)

      log_sub = @copy_from.learning_outcome_groups.new
      log_sub.context = @copy_from
      log_sub.title = "Sub group"
      log_sub.description = "<p>SubGroupage</p>"
      log_sub.save!
      log.adopt_outcome_group(log_sub)

      log_sub2 = @copy_from.learning_outcome_groups.new
      log_sub2.context = @copy_from
      log_sub2.title = "Sub group2"
      log_sub2.description = "<p>SubGroupage2</p>"
      log_sub2.save!
      log_sub.adopt_outcome_group(log_sub2)

      lo3 = create_outcome(@copy_from, log_sub2)

      # copy outcomes into new course
      new_root = @copy_to.root_outcome_group

      run_course_copy

      expect(@copy_to.created_learning_outcomes.count).to eq @copy_from.created_learning_outcomes.count
      expect(@copy_to.learning_outcome_groups.count).to eq @copy_from.learning_outcome_groups.count
      expect(new_root.child_outcome_links.count).to eq old_root.child_outcome_links.count
      expect(new_root.child_outcome_groups.count).to eq old_root.child_outcome_groups.count

      lo_new = new_root.child_outcome_links.first.content
      expect(lo_new.short_description).to eq lo.short_description
      expect(lo_new.description).to eq lo.description
      expect(lo_new.data).to eq lo.data
      expect(lo_new.calculation_method).to eq lo.calculation_method
      expect(lo_new.calculation_int).to eq lo.calculation_int

      log_new = new_root.child_outcome_groups.first
      expect(log_new.title).to eq log.title
      expect(log_new.description).to eq log.description
      expect(log_new.child_outcome_links.length).to eq 1

      lo_new = log_new.child_outcome_links.first.content
      expect(lo_new.short_description).to eq lo2.short_description
      expect(lo_new.description).to eq lo2.description
      expect(lo_new.data).to eq lo2.data

      log_sub_new = log_new.child_outcome_groups.first
      expect(log_sub_new.title).to eq log_sub.title
      expect(log_sub_new.description).to eq log_sub.description

      log_sub2_new = log_sub_new.child_outcome_groups.first
      expect(log_sub2_new.title).to eq log_sub2.title
      expect(log_sub2_new.description).to eq log_sub2.description

      lo3_new = log_sub2_new.child_outcome_links.first.content
      expect(lo3_new.short_description).to eq lo3.short_description
      expect(lo3_new.description).to eq lo3.description
      expect(lo3_new.data).to eq lo3.data
    end

    it "does not copy deleted learning outcomes into the new course" do
      old_root = @copy_from.root_outcome_group

      log = @copy_from.learning_outcome_groups.new
      log.context = @copy_from
      log.title = "An outcome group"
      log.description = "<p>Groupage</p>"
      log.save!
      old_root.adopt_outcome_group(log)

      lo = create_outcome(@copy_from, log)
      lo2 = create_outcome(@copy_from, log)
      lo2.destroy

      run_course_copy

      expect(@copy_to.created_learning_outcomes.count).to eq 1
      expect(@copy_to.created_learning_outcomes.first.migration_id).to eq mig_id(lo)
    end

    it "relinks to external outcomes" do
      account = @copy_from.account
      a_group = account.root_outcome_group

      root_group = LearningOutcomeGroup.create!(title: "contextless group")

      lo = create_outcome(nil, root_group)

      lo2 = create_outcome(account, a_group)

      from_root = @copy_from.root_outcome_group
      from_root.add_outcome(lo)
      from_root.add_outcome(lo2)

      run_course_copy

      to_root = @copy_to.root_outcome_group
      expect(to_root.child_outcome_links.count).to eq 2
      expect(to_root.child_outcome_links.where(content_id: lo.id).first).not_to be_nil
      expect(to_root.child_outcome_links.where(content_id: lo2.id).first).not_to be_nil
    end

    it "copies external learning outcomes linked to banks correctly" do
      account = @copy_from.account
      a_group = account.root_outcome_group
      lo = create_outcome(account, a_group)

      root = @copy_from.root_outcome_group
      log = @copy_from.learning_outcome_groups.create!(title: "some group")
      root.adopt_outcome_group(log)
      log.add_outcome(lo)

      bank = @copy_from.assessment_question_banks.create!(title: "bank")
      bank.assessment_questions.create!(question_data: { "name" => "test question", "question_type" => "essay_question" })

      lo.align(bank, @copy_from, { mastery_type: "points", mastery_score: 50.0 })

      run_course_copy

      to_log = @copy_to.learning_outcome_groups.where(migration_id: mig_id(log)).first
      expect(to_log.child_outcome_links.where(content_id: lo.id).first).not_to be_nil

      to_root = @copy_to.root_outcome_group
      expect(to_root.child_outcome_links.count).to eq 0
    end

    it "creates outcomes in new course if external context not found" do
      hash = { "is_global_outcome" => true,
               "points_possible" => nil,
               "type" => "learning_outcome",
               "ratings" => [],
               "description" => nil,
               "mastery_points" => nil,
               "external_identifier" => "0",
               "title" => "root outcome",
               "migration_id" => "id1072dcf40e801c6468d9eaa5774e56d" }

      @cm.outcome_to_id_map = {}
      @cm.copied_external_outcome_map = {}
      Importers::LearningOutcomeImporter.import_from_migration(hash, @cm)

      expect(@cm.warnings).to eq ["The external Learning Outcome couldn't be found for \"root outcome\", creating a copy."]

      to_root = @copy_to.root_outcome_group
      expect(to_root.child_outcome_links.count).to eq 1
      new_lo = to_root.child_outcome_links.first.content
      expect(new_lo.id).not_to eq 0
      expect(new_lo.short_description).to eq hash["title"]
    end

    it "creates rubrics in new course if external context not found" do
      hash = {
        "reusable" => false,
        "public" => false,
        "hide_score_total" => nil,
        "free_form_criterion_comments" => nil,
        "points_possible" => nil,
        "data" => [{ "id" => "1",
                     "description" => "Outcome row",
                     "long_description" => nil,
                     "points" => 3,
                     "mastery_points" => nil,
                     "title" => "Outcome row",
                     "ratings" => [{ "description" => "Rockin'",
                                     "id" => "2",
                                     "criterion_id" => "1",
                                     "points" => 3 }] }],
        "read_only" => false,
        "description" => nil,
        "external_identifier" => "0",
        "title" => "root rubric",
        "migration_id" => "id1072dcf40e801c6468d9eaa5774e56d"
      }

      @cm.outcome_to_id_map = {}
      Importers::RubricImporter.import_from_migration(hash, @cm)

      expect(@cm.warnings).to be_empty

      new_rubric = @copy_to.rubrics.first
      expect(new_rubric.id).not_to eq 0
      expect(new_rubric.title).to eq hash["title"]
    end

    it "links rubric (and assignments) to outcomes" do
      root_group = LearningOutcomeGroup.create!(title: "contextless group")

      lo = create_outcome(nil, root_group)
      lo2 = create_outcome(@copy_from)

      from_root = @copy_from.root_outcome_group
      from_root.add_outcome(lo)
      from_root.add_outcome(lo2)

      rub = Rubric.new(context: @copy_from)
      rub.data = [
        {
          points: 3,
          description: "Outcome row",
          id: 1,
          ratings: [{ points: 3, description: "Rockin'", criterion_id: 1, id: 2 }],
          learning_outcome_id: lo.id
        },
        {
          points: 3,
          description: "Outcome row 2",
          id: 2,
          ratings: [{ points: 3, description: "lame'", criterion_id: 2, id: 3 }],
          ignore_for_scoring: true,
          learning_outcome_id: lo2.id
        }
      ]
      rub.save!
      rub.associate_with(@copy_from, @copy_from)

      from_assign = @copy_from.assignments.create!(title: "some assignment")
      rub.associate_with(from_assign, @copy_from, purpose: "grading")

      run_course_copy

      new_lo2 = @copy_to.created_learning_outcomes.where(migration_id: mig_id(lo2)).first
      to_rub = @copy_to.rubrics.first
      to_assign = @copy_to.assignments.first

      expect(to_rub.data[1]["learning_outcome_id"]).to eq new_lo2.id
      expect(to_rub.data[1]["ignore_for_scoring"]).to be true
      expect(to_rub.data[0]["learning_outcome_id"]).to eq lo.id
      expect(to_rub.learning_outcome_alignments.map(&:learning_outcome_id).sort).to eq [lo.id, new_lo2.id].sort
      expect(to_assign.learning_outcome_alignments.map(&:learning_outcome_id).sort).to eq [lo.id, new_lo2.id].sort
    end

    it "links copied account outcomes to rubrics in a cross-account copy" do
      sub1 = Account.default.sub_accounts.create!(name: "A")
      @copy_from.account = sub1
      @copy_from.save!

      lo = create_outcome(sub1)
      rub = Rubric.new(context: @copy_from)
      rub.data = [
        {
          points: 3,
          description: "foo",
          id: 1,
          ratings: [{ points: 3, description: "Expeeds Excitations", criterion_id: 1, id: 2 }],
          learning_outcome_id: lo.id
        }
      ]
      rub.save!
      rub.associate_with(@copy_from, @copy_from)

      sub2 = Account.default.sub_accounts.create!(name: "B")
      @copy_to.account = sub2
      @copy_to.save!

      warnings = [%(The external Learning Outcome couldn't be found for "#{lo.short_description}", creating a copy.)]
      run_course_copy(warnings)

      rub_to = @copy_to.rubrics.find_by(migration_id: mig_id(rub))
      loa = rub_to.learning_outcome_alignments.take
      lo_to = loa.learning_outcome
      expect(lo_to.short_description).to eq lo.short_description
      expect(lo_to.context).to eq @copy_to
    end

    it "still associates rubrics and assignments and copy rubric association properties" do
      create_rubric_asmnt
      @assoc.summary_data = { saved_comments: { "309_6312" => ["what the comment", "hey"] } }
      @assoc.save!

      run_course_copy

      rub = @copy_to.rubrics.where(migration_id: mig_id(@rubric)).first
      expect(rub).not_to be_nil

      %i[description id points].each do |k|
        expect(rub.data.first[k]).to eq @rubric.data.first[k]
      end
      %i[criterion_id description id points].each do |k|
        rub.data.first[:ratings].each_with_index do |criterion, i|
          expect(criterion[k]).to eq @rubric.data.first[:ratings][i][k]
        end
      end

      asmnt2 = @copy_to.assignments.where(migration_id: mig_id(@assignment)).first
      expect(asmnt2.rubric.id).to eq rub.id
      expect(asmnt2.rubric_association.use_for_grading).to be true
      expect(asmnt2.rubric_association.hide_score_total).to be true
      expect(asmnt2.rubric_association.summary_data).to eq @assoc.summary_data
    end

    it "copies rubrics associated with assignments when rubric isn't selected" do
      create_rubric_asmnt
      @cm.copy_options = {
        assignments: { mig_id(@assignment) => "1" },
      }
      @cm.save!
      run_course_copy

      rub = @copy_to.rubrics.where(migration_id: mig_id(@rubric)).first
      expect(rub).not_to be_nil
      asmnt2 = @copy_to.assignments.where(migration_id: mig_id(@assignment)).first
      expect(asmnt2.rubric.id).to eq rub.id
    end

    it "restores deleted learning outcome groups on re-copy" do
      default = @copy_from.root_outcome_group
      log = @copy_from.learning_outcome_groups.new
      log.context = @copy_from
      log.title = "outcome group"
      log.description = "<p>Groupage</p>"
      log.save!
      default.adopt_outcome_group(log)

      lo = @copy_from.created_learning_outcomes.new
      lo.context = @copy_from
      lo.short_description = "outcome1"
      lo.workflow_state = "active"
      lo.data = { rubric_criterion: { mastery_points: 2, ratings: [{ description: "e", points: 50 }, { description: "me", points: 2 }, { description: "Does Not Meet Expectations", points: 0.5 }], description: "First outcome", points_possible: 5 } }
      lo.save!

      log.add_outcome(lo)

      run_course_copy

      group = @copy_to.learning_outcome_groups.where(migration_id: mig_id(log)).first
      group.destroy!

      run_course_copy

      expect(group.reload).to be_active
    end
  end
end
