require "rails_helper"

describe SurveyResponsesDataLoader do
  let(:path_to_teacher_responses) { Rails.root.join("spec", "fixtures", "test_2020-21_teacher_survey_responses.csv") }
  let(:path_to_student_responses) { Rails.root.join("spec", "fixtures", "test_2020-21_student_survey_responses.csv") }
  let(:path_to_parent_responses) { Rails.root.join("spec", "fixtures", "test_2020-21_parent_survey_responses.csv") }
  let(:path_to_butler_student_responses) do
    Rails.root.join("spec", "fixtures", "test_2022-23_butler_student_survey_responses.csv")
  end

  let(:ay_2020_21) { create(:academic_year, range: "2020-21") }
  let(:ay_2022_23) { create(:academic_year, range: "2022-23") }

  let(:school) { create(:school, name: "Lee Elementary School", slug: "lee-elementary-school", dese_id: 1_500_025) }
  let(:lowell) { create(:district, name: "Lowell", slug: "lowell") }
  let(:second_school) do
    create(:school, name: "Lee Middle High School", slug: "lee-middle-high-school", dese_id: 1_500_505,
                    district: lowell)
  end
  let(:butler_school) do
    create(:school, name: "Butler Elementary School", slug: "butler-elementary-school", dese_id: 1_600_310,
                    district: lowell)
  end

  let(:student_survey_items) do
    ids = %w[ s-emsa-q1 s-emsa-q2 s-emsa-q3
              s-tint-q1 s-tint-q2 s-tint-q4 s-tint-q5
              s-acpr-q1 s-acpr-q2 s-acpr-q3 s-acpr-q4
              s-cure-q3 s-cure-q4 s-sten-q2 s-sten-q3
              s-sper-q1 s-sper-q2 s-sper-q3 s-sper-q4
              s-civp-q1 s-civp-q2 s-civp-q3 s-civp-q4 s-grmi-q1
              s-grmi-q4 s-appa-q1
              s-appa-q2 s-peff-q1
              s-peff-q2 s-peff-q3 s-peff-q4 s-peff-q5 s-peff-q6
              s-sbel-q1 s-sbel-q2 s-sbel-q3 s-sbel-q4 s-sbel-q5
              s-phys-q1 s-phys-q2 s-phys-q3 s-phys-q4
              s-vale-q1 s-vale-q2 s-vale-q3 s-vale-q4
              s-acst-q1 s-acst-q2 s-acst-q3 s-acst-q4 s-acst-q5
              s-grit-q1 s-grit-q2 s-grit-q3 s-grit-q4]

    ids.each do |id|
      create(:survey_item, survey_item_id: id)
    end
  end
  let(:parent_survey_items) do
    ids = %w[
      p-socx-q1
      p-socx-q2
      p-socx-q3
      p-socx-q4
      p-sosu-q1
      p-sosu-q2
      p-sosu-q3
      p-sosu-q4
      p-tcom-q1
      p-tcom-q2
      p-tcom-q3
      p-comm-q1
      p-comm-q2
      p-comm-q3
      p-comm-q4
      p-valm-q1
      p-valm-q2
      p-valm-q3
      p-valm-q4
      p-acpr-q1
      p-acpr-q2
      p-acpr-q3
      p-acpr-q4
    ]

    ids.each do |id|
      create(:survey_item, survey_item_id: id)
    end
  end

  let(:vestigial_parent_ids) do
    %w[
      p-scrp-q3
      p-cure-q1
      p-cure-q2
      p-cure-q3
      p-cure-q4
      p-evnt-q1
      p-evnt-q2
      p-evnt-q3
      p-evnt-q4
      p-phys-q3
      p-scrp-q1
      p-scrp-q2
    ]
  end

  let(:housings) do
    create(:housing, designation: "Own")
    create(:housing, designation: "Rent")
    create(:housing, designation: "Unknown")
  end

  let(:t_pcom_q3) { create(:survey_item, survey_item_id: "t-pcom-q3") }
  let(:t_pcom_q2) { create(:survey_item, survey_item_id: "t-pcom-q2") }
  let(:t_coll_q1) { create(:survey_item, survey_item_id: "t-coll-q1") }
  let(:t_coll_q2) { create(:survey_item, survey_item_id: "t-coll-q2") }
  let(:t_coll_q3) { create(:survey_item, survey_item_id: "t-coll-q3") }
  let(:t_sach_q1) { create(:survey_item, survey_item_id: "t-sach-q1") }
  let(:t_sach_q2) { create(:survey_item, survey_item_id: "t-sach-q2") }
  let(:t_sach_q3) { create(:survey_item, survey_item_id: "t-sach-q3") }

  let(:female) { create(:gender, qualtrics_code: 1) }
  let(:male) { create(:gender, qualtrics_code: 2) }
  let(:another_gender) { create(:gender, qualtrics_code: 3) }
  let(:non_binary) { create(:gender, qualtrics_code: 4) }
  let(:unknown_gender) { create(:gender, qualtrics_code: 99) }

  let(:low_income) { create(:income, designation: "Economically Disadvantaged – Y") }
  let(:high_income) { create(:income, designation: "Economically Disadvantaged – N") }
  let(:unknown_income) { create(:income, designation: "Unknown") }

  let(:yes_ell) { create(:ell, designation: "ELL") }
  let(:not_ell) { create(:ell, designation: "Not ELL") }
  let(:unknown_ell) { create(:ell, designation: "Unknown") }

  let(:american_indian) { create(:race, qualtrics_code: 1, designation: "American Indian or Alaskan Native") }
  let(:asian)           { create(:race, qualtrics_code: 2, designation: "Asian or Pacific Islander") }
  let(:black)           { create(:race, qualtrics_code: 3, designation: "Black or African American") }
  let(:latinx)          { create(:race, qualtrics_code: 4, designation: "Hispanic or Latinx") }
  let(:white)           { create(:race, qualtrics_code: 5, designation: "White or Caucasian") }
  let(:middle_eastern)  { create(:race, qualtrics_code: 8, designation: "Middle Eastern") }
  let(:unknown_race)    { create(:race, qualtrics_code: 99, designation: "Race/Ethnicity Not Listed") }
  let(:multiracial)     { create(:race, qualtrics_code: 100, designation: "Multiracial") }

  let(:languages) do
    create(:language, designation: "English")
    create(:language, designation: "Spanish")
    create(:language, designation: "Portuguese")
    create(:language, designation: "Unknown")
  end

  let(:education_list) do
    designations = [
      "No formal schooling completed",
      "Some formal schooling",
      "High school diploma or GED",
      "Associates Degree",
      "Bachelors Degree",
      "Masters Degree",
      "Professional Degree",
      "Doctoral Degree",
      "Unknown"
    ]
    designations.map do |designation|
      create(:education, designation:)
    end
  end

  let(:employment_list) do
    designations = [
      "Two adults with full-time employment",
      "One adult with full-time employment",
      "Two adults with part-time employment",
      "One adult with part-time employment",
      "No full-time or part-time employment",
      "Unknown"
    ]
    designations.map do |designation|
      create(:employment, designation:)
    end
  end

  let(:benefit_list) do
    designations = [
      "Yes",
      "No",
      "Unknown"
    ]
    designations.map do |designation|
      create(:benefit, designation:)
    end
  end

  let(:setup) do
    ay_2020_21
    ay_2022_23
    school
    second_school
    butler_school
    housings
    t_pcom_q3
    t_pcom_q2
    t_coll_q1
    t_coll_q2
    t_coll_q3
    t_sach_q1
    t_sach_q2
    t_sach_q3
    student_survey_items

    female
    male
    another_gender
    non_binary
    unknown_gender

    low_income
    high_income
    unknown_income

    yes_ell
    not_ell
    unknown_ell

    american_indian
    asian
    black
    latinx
    white
    middle_eastern
    unknown_race
    multiracial

    languages
    education_list
    employment_list
    benefit_list
  end

  before :each do
    setup
  end

  describe "loading teacher survey responses" do
    before do
      SurveyResponsesDataLoader.new.load_data filepath: path_to_teacher_responses
    end

    it "ensures teacher responses load correctly" do
      assigns_academic_year_to_survey_item_responses
      assigns_school_to_the_survey_item_responses
      assigns_recorded_date_to_teacher_responses
      loads_survey_item_responses_for_a_given_survey_response
      loads_all_survey_item_responses_for_a_given_survey_item
      captures_likert_scores_for_survey_item_responses
      is_idempotent
    end
  end

  describe "student survey responses" do
    before do
      SurveyResponsesDataLoader.new.load_data filepath: path_to_student_responses
    end

    it "ensures student responses load correctly" do
      assigns_academic_year_to_student_survey_item_responses
      assigns_school_to_student_survey_item_responses
      assigns_recorded_date_to_student_responses
      loads_student_survey_item_response_values
      student_survey_item_response_count_matches_expected
      captures_likert_scores_for_student_survey_item_responses
      assigns_grade_level_to_responses
      assigns_gender_to_responses
      assigns_income_to_responses
      assigns_ell_to_responses
      assigns_races_to_students
      is_idempotent_for_students
    end

    context "when updating student survey responses from another csv file" do
      before :each do
        SurveyResponsesDataLoader.new.load_data filepath: Rails.root.join("spec", "fixtures",
                                                                          "secondary_test_2020-21_student_survey_responses.csv")
      end
      it "updates the likert score to the score on the new csv file" do
        s_emsa_q1 = SurveyItem.find_by_survey_item_id "s-emsa-q1"
        s_acst_q3 = SurveyItem.find_by_survey_item_id "s-acst-q3"
        expect(SurveyItemResponse.where(response_id: "student_survey_response_3",
                                        survey_item: s_emsa_q1).first.likert_score).to eq 1
        expect(SurveyItemResponse.where(response_id: "student_survey_response_4",
                                        survey_item: s_emsa_q1).first.likert_score).to eq 1
        expect(SurveyItemResponse.where(response_id: "student_survey_response_5",
                                        survey_item: s_emsa_q1).first.likert_score).to eq 1

        expect(SurveyItemResponse.where(response_id: "student_survey_response_5",
                                        survey_item: s_acst_q3).first.likert_score).to eq 4
      end
    end
  end

  describe "parent survey responses" do
    before do
      school
      ay_2020_21
      parent_survey_items
      SurveyResponsesDataLoader.new.load_data filepath: path_to_parent_responses
    end

    it "adds only the surveyitems that exist in source of truth" do
      si = (SurveyItemResponse.where(school:, response_id: "parent_survey_response_1").map do |response|
        response.survey_item.survey_item_id
      end)
      response_ids = %w[
        parent_survey_response_1
        parent_survey_response_2
        parent_survey_response_3
        parent_survey_response_4
        parent_survey_response_5
        parent_survey_response_6
      ]
      response_ids.each do |id|
        expect(SurveyItemResponse.where(response_id: id,
                                        survey_item: SurveyItem.parent_survey_items).count).to eq 23
      end

      expect(SurveyItemResponse.where(response_id: "parent_survey_response_8").count).to eq 0
    end

    it "does not add surveyitems from questions that have been disabled" do
      vestigial_parent_ids.each do |id|
        expect(SurveyItemResponse.where(school:, survey_item: id).count).to eq 0
      end
    end

    it "loads the correct set of races for parents" do
      assigns_races_to_parents
    end

    it "loads the correct set of genders for parents" do
      assigns_genders_to_parents
    end

    it "assigns the correct education for parents" do
      results = { "parent_survey_response_1" => "No formal schooling completed",
                  "parent_survey_response_2" => "Some formal schooling",
                  "parent_survey_response_3" => "High school diploma or GED",
                  "parent_survey_response_4" => "Associates Degree",
                  "parent_survey_response_5" => "Bachelors Degree",
                  "parent_survey_response_6" => "Unknown",
                  "parent_survey_response_7" => "Unknown" }

      results.each do |key, value|
        expect(SurveyItemResponse.where(response_id: key).first.parent.education.designation).to eq value
      end
    end
  end

  describe ".socio_economic_score" do
    it "returns 0 when none of the rubrics meet the standard for higher advantage" do
      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "No formal schooling completed"), Benefit.new(designation: "Yes"), [Employment.new(designation: "No full-time or part-time employment")])
      expect(score).to eq 0
    end

    it "returns 1 when one of the rubrics meets the standard for higher advantage" do
      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "Yes"), [Employment.new(designation: "No full-time or part-time employment")])
      expect(score).to eq 1
    end

    it "returns 2 when two of the rubrics meet the standard for higher advantage" do
      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "No"), [Employment.new(designation: "No full-time or part-time employment")])
      expect(score).to eq 2
    end

    it "returns 3 when all three of the rubrics meet the standard for higher advantage" do
      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "No"), [Employment.new(designation: "Two adults with full-time employment")])
      expect(score).to eq 3

      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "No"), [Employment.new(designation: "One adult with full-time employment"), Employment.new(designation: "Two adults with full-time employment")])
      expect(score).to eq 3
    end

    it "returns -1 when any aspect of the data is unknown" do
      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "Unknown"), [Employment.new(designation: "Two adults with full-time employment")])
      expect(score).to eq(-1)

      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "No"), [Employment.new(designation: "Unknown")])
      expect(score).to eq(-1)

      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "Unknown"), Benefit.new(designation: "No"), [Employment.new(designation: "No full-time or part-time employment")])
      expect(score).to eq(-1)

      score = SocioEconomicCalculator.socio_economic_score(Education.new(designation: "Unknown"), Benefit.new(designation: "No"), [])
      expect(score).to eq(-1)
    end
  end
