require "rails_helper"

describe Poll::Stats do
  let(:poll) { create(:poll) }
  let(:stats) { Poll::Stats.new(poll) }

  describe "#participants" do
    it "includes hidden users" do
      create(:poll_voter, poll: poll)
      create(:poll_voter, poll: poll, user: create(:user, :level_two, :hidden))

      expect(stats.participants.count).to eq(2)
    end
  end

  describe "total participants" do
    it "supports every channel" do
      3.times { create(:poll_voter, :from_web, poll: poll) }
      create(:poll_recount, :from_booth, poll: poll,
                                         total_amount: 8,
                                         white_amount: 4,
                                         null_amount: 1)

      expect(stats.total_participants_web).to eq(3)
      expect(stats.total_participants_booth).to eq(13)
      expect(stats.total_participants).to eq(16)
    end
  end

  describe "#total_participants_booth" do
    it "uses recounts even if there are discrepancies when recounting" do
      create(:poll_recount, :from_booth, poll: poll, total_amount: 1)
      2.times { create(:poll_voter, :from_booth, poll: poll) }

      expect(stats.total_participants_booth).to eq(1)
    end
  end

  describe "total participants percentage by channel" do
    it "is relative to the total amount of participants" do
      create(:poll_voter, :from_web, poll: poll)
      create(:poll_recount, :from_booth, poll: poll, total_amount: 5)

      expect(stats.total_participants_web_percentage).to eq(16.667)
      expect(stats.total_participants_booth_percentage).to eq(83.333)
    end
  end

  describe "#total_web_valid" do
    it "returns only votes containing answers" do
      question = create(:poll_question, :yes_no, poll: poll)

      2.times do
        voter = create(:poll_voter, :from_web, poll: poll)
        create(:poll_answer, author: voter.user, question: question)
      end
      create(:poll_voter, :from_web, poll: poll)

      expect(stats.total_web_valid).to eq(2)
    end
  end

  describe "#total_web_white" do
    it "returns voters with no answers" do
      question = create(:poll_question, :yes_no, poll: poll)
      3.times { create(:poll_voter, :from_web, poll: poll) }
      create(:poll_answer, author: poll.voters.last.user, question: question)

      expect(stats.total_web_white).to eq(2)
    end
  end

  describe "#total_web_null" do
    it "returns 0" do
      expect(stats.total_web_null).to eq(0)
    end
  end

  describe "#total_booth_valid" do
    it "sums the total amounts in the recounts" do
      create(:poll_recount, :from_booth, poll: poll, total_amount: 3, white_amount: 1)
      create(:poll_recount, :from_booth, poll: poll, total_amount: 4, null_amount: 2)

      expect(stats.total_booth_valid).to eq(7)
    end
  end

  describe "#total_booth_white" do
    it "sums the white amounts in the recounts" do
      create(:poll_recount, :from_booth, poll: poll, white_amount: 120, total_amount: 3)
      create(:poll_recount, :from_booth, poll: poll, white_amount: 203, null_amount: 5)

      expect(stats.total_booth_white).to eq(323)
    end
  end

  describe "#total_booth_null" do
    it "sums the null amounts in the recounts" do
      create(:poll_recount, :from_booth, poll: poll, null_amount: 125, total_amount: 3)
      create(:poll_recount, :from_booth, poll: poll, null_amount: 34, white_amount: 5)

      expect(stats.total_booth_null).to eq(159)
    end
  end

  describe "valid percentage by channel" do
    it "is relative to the total amount of valid votes" do
      allow(stats).to receive(:total_web_valid).and_return(1)
      create(:poll_recount, :from_booth, poll: poll, total_amount: 2)

      expect(stats.valid_percentage_web).to eq(33.333)
      expect(stats.valid_percentage_booth).to eq(66.667)
    end
  end

  describe "white percentage by channel" do
    before { allow(stats).to receive(:total_web_white).and_return(10) }

    it "is relative to the total amount of white votes" do
      create(:poll_recount, :from_booth, poll: poll, white_amount: 70)

      expect(stats.white_percentage_web).to eq(12.5)
      expect(stats.white_percentage_booth).to eq(87.5)
    end
  end

  describe "null percentage by channel" do
    it "only accepts null votes from booth" do
      create(:poll_recount, :from_booth, poll: poll, null_amount: 70)

      expect(stats.null_percentage_web).to eq(0)
      expect(stats.null_percentage_booth).to eq(100)
    end
  end

  describe "#total_valid_votes" do
    it "counts valid votes from every channel" do
      allow(stats).to receive(:total_web_valid).and_return(2)
      create(:poll_recount, :from_booth, poll: poll, total_amount: 3, white_amount: 10)
      create(:poll_recount, :from_booth, poll: poll, total_amount: 4, null_amount: 20)

      expect(stats.total_valid_votes).to eq(9)
    end
  end

  describe "#total_white_votes" do
    before { allow(stats).to receive(:total_web_white).and_return(9) }

    it "counts white votes on every channel" do
      create(:poll_recount, :from_booth, poll: poll, white_amount: 12)

      expect(stats.total_white_votes).to eq(21)
    end
  end

  describe "#total_null_votes" do
    it "only accepts null votes from booth" do
      create(:poll_recount, :from_booth, poll: poll, null_amount: 32)

      expect(stats.total_null_votes).to eq(32)
    end
  end

  describe "total percentage by type" do
    before { allow(stats).to receive_messages(total_web_white: 1, total_web_valid: 2) }

    it "is relative to the total amount of votes" do
      create(:poll_recount, :from_booth, poll: poll,
                                         total_amount: 8,
                                         white_amount: 5,
                                         null_amount: 4)

      expect(stats.total_valid_percentage).to eq(50)
      expect(stats.total_white_percentage).to eq(30)
      expect(stats.total_null_percentage).to eq(20)
    end
  end

  describe "#participants_by_age" do
    it "returns stats based on what happened when the voting took place" do
      travel_to(100.years.ago) do
        [16, 18, 32, 32, 33, 34, 64, 65, 71, 73, 90, 99, 105].each do |age|
          create(:user, date_of_birth: age.years.ago - rand(0..11).months)
        end

        create(:poll, starts_at: 1.minute.from_now, ends_at: 2.minutes.from_now)
      end

      stats = Poll::Stats.new(Poll.last)
      allow(stats).to receive(:participants).and_return(User.all)

      expect(stats.participants_by_age["16 - 19"][:count]).to eq 2
      expect(stats.participants_by_age["20 - 24"][:count]).to eq 0
      expect(stats.participants_by_age["25 - 29"][:count]).to eq 0
      expect(stats.participants_by_age["30 - 34"][:count]).to eq 4
      expect(stats.participants_by_age["35 - 39"][:count]).to eq 0
      expect(stats.participants_by_age["40 - 44"][:count]).to eq 0
      expect(stats.participants_by_age["45 - 49"][:count]).to eq 0
      expect(stats.participants_by_age["50 - 54"][:count]).to eq 0
      expect(stats.participants_by_age["55 - 59"][:count]).to eq 0
      expect(stats.participants_by_age["60 - 64"][:count]).to eq 1
      expect(stats.participants_by_age["65 - 69"][:count]).to eq 1
      expect(stats.participants_by_age["70 - 74"][:count]).to eq 2
      expect(stats.participants_by_age["75 - 79"][:count]).to eq 0
      expect(stats.participants_by_age["80 - 84"][:count]).to eq 0
      expect(stats.participants_by_age["85 - 89"][:count]).to eq 0
      expect(stats.participants_by_age["90 - 300"][:count]).to eq 3
    end
  end

  describe "#participation_date", :with_frozen_time do
    let(:poll) { create(:poll, starts_at: 3.years.ago, ends_at: 2.years.ago) }

    it "returns the date when the poll finishes" do
      expect(stats.participation_date).to eq 2.years.ago
    end
  end

  describe "#participants_by_geozone" do
    it "groups by geozones in alphabetic order" do
      %w[Oceania Eurasia Eastasia].each { |name| create(:geozone, name: name) }

      expect(stats.participants_by_geozone.keys).to eq %w[Eastasia Eurasia Oceania]
    end

    it "calculates percentage relative to total participants" do
      hobbiton = create(:geozone, name: "Hobbiton")
      rivendel = create(:geozone, name: "Rivendel")

      3.times { create(:poll_voter, poll: poll, user: create(:user, :level_two, geozone: hobbiton)) }
      2.times { create(:poll_voter, poll: poll, user: create(:user, :level_two, geozone: rivendel)) }

      expect(stats.participants_by_geozone["Hobbiton"][:count]).to eq 3
      expect(stats.participants_by_geozone["Hobbiton"][:percentage]).to eq 60.0
      expect(stats.participants_by_geozone["Rivendel"][:count]).to eq 2
      expect(stats.participants_by_geozone["Rivendel"][:percentage]).to eq 40.0
    end
  end

  describe "#total_no_demographic_data" do
    before do
      create(:poll_voter, :from_web, poll: poll, user: create(:user, :level_two, gender: nil))
    end

    context "more registered participants than participants in recounts" do
      before do
        create(:poll_recount, :from_booth, poll: poll, total_amount: 1)
        2.times { create(:poll_voter, :from_booth, poll: poll) }
      end

      it "returns registered users with no demographic data" do
        expect(stats.total_no_demographic_data).to eq 1
      end
    end

    context "more participants in recounts than registered participants" do
      before do
        create(:poll_recount, :from_booth, poll: poll, total_amount: 3)
        2.times { create(:poll_voter, :from_booth, poll: poll) }
      end

      it "returns registered users with no demographic data plus users not registered" do
        expect(stats.total_no_demographic_data).to eq 2
      end
    end
  end

  describe "#channels" do
    context "no participants" do
      it "returns no channels" do
        expect(stats.channels).to eq []
      end
    end

    context "only participants from web" do
      before { create(:poll_voter, :from_web, poll: poll) }

      it "returns the web channel" do
        expect(stats.channels).to eq ["web"]
      end
    end

    context "only participants from booth" do
      before do
        create(:poll_recount, :from_booth, poll: poll, total_amount: 1)
      end

      it "returns the booth channel" do
        expect(stats.channels).to eq ["booth"]
      end
    end

    context "only participants from letter" do
      before { create(:poll_voter, origin: "letter", poll: poll) }

      it "returns the web channel" do
        expect(stats.channels).to eq ["letter"]
      end
    end

    context "participants from all channels" do
      before do
        create(:poll_voter, :from_web, poll: poll)
        create(:poll_recount, :from_booth, poll: poll, total_amount: 1)
        create(:poll_voter, origin: "letter", poll: poll)
      end

      it "returns all channels" do
        expect(stats.channels).to eq %w[web booth letter]
      end
    end
  end
end
