# frozen_string_literal: true

require "rails_helper"

RSpec.describe Question, type: :model do

  it_behaves_like "VersionableModel"

  context "validations" do

    it { is_expected.to validate_presence_of(:text)
          .with_message("for 'Question text' can't be blank.") }

    it { is_expected.to validate_presence_of(:number) }

    it "validates uniqueness of number" do
      subject.versionable_id = SecureRandom.uuid
      expect(subject).to validate_uniqueness_of(:number)
                          .scoped_to(:section_id)
                          .with_message("must be unique")
    end

    it { is_expected.to validate_presence_of(:section).on(:update) }


    it { is_expected.to validate_presence_of(:question_format) }

    it { is_expected.to allow_values(true, false).for(:option_comment_display) }

    it { is_expected.to allow_value(nil).for(:option_comment_display) }

    it { is_expected.to allow_values(true, false).for(:modifiable) }

    it { is_expected.to allow_value(nil).for(:modifiable) }

  end

  context "associations" do

    it { is_expected.to belong_to :section }

    it { is_expected.to belong_to :question_format }

    it { is_expected.to have_one :phase }

    it { is_expected.to have_one :template }

    it { is_expected.to have_many :answers }

    it { is_expected.to have_many :question_options }

    it { is_expected.to have_many :annotations }

    it { is_expected.to have_and_belong_to_many(:themes)
                          .join_table("questions_themes") }

  end


  describe "#to_s" do

    before do
      subject.text = "foo bar"
    end

    it "returns the Question's text" do
      expect(subject.to_s).to eql("foo bar")
    end

  end

  describe "#option_based?" do

    subject { question_format.option_based? }

    context "when QuestionFormat is option_based and has at least one option" do

      let!(:question_format) { create(:question_format, option_based: true) }
      let!(:question) { create(:question, question_format: question_format, options: 1) }

      it { is_expected.to eql(true) }

    end

    context "when QuestionFormat is option_based and has no option" do

      let!(:question_format) { create(:question_format, option_based: true) }

      it {
        expect {
          create(:question, question_format: question_format, options: 0)
        }.to raise_error(ActiveRecord::RecordInvalid,
         "Validation failed: You must have at least one option with accompanying text."
        )
      }

      it { is_expected.to eql(true) }

    end

    context "when QuestionFormat is not option_based" do

      let!(:question_format) { create(:question_format, option_based: false) }
      let!(:question) { create(:question, question_format: question_format) }

      it { is_expected.to eql(false) }

    end
  end

  describe "#deep_copy" do

    let!(:question) { create(:question,
      default_value: "foo bar",
      modifiable: true,
      number: 12,
      option_comment_display: false,
      text: "How many foos can bar?",
    ) }

    let!(:options) { Hash.new }

    subject { question.deep_copy(options) }

    context "when no options are provided" do

      it "doesn't persist the record" do
        expect(subject).to be_new_record
      end

      it "copies default_value from original Question" do
        expect(subject.default_value).to eql("foo bar")
      end

      it "copies modifiable from original Question" do
        expect(subject.modifiable).to eql(true)
        question.modifiable = false
        expect(question.deep_copy.modifiable).to eql(false)
      end

      it "copies number from original Question" do
        expect(subject.number).to eql(12)
      end

      it "copies option_comment_display from original Question" do
        expect(subject.option_comment_display).to eql(false)
      end

      it "copies text from original Question" do
        expect(subject.text).to eql("How many foos can bar?")
      end

      it "copies question_format_id from original Question" do
        expect(subject.question_format_id).to eql(question.question_format_id)
      end

      it "sets section_id to nil" do
        expect(subject.section_id).to be_nil
      end

    end

    context "when modifiable option provided" do

      let!(:options) { { modifiable: true } }

      it "copies modifiable from option" do
        expect(subject.modifiable).to eql(true)
        question.modifiable = false
        expect(question.deep_copy.modifiable).to eql(false)
      end

      it "ignores the original record's value" do
        question.modifiable = false
        expect(question.deep_copy(options).modifiable).to eql(true)
      end

    end

    context "when section_id option provided" do

      let!(:section) { create(:section) }

      let!(:options) { { section_id: section.id } }

      it "sets the section_id attribute" do
        expect(subject.section_id).to eql(section.id)
      end

    end

    context "when save option provided" do

      let!(:options) { { save: true } }

      it "persists the record to the database" do
        expect(subject).to be_persisted
      end
    end
  end

  describe "#example_answers" do

    subject { question.example_answers([org.id]) }

    let!(:question) { create(:question) }

    let!(:org) { create(:org) }

    context "when belongs to Org and type 'Example answer'" do

      let!(:annotation) do
        create(:annotation, question: question, org: org,
                            type: Annotation.types[:example_answer])
      end

      it { is_expected.to include(annotation) }

    end

    context "when belongs to Org and type 'Guidance'" do

      let!(:annotation) do
        create(:annotation, question: question, org: org,
                            type: Annotation.types[:guidance])
      end

      it { is_expected.not_to include(annotation) }

    end

    context "when belongs to other Org and type 'Example answer'" do

      let!(:annotation) do
        create(:annotation, question: question,
                            type: Annotation.types[:guidance])
      end

      it { is_expected.not_to include(annotation) }

    end

  end

  describe "#guidance_annotation" do

    subject { question.guidance_annotation(org.id) }

    let!(:question) { create(:question) }

    let!(:org) { create(:org) }

    context "when Annotation type is 'guidance' and belongs to Org" do

      let!(:annotation) do
        create(:annotation,
                 org: org,
                 question: question,
                 type: Annotation.types[:guidance])
      end

      it { is_expected.to eql(annotation) }

    end

    context "when Annotation type is 'Example Answer' and belongs to Org" do

      let!(:annotation) do
        create(:annotation,
                 org: org,
                 question: question,
                 type: Annotation.types[:example_answer])
      end

      it { is_expected.to be_nil }

    end

    context "when Annotation type is 'guidance' and doesn't belong to Org" do

      let!(:annotation) do
        create(:annotation,
                 question: question,
                 type: Annotation.types[:guidance])
      end

      it { is_expected.to be_nil }

    end

    context "when Annotation type 'Example Answer' and doesn't belong to Org" do

      let!(:annotation) do
        create(:annotation,
                 question: question,
                 type: Annotation.types[:example_answer])
      end

      it { is_expected.to be_nil }

    end

  end

  describe "#annotations_per_org" do

    subject { question.annotations_per_org(org.id) }

    let!(:org) { create(:org) }

    let!(:question) { create(:question) }

    context "when example answer already present" do

      before do
        create(:annotation, type: Annotation.types[:example_answer],
                            org: org, question: question)
      end

      it "returns the existing annotation" do
        expect(subject.first).to be_persisted
      end

      it "returns example answer" do
        expect(subject.first).to be_example_answer
      end

    end

    context "when example answer not present" do

      it "returns the existing annotation" do
        expect(subject.first).to be_new_record
      end

      it "returns example answer" do
        expect(subject.first).to be_example_answer
      end

    end

    context "when guidance already present" do

      before do
        create(:annotation, type: Annotation.types[:guidance],
                            org: org, question: question)
      end

      it "returns the existing annotation" do
        expect(subject.last).to be_persisted
      end

      it "returns example answer" do
        expect(subject.last).to be_guidance
      end

    end

    context "when guidance not present" do

      it "returns the existing annotation" do
        expect(subject.last).to be_new_record
      end

      it "returns example answer" do
        expect(subject.last).to be_guidance
      end

    end

  end

end