end

def assigns_academic_year_to_survey_item_responses
  expect(SurveyItemResponse.find_by_response_id("teacher_survey_response_1").academic_year).to eq ay_2020_21
end

def assigns_school_to_the_survey_item_responses
  expect(SurveyItemResponse.find_by_response_id("teacher_survey_response_1").school).to eq school
end

def loads_survey_item_responses_for_a_given_survey_response
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").count).to eq 5
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").count).to eq 0
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").count).to eq 8
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").count).to eq 8
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").count).to eq 8
end

def loads_all_survey_item_responses_for_a_given_survey_item
  expect(SurveyItemResponse.where(survey_item: t_pcom_q2).count).to eq 3
  expect(SurveyItemResponse.where(survey_item: t_pcom_q3).count).to eq 4
end

def captures_likert_scores_for_survey_item_responses
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").where(survey_item: t_pcom_q2)).to be_empty
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").where(survey_item: t_pcom_q3).first.likert_score).to eq 3

  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").where(survey_item: t_pcom_q2)).to be_empty
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").where(survey_item: t_pcom_q3)).to be_empty

  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").where(survey_item: t_pcom_q2).first.likert_score).to eq 5
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").where(survey_item: t_pcom_q3).first.likert_score).to eq 5

  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").where(survey_item: t_pcom_q2).first.likert_score).to eq 4
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").where(survey_item: t_pcom_q3).first.likert_score).to eq 4

  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").where(survey_item: t_pcom_q2).first.likert_score).to eq 2
  expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").where(survey_item: t_pcom_q3).first.likert_score).to eq 4
