require "rails_helper"

describe Poll::Voter do
  describe "validations" do
    let(:poll) { create(:poll) }
    let(:booth) { create(:poll_booth) }
    let(:booth_assignment) { create(:poll_booth_assignment, poll: poll, booth: booth) }
    let(:voter) { create(:poll_voter) }
    let(:user) { create(:user, :level_two) }

    it "is valid" do
      expect(voter).to be_valid
    end

    it "is not valid without a user" do
      voter.user = nil
      expect(voter).not_to be_valid
    end

    it "is not valid without a poll" do
      voter.poll = nil
      expect(voter).not_to be_valid
    end

    it "is valid if has not voted" do
      voter = build(:poll_voter, :valid_document)

      expect(voter).to be_valid
    end

    it "is not valid if the user has already voted in the same poll or booth_assignment" do
      create(:poll_voter, user: user, poll: poll)

      voter = build(:poll_voter, user: user, poll: poll)

      expect { voter.save }.to raise_error ActiveRecord::RecordNotUnique
    end

    it "is not valid if the user has already voted in the same poll/booth" do
      create(:poll_voter, user: user, poll: poll, booth_assignment: booth_assignment)

      voter = build(:poll_voter, user: user, poll: poll, booth_assignment: booth_assignment)

      expect { voter.save }.to raise_error ActiveRecord::RecordNotUnique
    end

    it "is not valid if the user has already voted in different booth in the same poll" do
      create(:poll_voter, :from_booth, user: user, poll: poll, booth: create(:poll_booth))

      voter = build(:poll_voter, :from_booth, user: user, poll: poll, booth: booth)

      expect { voter.save }.to raise_error ActiveRecord::RecordNotUnique
    end

    it "is valid if the user has already voted in the same booth in different poll" do
      create(:poll_voter, :from_booth, user: user, booth: booth, poll: create(:poll))

      voter = build(:poll_voter, :from_booth, user: user, booth: booth, poll: poll)

      expect(voter).to be_valid
    end

    it "is not valid if the user has voted via web" do
      answer = create(:poll_answer)
      create(:poll_voter, :from_web, user: answer.author, poll: answer.poll)

      voter = build(:poll_voter, poll: answer.question.poll, user: answer.author)

      expect { voter.save }.to raise_error ActiveRecord::RecordNotUnique
    end

    context "Skip verification is enabled" do
      before do
        Setting["feature.user.skip_verification"] = true
        user.update!(document_number: nil, document_type: nil)
      end

      it "is not valid if the user has already voted in the same poll" do
        create(:poll_voter, user: user, poll: poll)

        voter = build(:poll_voter, user: user, poll: poll)

        expect { voter.save }.to raise_error ActiveRecord::RecordNotUnique
      end

      it "is valid if other users have voted in the same poll" do
        another_user = create(:user, :level_two, document_number: nil, document_type: nil)
        create(:poll_voter, user: another_user, poll: poll)

        voter = build(:poll_voter, user: user, poll: poll)

        expect(voter).to be_valid
      end
    end

    context "origin" do
      it "is not valid without an origin" do
        voter.origin = nil
        expect(voter).not_to be_valid
      end

      it "is not valid without a valid origin" do
        voter.origin = "invalid_origin"
        expect(voter).not_to be_valid
      end

      it "is valid with a booth origin" do
        voter.origin = "booth"
        voter.officer_assignment = create(:poll_officer_assignment)
        expect(voter).to be_valid
      end

      it "is valid with a web origin" do
        voter.origin = "web"
        expect(voter).to be_valid
      end

      it "dynamically validates the valid origins" do
        stub_const("#{Poll::Voter}::VALID_ORIGINS", %w[custom])

        expect(build(:poll_voter, origin: "custom")).to be_valid
        expect(build(:poll_voter, origin: "web")).not_to be_valid
      end
    end

    context "assignments" do
      it "is not valid without a booth_assignment_id when origin is booth" do
        voter.origin = "booth"
        voter.booth_assignment_id = nil
        expect(voter).not_to be_valid
      end

      it "is not valid without an officer_assignment_id when origin is booth" do
        voter.origin = "booth"
        voter.officer_assignment_id = nil
        expect(voter).not_to be_valid
      end

      it "is valid without assignments when origin is web" do
        voter.origin = "web"
        voter.booth_assignment_id = nil
        voter.officer_assignment_id = nil
        expect(voter).to be_valid
      end
    end

    describe ".create_or_find_by!" do
      it "finds the voter when it already exists instead of failing the validation" do
        existing_voter = Poll::Voter.create_with(origin: "web")
                                    .create_or_find_by!(user: voter.user, poll: voter.poll)

        expect(existing_voter).to eq voter
      end
    end
  end

  describe "scopes" do
    describe "#web" do
      it "returns voters with a web origin" do
        voter = create(:poll_voter, :from_web)

        expect(Poll::Voter.web).to eq [voter]
      end

      it "does not return voters with a booth origin" do
        create(:poll_voter, :from_booth)

        expect(Poll::Voter.web).to be_empty
      end
    end

    describe "#booth" do
      it "returns voters with a booth origin" do
        voter = create(:poll_voter, :from_booth)

        expect(Poll::Voter.booth).to eq [voter]
      end

      it "does not return voters with a web origin" do
        create(:poll_voter, :from_web)

        expect(Poll::Voter.booth).to be_empty
      end
    end
  end

  describe "save" do
    it "sets demographic info" do
      geozone = create(:geozone)
      user = create(:user, :level_two,
                    geozone: geozone,
                    date_of_birth: 30.years.ago,
                    gender: "female")

      voter = build(:poll_voter, user: user)
      voter.save!

      expect(voter.geozone).to eq(geozone)
      expect(voter.age).to eq(30)
      expect(voter.gender).to eq("female")
    end

    it "sets user info" do
      user = create(:user, document_number: "1234A", document_type: "1")
      voter = build(:poll_voter, user: user)
      voter.save!

      expect(voter.document_number).to eq("1234A")
      expect(voter.document_type).to eq("1")
    end

    it "sets user info with skip verification enabled" do
      Setting["feature.user.skip_verification"] = true
      user = create(:user)
      voter = build(:poll_voter, user: user)

      expect { voter.save! }.not_to raise_exception
    end
  end
end