end

def is_idempotent
  number_of_survey_item_responses = SurveyItemResponse.count

  SurveyResponsesDataLoader.new.load_data filepath: path_to_teacher_responses

  expect(SurveyItemResponse.count).to eq number_of_survey_item_responses
end

def assigns_academic_year_to_student_survey_item_responses
  expect(SurveyItemResponse.find_by_response_id("student_survey_response_3").academic_year).to eq ay_2020_21
end

def assigns_school_to_student_survey_item_responses
  expect(SurveyItemResponse.find_by_response_id("student_survey_response_3").school).to eq second_school
end

def loads_student_survey_item_response_values
  expect(SurveyItemResponse.where(response_id: "student_survey_response_1").count).to eq 3
  expect(SurveyItemResponse.where(response_id: "student_survey_response_2").count).to eq 0
  expect(SurveyItemResponse.where(response_id: "student_survey_response_3").count).to eq 30
  expect(SurveyItemResponse.where(response_id: "student_survey_response_4").count).to eq 27
  expect(SurveyItemResponse.where(response_id: "student_survey_response_5").count).to eq 30
end

def student_survey_item_response_count_matches_expected
  s_phys_q1 = SurveyItem.where(survey_item_id: "s-phys-q1")
  s_phys_q2 = SurveyItem.where(survey_item_id: "s-phys-q2")
  expect(SurveyItemResponse.where(survey_item: s_phys_q1).count).to eq 6
  expect(SurveyItemResponse.where(survey_item: s_phys_q2).count).to eq 5
end

def captures_likert_scores_for_student_survey_item_responses
  s_phys_q1 = SurveyItem.where(survey_item_id: "s-phys-q1")
  s_phys_q2 = SurveyItem.where(survey_item_id: "s-phys-q2")
  expect(SurveyItemResponse.where(response_id: "student_survey_response_1").where(survey_item: s_phys_q1).first.likert_score).to eq 3
  expect(SurveyItemResponse.where(response_id: "student_survey_response_1").where(survey_item: s_phys_q2)).to be_empty

  expect(SurveyItemResponse.where(response_id: "student_survey_response_2").where(survey_item: s_phys_q1)).to be_empty
  expect(SurveyItemResponse.where(response_id: "student_survey_response_2").where(survey_item: s_phys_q2)).to be_empty

  expect(SurveyItemResponse.where(response_id: "student_survey_response_3").where(survey_item: s_phys_q1).first.likert_score).to eq 1
  expect(SurveyItemResponse.where(response_id: "student_survey_response_3").where(survey_item: s_phys_q2).first.likert_score).to eq 3

  expect(SurveyItemResponse.where(response_id: "student_survey_response_4").where(survey_item: s_phys_q1).first.likert_score).to eq 1
  expect(SurveyItemResponse.where(response_id: "student_survey_response_4").where(survey_item: s_phys_q2).first.likert_score).to eq 1

  expect(SurveyItemResponse.where(response_id: "student_survey_response_5").where(survey_item: s_phys_q1).first.likert_score).to eq 1
  expect(SurveyItemResponse.where(response_id: "student_survey_response_5").where(survey_item: s_phys_q2).first.likert_score).to eq 2
end

def is_idempotent_for_students
  number_of_survey_item_responses = SurveyItemResponse.count

  SurveyResponsesDataLoader.new.load_data filepath: path_to_student_responses

  expect(SurveyItemResponse.count).to eq number_of_survey_item_responses
end

def assigns_grade_level_to_responses
  results = { "student_survey_response_1" => 11,
              "student_survey_response_3" => 8,
              "student_survey_response_4" => 8,
              "student_survey_response_5" => 7,
              "student_survey_response_6" => 3,
              "student_survey_response_7" => 4 }
  results.each do |key, value|
    expect(SurveyItemResponse.where(response_id: key).all? do |response|
      response.grade == value
    end).to eq true
  end
end

def assigns_gender_to_responses
  results = { "student_survey_response_1" => female,
              "student_survey_response_3" => male,
              "student_survey_response_4" => non_binary,
              "student_survey_response_5" => non_binary,
              "student_survey_response_6" => unknown_gender,
              "student_survey_response_7" => non_binary }

  results.each do |key, value|
    expect(SurveyItemResponse.where(response_id: key).first.gender).to eq value
  end
end

def assigns_recorded_date_to_student_responses
  results = { "student_survey_response_1" => "2020-09-30T18:48:50",
              "student_survey_response_3" => "2021-03-31T09:59:02",
              "student_survey_response_4" => "2021-03-31T10:00:17",
              "student_survey_response_5" => "2021-03-31T10:01:36",
              "student_survey_response_6" => "2021-03-31T10:01:37",
              "student_survey_response_7" => "2021-03-31T10:01:38" }
  results.each do |key, value|
    expect(SurveyItemResponse.find_by_response_id(key).recorded_date).to eq Date.parse(value)
  end
end

def assigns_recorded_date_to_teacher_responses
  results = { "teacher_survey_response_1" => "2020-10-16T11:09:03",
              "teacher_survey_response_3" => "2020-12-06T8:36:52",
              "teacher_survey_response_4" => "2020-12-06T8:51:25",
              "teacher_survey_response_5" => "2020-12-06T8:55:58" }
  results.each do |key, value|
    expect(SurveyItemResponse.find_by_response_id(key).recorded_date).to eq Date.parse(value)
  end
end

def assigns_income_to_responses
  results = { "student_survey_response_1" => low_income,
              "student_survey_response_3" => low_income,
              "student_survey_response_4" => high_income,
              "student_survey_response_5" => low_income,
              "student_survey_response_6" => high_income,
              "student_survey_response_7" => low_income }

  results.each do |key, value|
    income = SurveyItemResponse.find_by_response_id(key).income
    expect(income).to eq value
  end
end

def assigns_ell_to_responses
  results = { "student_survey_response_1" => not_ell,
              "student_survey_response_3" => not_ell,
              "student_survey_response_4" => yes_ell,
              "student_survey_response_5" => yes_ell,
              "student_survey_response_6" => unknown_ell,
              "student_survey_response_7" => unknown_ell }

  results.each do |key, value|
    ell = SurveyItemResponse.find_by_response_id(key).ell
    expect(ell).to eq value
  end
end

def assigns_races_to_students
  results = {  "student_survey_response_1" => [american_indian],
               "student_survey_response_3" => [asian, black, latinx, multiracial],
               "student_survey_response_4" => [unknown_race],
               "student_survey_response_5" => [american_indian, asian, black, latinx, white, middle_eastern,
                                               multiracial],
               "student_survey_response_6" => [american_indian, asian, black, latinx, white, middle_eastern,
                                               multiracial],
               "student_survey_response_7" => [white] }

  results.each do |key, value|
    race = SurveyItemResponse.find_by_response_id(key).student.races.to_a
    qualtrics = race.map(&:qualtrics_code)
    expect(race).to eq value
  end
end

def assigns_races_to_parents
  results = {
    "parent_survey_response_1" => [american_indian],
    "parent_survey_response_2" => [unknown_race],
    "parent_survey_response_3" => [american_indian, latinx, white, multiracial],
    "parent_survey_response_4" => [unknown_race],
    "parent_survey_response_5" => [american_indian, asian, black, latinx, white, middle_eastern,
                                   multiracial],
    "parent_survey_response_6" => [american_indian, asian, black, latinx, white, middle_eastern,
                                   multiracial],
    "parent_survey_response_7" => [white]
  }

  results.each do |key, value|
    race = SurveyItemResponse.find_by_response_id(key).parent.races.to_a
    qualtrics = race.map(&:qualtrics_code).sort
    expect(race).to eq value
  end
end

def assigns_genders_to_parents
  results = { "parent_survey_response_1" => [male],
              "parent_survey_response_2" => [female],
              "parent_survey_response_3" => [female, male],
              "parent_survey_response_4" => [non_binary],
              "parent_survey_response_5" => [male],
              "parent_survey_response_6" => [unknown_gender] }
  # "parent_survey_response_7" => [non_binary] }

  results.each do |key, value|
    expect(SurveyItemResponse.where(response_id: key).first.parent.genders).to eq value
  end
end
