require "rails_helper"

describe User do
  describe "#headings_voted_within_group" do
    it "returns the headings voted by a user" do
      budget = create(:budget)
      group = create(:budget_group, budget: budget)

      new_york = create(:budget_heading, group: group, name: "New york")
      san_francisco = create(:budget_heading, group: group, name: "San Franciso")
      wyoming = create(:budget_heading, group: group, name: "Wyoming")
      another_heading = create(:budget_heading, group: group)

      new_york_investment = create(:budget_investment, heading: new_york)
      san_franciso_investment = create(:budget_investment, heading: san_francisco)
      wyoming_investment = create(:budget_investment, heading: wyoming)

      user1 = create(:user, votables: [wyoming_investment, san_franciso_investment, new_york_investment])
      user2 = create(:user)

      expect(user1.headings_voted_within_group(group)).to match_array [new_york, san_francisco, wyoming]
      expect(user1.headings_voted_within_group(group)).not_to include(another_heading)

      expect(user2.headings_voted_within_group(group)).to be_empty
    end

    it "returns headings with multiple translations only once" do
      group = create(:budget_group)
      heading = create(:budget_heading, group: group, name_en: "English", name_es: "Spanish")
      user = create(:user, votables: [create(:budget_investment, heading: heading)])

      expect(user.headings_voted_within_group(group).count).to eq 1
    end
  end

  describe "#comment_flags" do
    let(:user) { create(:user) }

    it "returns {} if no comment" do
      expect(user.comment_flags([])).to eq({})
    end

    it "returns a hash of flaggable_ids with 'true' if they were flagged by the user" do
      comment1 = create(:comment)
      comment2 = create(:comment)
      comment3 = create(:comment)
      Flag.flag(user, comment1)
      Flag.flag(user, comment3)

      flagged = user.comment_flags([comment1, comment2, comment3])

      expect(flagged[comment1.id]).to be
      expect(flagged[comment2.id]).not_to be
      expect(flagged[comment3.id]).to be
    end
  end

  subject { build(:user) }

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

  describe "#terms" do
    it "is not valid without accepting the terms of service" do
      subject.terms_of_service = nil
      expect(subject).not_to be_valid
    end
  end

  describe "#name" do
    it "is the username when the user is not an organization" do
      expect(subject.name).to eq(subject.username)
    end
  end

  describe "#age" do
    it "is the rounded integer age based on the date_of_birth" do
      user = create(:user, date_of_birth: 33.years.ago)
      expect(user.age).to eq(33)
    end
  end

  describe "#locale" do
    it "defaults to the default locale setting" do
      Setting["locales.default"] = "nl"

      user = build(:user, locale: nil)

      expect(user.locale).to eq "nl"
    end
  end

  describe "preferences" do
    describe "#email_on_comment" do
      it "is false by default" do
        expect(subject.email_on_comment).to be false
      end
    end

    describe "#email_on_comment_reply" do
      it "is false by default" do
        expect(subject.email_on_comment_reply).to be false
      end
    end

    describe "#newsletter" do
      it "is true by default when the consent for notifications setting is disabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = false

        expect(build(:user).newsletter).to be true
      end

      it "is false by default when the consent for notifications setting is enabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = true

        expect(build(:user).newsletter).to be false
      end
    end

    describe "#email_digest" do
      it "is true by default when the consent for notifications setting is disabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = false

        expect(build(:user).email_digest).to be true
      end

      it "is false by default when the consent for notifications setting is enabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = true

        expect(build(:user).email_digest).to be false
      end
    end

    describe "#email_on_direct_message" do
      it "is true by default when the consent for notifications setting is disabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = false

        expect(build(:user).email_on_direct_message).to be true
      end

      it "is false by default when the consent for notifications setting is enabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = true

        expect(build(:user).email_on_direct_message).to be false
      end
    end

    describe "#public_activity" do
      it "is true by default when the consent for notifications setting is disabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = false

        expect(build(:user).public_activity).to be true
      end

      it "is false by default when the consent for notifications setting is enabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = true

        expect(build(:user).public_activity).to be false
      end
    end

    describe "#recommended_debates" do
      it "is true by default when the consent for notifications setting is disabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = false

        expect(build(:user).recommended_debates).to be true
      end

      it "is false by default when the consent for notifications setting is enabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = true

        expect(build(:user).recommended_debates).to be false
      end
    end

    describe "#recommended_proposals" do
      it "is true by default when the consent for notifications setting is disabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = false

        expect(build(:user).recommended_proposals).to be true
      end

      it "is false by default when the consent for notifications setting is enabled" do
        Setting["feature.gdpr.require_consent_for_notifications"] = true

        expect(build(:user).recommended_proposals).to be false
      end
    end

    describe "#official_position_badge" do
      it "is false by default" do
        expect(subject.official_position_badge).to be false
      end
    end
  end

  describe "administrator?" do
    it "is false when the user is not an admin" do
      expect(subject.administrator?).to be false
    end

    it "is true when the user is an admin" do
      subject.save!
      create(:administrator, user: subject)
      expect(subject.administrator?).to be true
    end
  end

  describe "moderator?" do
    it "is false when the user is not a moderator" do
      expect(subject.moderator?).to be false
    end

    it "is true when the user is a moderator" do
      subject.save!
      create(:moderator, user: subject)
      expect(subject.moderator?).to be true
    end
  end

  describe "valuator?" do
    it "is false when the user is not a valuator" do
      expect(subject.valuator?).to be false
    end

    it "is true when the user is a valuator" do
      subject.save!
      create(:valuator, user: subject)
      expect(subject.valuator?).to be true
    end
  end

  describe "manager?" do
    it "is false when the user is not a manager" do
      expect(subject.manager?).to be false
    end

    it "is true when the user is a manager" do
      subject.save!
      create(:manager, user: subject)
      expect(subject.manager?).to be true
    end
  end

  describe "sdg_manager?" do
    it "is false when the user is not a sdg manager" do
      expect(subject.sdg_manager?).to be false
    end

    it "is true when the user is a sdg manager" do
      subject.save!
      create(:sdg_manager, user: subject)
      expect(subject.sdg_manager?).to be true
    end
  end

  describe "poll_officer?" do
    it "is false when the user is not a poll officer" do
      expect(subject.poll_officer?).to be false
    end

    it "is true when the user is a poll officer" do
      subject.save!
      create(:poll_officer, user: subject)
      expect(subject.poll_officer?).to be true
    end
  end

  describe "organization?" do
    it "is false when the user is not an organization" do
      expect(subject.organization?).to be false
    end

    describe "when it is an organization" do
      before { create(:organization, user: subject) }

      it "is true when the user is an organization" do
        expect(subject.organization?).to be true
      end

      it "calculates the name using the organization name" do
        expect(subject.name).to eq(subject.organization.name)
      end
    end
  end

  describe "verified_organization?" do
    it "is falsy when the user is not an organization" do
      expect(subject).not_to be_verified_organization
    end

    describe "when it is an organization" do
      before { create(:organization, user: subject) }

      it "is false when the user is not a verified organization" do
        expect(subject).not_to be_verified_organization
      end

      it "is true when the user is a verified organization" do
        subject.organization.verify
        expect(subject).to be_verified_organization
      end
    end
  end

  describe "organization_attributes" do
    before { subject.organization_attributes = { name: "org", responsible_name: "julia" } }

    it "triggers the creation of an associated organization" do
      expect(subject.organization).to be
      expect(subject.organization.name).to eq("org")
      expect(subject.organization.responsible_name).to eq("julia")
    end

    it "deactivates the validation of username, and activates the validation of organization" do
      subject.username = nil
      expect(subject).to be_valid

      subject.organization.name = nil
      expect(subject).not_to be_valid
    end
  end

  describe "official?" do
    it "is false when the user is not an official" do
      expect(subject.official_level).to eq(0)
      expect(subject.official?).to be false
    end

    it "is true when the user is an official" do
      subject.official_level = 3
      subject.save!
      expect(subject.official?).to be true
    end
  end

  describe "add_official_position!" do
    it "raises an exception when level not valid" do
      expect { subject.add_official_position!("Boss", 89) }.to raise_error ActiveRecord::RecordInvalid
    end

    it "updates official position fields" do
      expect(subject).not_to be_official
      subject.add_official_position!("Veterinarian", 2)

      expect(subject).to be_official
      expect(subject.official_position).to eq("Veterinarian")
      expect(subject.official_level).to eq(2)

      subject.add_official_position!("Brain surgeon", 3)
      expect(subject.official_position).to eq("Brain surgeon")
      expect(subject.official_level).to eq(3)
    end
  end

  describe "remove_official_position!" do
    it "updates official position fields" do
      subject.add_official_position!("Brain surgeon", 3)
      expect(subject).to be_official

      subject.remove_official_position!

      expect(subject).not_to be_official
      expect(subject.official_position).to be nil
      expect(subject.official_level).to eq(0)
    end
  end

  describe "officials scope" do
    it "returns only users with official positions" do
      create(:user, official_position: "Mayor", official_level: 1)
      create(:user, official_position: "Director", official_level: 3)
      create(:user, official_position: "Math Teacher", official_level: 4)
      create(:user, official_position: "Manager", official_level: 5)
      2.times { create(:user) }

      officials = User.officials
      expect(officials.size).to eq(4)
      officials.each do |user|
        expect(user.official_level).to be > 0
        expect(user.official_position).to be_present
      end
    end
  end

  describe "has_official_email" do
    it "checks if the mail address has the officials domain" do
      # We will use empleados.madrid.es as the officials' domain
      # Subdomains are also accepted

      Setting["email_domain_for_officials"] = "officials.madrid.es"
      user1 = create(:user, email: "john@officials.madrid.es", confirmed_at: Time.current)
      user2 = create(:user, email: "john@yes.officials.madrid.es", confirmed_at: Time.current)
      user3 = create(:user, email: "john@unofficials.madrid.es", confirmed_at: Time.current)
      user4 = create(:user, email: "john@example.org", confirmed_at: Time.current)

      expect(user1.has_official_email?).to be true
      expect(user2.has_official_email?).to be true
      expect(user3.has_official_email?).to be false
      expect(user4.has_official_email?).to be false

      # We reset the officials' domain setting
      Setting.find_by(key: "email_domain_for_officials").update!(value: "")
    end
  end

  describe "official_position_badge" do
    describe "Users of level 1" do
      it "displays the badge if set in preferences" do
        user = create(:user, official_level: 1, official_position_badge: true)

        expect(user.display_official_position_badge?).to be true
      end

      it "does not display the badge if set in preferences" do
        user = create(:user, official_level: 1, official_position_badge: false)

        expect(user.display_official_position_badge?).to be false
      end
    end

    describe "Users higher than level 1" do
      it "displays the badge regardless of preferences" do
        user1 = create(:user, official_level: 2, official_position_badge: false)
        user2 = create(:user, official_level: 3, official_position_badge: false)
        user3 = create(:user, official_level: 4, official_position_badge: false)
        user4 = create(:user, official_level: 5, official_position_badge: false)

        expect(user1.display_official_position_badge?).to be true
        expect(user2.display_official_position_badge?).to be true
        expect(user3.display_official_position_badge?).to be true
        expect(user4.display_official_position_badge?).to be true
      end
    end
  end

  describe "scopes" do
    describe "active" do
      it "returns users that have not been erased" do
        user1 = create(:user, erased_at: nil)
        user2 = create(:user, erased_at: nil)
        user3 = create(:user, erased_at: Time.current)

        expect(User.active).to match_array [user1, user2]
        expect(User.active).not_to include(user3)
      end

      it "returns users that have not been blocked" do
        user1 = create(:user)
        user2 = create(:user)
        user3 = create(:user)
        user3.block

        expect(User.active).to match_array [user1, user2]
        expect(User.active).not_to include(user3)
      end
    end

    describe "erased" do
      it "returns users that have been erased" do
        user1 = create(:user, erased_at: Time.current)
        user2 = create(:user, erased_at: Time.current)
        user3 = create(:user, erased_at: nil)

        expect(User.erased).to match_array [user1, user2]
        expect(User.erased).not_to include(user3)
      end
    end

    describe ".newsletter" do
      it "returns users subscribed to the newsletter" do
        create(:user, newsletter: true, username: "Subscriber1")
        create(:user, newsletter: true, username: "Subscriber2")
        create(:user, newsletter: false, username: "NonSubscriber")

        expect(User.newsletter.pluck(:username)).to eq ["Subscriber1", "Subscriber2"]
      end
    end

    describe ".email_digest" do
      it "returns users subscribed to email digests" do
        create(:user, email_digest: true, username: "Digester1")
        create(:user, email_digest: true, username: "Digester2")
        create(:user, email_digest: false, username: "NonDigester")

        expect(User.email_digest.pluck(:username)).to eq ["Digester1", "Digester2"]
      end
    end

    describe ".by_username_email_or_document_number" do
      let!(:larry) do
        create(:user, email: "larry@consul.dev", username: "Larry Bird", document_number: "12345678Z")
      end

      before { create(:user, email: "robert@consul.dev", username: "Robert Parish") }

      it "finds users by email" do
        expect(User.by_username_email_or_document_number("larry@consul.dev")).to eq [larry]
      end

      it "finds users by email with whitespaces" do
        expect(User.by_username_email_or_document_number(" larry@consul.dev ")).to eq [larry]
      end

      it "finds users by name" do
        expect(User.by_username_email_or_document_number("larry")).to eq [larry]
      end

      it "finds users by name with whitespaces" do
        expect(User.by_username_email_or_document_number(" larry ")).to eq [larry]
      end

      it "finds users by document_number" do
        expect(User.by_username_email_or_document_number("12345678Z")).to eq [larry]
      end

      it "finds users by document_number with whitespaces" do
        expect(User.by_username_email_or_document_number(" 12345678Z ")).to eq [larry]
      end
    end

    describe ".between_ages" do
      it "returns users between certain ages, including both" do
        [21, 22, 23, 23, 42, 43, 44, 51].each do |age|
          create(:user, date_of_birth: age.years.ago)
        end

        expect(User.between_ages(0, 20).count).to eq 0
        expect(User.between_ages(0, 21).count).to eq 1
        expect(User.between_ages(21, 23).count).to eq 4
        expect(User.between_ages(24, 41).count).to eq 0
        expect(User.between_ages(41, 45).count).to eq 3
        expect(User.between_ages(51, 100).count).to eq 1
      end

      it "does not consider that people born in the same year share the same age" do
        april_1st_user = create(:user, date_of_birth: Time.zone.local(2001, 4, 1))
        april_2nd_user = create(:user, date_of_birth: Time.zone.local(2001, 4, 2))
        april_3rd_user = create(:user, date_of_birth: Time.zone.local(2001, 4, 3))

        travel_to "2021-04-02" do
          expect(User.between_ages(18, 19)).to eq [april_3rd_user]
          expect(User.between_ages(20, 21)).to match_array [april_1st_user, april_2nd_user]
          expect(User.between_ages(19, 20)).to match_array [april_1st_user, april_2nd_user, april_3rd_user]
        end
      end

      it "works on leap years with users born on leap years" do
        leap_day_user = create(:user, date_of_birth: Time.zone.local(2000, 2, 29))
        march_1st_user = create(:user, date_of_birth: Time.zone.local(2000, 3, 1))

        travel_to "2024-02-28" do
          expect(User.between_ages(22, 23)).to match_array [leap_day_user, march_1st_user]
          expect(User.between_ages(23, 24)).to match_array [leap_day_user, march_1st_user]
          expect(User.between_ages(24, 25)).to eq []
        end

        travel_to "2024-02-29" do
          expect(User.between_ages(22, 23)).to eq [march_1st_user]
          expect(User.between_ages(23, 24)).to match_array [leap_day_user, march_1st_user]
          expect(User.between_ages(24, 25)).to eq [leap_day_user]
        end

        travel_to "2024-03-01" do
          expect(User.between_ages(22, 23)).to eq []
          expect(User.between_ages(23, 24)).to match_array [leap_day_user, march_1st_user]
          expect(User.between_ages(24, 25)).to match_array [leap_day_user, march_1st_user]
        end
      end

      it "works on non-leap years with users born on leap years" do
        leap_day_user = create(:user, date_of_birth: Time.zone.local(2000, 2, 29))
        march_1st_user = create(:user, date_of_birth: Time.zone.local(2000, 3, 1))

        travel_to "2023-02-28" do
          expect(User.between_ages(21, 22)).to match_array [leap_day_user, march_1st_user]
          expect(User.between_ages(22, 23)).to match_array [leap_day_user, march_1st_user]
          expect(User.between_ages(23, 24)).to eq []
        end

        travel_to "2023-03-01" do
          expect(User.between_ages(21, 22)).to eq []
          expect(User.between_ages(22, 23)).to match_array [leap_day_user, march_1st_user]
          expect(User.between_ages(23, 24)).to match_array [leap_day_user, march_1st_user]
        end
      end

      it "works on leap years with users born on non-leap years" do
        march_1st_user = create(:user, date_of_birth: Time.zone.local(2001, 3, 1))

        travel_to "2024-02-28" do
          expect(User.between_ages(21, 22)).to eq [march_1st_user]
          expect(User.between_ages(22, 23)).to eq [march_1st_user]
          expect(User.between_ages(23, 24)).to eq []
        end

        travel_to "2024-02-29" do
          expect(User.between_ages(21, 22)).to eq [march_1st_user]
          expect(User.between_ages(22, 23)).to eq [march_1st_user]
          expect(User.between_ages(23, 24)).to eq []
        end

        travel_to "2024-03-01" do
          expect(User.between_ages(21, 22)).to eq []
          expect(User.between_ages(22, 23)).to match_array [march_1st_user]
          expect(User.between_ages(23, 24)).to match_array [march_1st_user]
        end
      end

      it "returns users between certain ages on a reference date" do
        reference_date = 20.years.ago

        travel_to(reference_date) do
          [21, 22, 23, 23, 34, 42, 43, 44, 50, 51].each do |age|
            create(:user, date_of_birth: age.years.ago)
          end
        end

        expect(User.between_ages(0, 20, at_time: reference_date).count).to eq 0
        expect(User.between_ages(21, 25, at_time: reference_date).count).to eq 4
        expect(User.between_ages(25, 30, at_time: reference_date).count).to eq 0
        expect(User.between_ages(30, 34, at_time: reference_date).count).to eq 1
        expect(User.between_ages(35, 39, at_time: reference_date).count).to eq 0
        expect(User.between_ages(40, 44, at_time: reference_date).count).to eq 3
        expect(User.between_ages(45, 49, at_time: reference_date).count).to eq 0
        expect(User.between_ages(50, 54, at_time: reference_date).count).to eq 2
        expect(User.between_ages(55, 100, at_time: reference_date).count).to eq 0
      end
    end
  end

  describe "self.search" do
    describe "find users" do
      let!(:larry) { create(:user, email: "larry@consul.dev", username: "Larry Bird") }

      before { create(:user, email: "robert@consul.dev", username: "Robert Parish") }

      it "by email" do
        expect(User.search("larry@consul.dev")).to eq [larry]
      end

      it "by email with whitespaces" do
        expect(User.search(" larry@consul.dev ")).to eq [larry]
      end

      it "by name" do
        expect(User.search("larry")).to eq [larry]
      end

      it "by name with whitespaces" do
        expect(User.search(" larry ")).to eq [larry]
      end
    end

    it "returns no results if no search term provided" do
      expect(User.search("    ")).to be_empty
    end
  end

  describe "verification" do
    it_behaves_like "verifiable"
  end

  describe "cache" do
    let(:user) { create(:user) }

    it "expires cache with becoming a moderator" do
      expect { create(:moderator, user: user) }.to change { user.cache_version }
    end

    it "expires cache with becoming an admin" do
      expect { create(:administrator, user: user) }.to change { user.cache_version }
    end

    it "expires cache with becoming a veridied organization" do
      create(:organization, user: user)
      expect { user.organization.verify }.to change { user.reload.cache_version }
    end
  end

  describe "document_number" do
    it "upcases document number" do
      user = User.new(document_number: "x1234567z")
      user.valid?
      expect(user.document_number).to eq("X1234567Z")
    end

    it "removes all characters except numbers and letters" do
      user = User.new(document_number: " 12.345.678 - B")
      user.valid?
      expect(user.document_number).to eq("12345678B")
    end
  end

  describe "#erase" do
    it "erases user information and marks him as erased" do
      user = create(:user,
                    username: "manolo",
                    email: "a@a.com",
                    unconfirmed_email: "a@a.com",
                    phone_number: "5678",
                    confirmed_phone: "5678",
                    unconfirmed_phone: "5678",
                    encrypted_password: "foobar",
                    confirmation_token: "token1",
                    reset_password_token: "token2",
                    email_verification_token: "token3")

      user.erase("a test")
      user.reload

      expect(user.erase_reason).to eq("a test")
      expect(user.erased_at).to    be

      expect(user.username).to be nil
      expect(user.email).to be nil
      expect(user.unconfirmed_email).to be nil
      expect(user.phone_number).to be nil
      expect(user.confirmed_phone).to be nil
      expect(user.unconfirmed_phone).to be nil
      expect(user.encrypted_password).to be_empty
      expect(user.confirmation_token).to be nil
      expect(user.reset_password_token).to be nil
      expect(user.email_verification_token).to be nil
    end

    it "maintains associated identification document" do
      user = create(:user, document_number: "1234", document_type: "1")
      user.erase
      user.reload

      expect(user.erased_at).to be
      expect(user.document_number).to be
      expect(user.document_type).to be
    end

    it "destroys associated social network identities" do
      user = create(:user)
      identity = create(:identity, user: user)

      user.erase("an identity test")

      expect(Identity.exists?(identity.id)).not_to be
    end

    it "removes all user roles" do
      user = create(:user)
      [:administrator, :moderator, :manager, :sdg_manager, :valuator].each do |role|
        create(role, user: user)
      end

      expect { user.erase }.to change { Administrator.count }.by(-1)
                           .and change { Moderator.count }.by(-1)
                           .and change { Manager.count }.by(-1)
                           .and change { SDG::Manager.count }.by(-1)
                           .and change { Valuator.count }.by(-1)
    end
  end

  describe "#take_votes_from" do
    it "logs info of previous users" do
      user = create(:user, :level_three)
      other_user = create(:user, :level_three)
      another_user = create(:user)

      expect(user.former_users_data_log).to be_blank

      user.take_votes_from other_user

      expect(user.former_users_data_log).to include("id: #{other_user.id}")

      user.take_votes_from another_user

      expect(user.former_users_data_log).to include("id: #{other_user.id}")
      expect(user.former_users_data_log).to include("| id: #{another_user.id}")
    end

    it "reassigns votes from other user" do
      other_user = create(:user, :level_three)
      user = create(:user, :level_three)

      v1 = create(:vote, voter: other_user, votable: create(:debate))
      v2 = create(:vote, voter: other_user, votable: create(:proposal))
      v3 = create(:vote, voter: other_user, votable: create(:comment))

      create(:vote)

      expect(other_user.votes.count).to eq(3)
      expect(user.votes.count).to eq(0)

      user.take_votes_from other_user

      expect(other_user.votes.count).to eq(0)
      expect(user.vote_ids).to match_array [v1.id, v2.id, v3.id]
    end

    it "reassigns budget ballots from other user" do
      other_user = create(:user, :level_three)
      user = create(:user, :level_three)

      b1 = create(:budget_ballot, user: other_user)
      b2 = create(:budget_ballot, user: other_user)

      create(:budget_ballot)

      expect(Budget::Ballot.where(user: other_user).count).to eq(2)
      expect(Budget::Ballot.where(user: user).count).to eq(0)

      user.take_votes_from other_user

      expect(Budget::Ballot.where(user: other_user).count).to eq(0)
      expect(Budget::Ballot.where(user: user)).to match_array [b1, b2]
    end

    it "reassigns poll voters from other user" do
      other_user = create(:user, :level_three)
      user = create(:user, :level_three)

      v1 = create(:poll_voter, user: other_user)
      v2 = create(:poll_voter, user: other_user)

      create(:poll_voter)

      expect(Poll::Voter.where(user: other_user).count).to eq(2)
      expect(Poll::Voter.where(user: user).count).to eq(0)

      user.take_votes_from other_user

      expect(Poll::Voter.where(user: other_user).count).to eq(0)
      expect(Poll::Voter.where(user: user)).to match_array [v1, v2]
    end

    it "does not reassign votes if the user has already voted" do
      poll = create(:poll)
      user = create(:user, :level_three)
      other_user = create(:user, :level_three)

      voter = create(:poll_voter, poll: poll, user: user)
      other_voter = create(:poll_voter, poll: poll, user: other_user)
      other_poll_voter = create(:poll_voter, poll: create(:poll), user: other_user)

      expect(Poll::Voter.where(user: user)).to eq [voter]
      expect(Poll::Voter.where(user: other_user)).to match_array [other_voter, other_poll_voter]

      user.take_votes_from(other_user)

      expect(Poll::Voter.where(user: user)).to match_array [voter, other_poll_voter]
      expect(Poll::Voter.where(user: other_user)).to eq []
    end
  end

  describe "#take_votes_if_erased_document" do
    it "does nothing if no erased user with received document" do
      user_1 = create(:user, :level_three)
      user_2 = create(:user, :level_three)

      create(:vote, voter: user_1)
      create(:budget_ballot, user: user_1)
      create(:poll_voter, user: user_1)

      user_2.take_votes_if_erased_document(111, 1)

      expect(user_1.votes.count).to eq(1)
      expect(user_2.votes.count).to eq(0)

      expect(Budget::Ballot.where(user: user_1).count).to eq(1)
      expect(Budget::Ballot.where(user: user_2).count).to eq(0)

      expect(Poll::Voter.where(user: user_1).count).to eq(1)
      expect(Poll::Voter.where(user: user_2).count).to eq(0)
    end

    it "takes votes from erased user with received document" do
      user_1 = create(:user, :level_two, document_number: "12345777", document_type: "1")
      user_2 = create(:user)

      create(:vote, voter: user_1)
      create(:budget_ballot, user: user_1)
      create(:poll_voter, user: user_1)

      user_1.erase

      user_2.take_votes_if_erased_document("12345777", "1")

      expect(user_1.votes.count).to eq(0)
      expect(user_2.votes.count).to eq(1)

      expect(Budget::Ballot.where(user: user_1).count).to eq(0)
      expect(Budget::Ballot.where(user: user_2).count).to eq(1)

      expect(Poll::Voter.where(user: user_1).count).to eq(0)
      expect(Poll::Voter.where(user: user_2).count).to eq(1)
    end

    it "removes document from erased user and logs info" do
      user_1 = create(:user, document_number: "12345777", document_type: "1")
      user_2 = create(:user)
      user_1.erase

      user_2.take_votes_if_erased_document("12345777", "1")

      expect(user_2.reload.former_users_data_log).to include("id: #{user_1.id}")
      expect(user_1.reload.document_number).to be_blank
    end
  end

  describe "email_required?" do
    it "is true for regular users" do
      expect(subject.email_required?).to be true
      expect(create(:user, :hidden).email_required?).to be true
    end

    it "is false for erased users" do
      user = create(:user)
      user.erase
      user.reload

      expect(user.email_required?).to be false
    end

    it "is false for verified users with no email" do
      user = create(:user, username: "Lois", email: "", verified_at: Time.current)

      expect(user).to be_valid
      expect(user.email_required?).to be false
    end
  end

  describe "#interests" do
    let(:user) { create(:user) }

    it "returns followed object tags" do
      create(:proposal, tag_list: "Sport", followers: [user])

      expect(user.interests).to eq ["Sport"]
    end

    it "deals gracefully with hidden proposals" do
      proposal = create(:proposal, tag_list: "Sport", followers: [user])

      proposal.hide

      expect(user.interests).to eq []
    end

    it "discards followed objects duplicated tags" do
      create(:proposal, tag_list: "Sport", followers: [user])
      create(:proposal, tag_list: "Sport", followers: [user])
      create(:budget_investment, tag_list: "Sport", followers: [user])

      expect(user.interests).to eq ["Sport"]
    end
  end

  describe "#public_interests" do
    it "is false by default" do
      expect(User.new.public_interests).to be false
    end
  end

  describe ".find_by_manager_login" do
    it "works with a low ID" do
      user = create(:user)
      expect(User.find_by_manager_login("admin_user_#{user.id}")).to eq user
    end

    it "works with a high ID" do
      10.times { create(:user) }
      user = User.last
      expect(User.find_by_manager_login("admin_user_#{user.id}")).to eq user
    end
  end

  describe "#block" do
    it "hides legislation proposals created by the user" do
      user = create(:user)
      other_user = create(:user)

      proposal = create(:legislation_proposal, author: user)
      other_proposal = create(:legislation_proposal, author: other_user)

      user.block

      expect(Legislation::Proposal.all).to eq [other_proposal]
      expect(Legislation::Proposal.with_hidden).to match_array [proposal, other_proposal]
    end

    it "removes all user roles" do
      user = create(:user)
      [:administrator, :moderator, :manager, :sdg_manager, :valuator].each do |role|
        create(role, user: user)
      end

      expect { user.block }.to change { Administrator.count }.by(-1)
                           .and change { Moderator.count }.by(-1)
                           .and change { Manager.count }.by(-1)
                           .and change { SDG::Manager.count }.by(-1)
                           .and change { Valuator.count }.by(-1)
    end
  end

  describe "#full_restore" do
    it "restore all previous hidden user content" do
      user = create(:user, :hidden)
      other_user = create(:user, :hidden)

      comment = create(:comment, :hidden, author: user)
      debate = create(:debate, :hidden, author: user)
      investment = create(:budget_investment, :hidden, author: user)
      proposal = create(:proposal, :hidden, author: user)
      legislation_proposal = create(:legislation_proposal, :hidden, author: user)
      proposal_notification = create(:proposal_notification, :hidden, proposal: proposal)

      old_hidden_comment = create(:comment, hidden_at: 3.days.ago, author: user)
      old_hidden_debate = create(:debate, hidden_at: 3.days.ago, author: user)
      old_hidden_investment = create(:budget_investment, hidden_at: 3.days.ago, author: user)
      old_hidden_proposal = create(:proposal, hidden_at: 3.days.ago, author: user)
      old_hidden_legislation_proposal = create(:legislation_proposal, hidden_at: 3.days.ago, author: user)
      old_hidden_proposal_notification = create(:proposal_notification,
                                                hidden_at: 3.days.ago,
                                                proposal: proposal)

      other_user_comment = create(:comment, :hidden, author: other_user)
      other_user_debate = create(:debate, :hidden, author: other_user)
      other_user_proposal = create(:proposal, :hidden, author: other_user)
      other_user_investment = create(:budget_investment, :hidden, author: other_user)
      other_user_legislation_proposal = create(:legislation_proposal, :hidden, author: other_user)
      other_user_proposal_notification = create(:proposal_notification,
                                                :hidden,
                                                proposal: other_user_proposal)

      user.full_restore

      expect(debate.reload).not_to be_hidden
      expect(comment.reload).not_to be_hidden
      expect(investment.reload).not_to be_hidden
      expect(proposal.reload).not_to be_hidden
      expect(proposal_notification.reload).not_to be_hidden
      expect(legislation_proposal.reload).not_to be_hidden

      expect(old_hidden_comment.reload).to be_hidden
      expect(old_hidden_debate.reload).to be_hidden
      expect(old_hidden_investment.reload).to be_hidden
      expect(old_hidden_proposal.reload).to be_hidden
      expect(old_hidden_proposal_notification.reload).to be_hidden
      expect(old_hidden_legislation_proposal.reload).to be_hidden

      expect(other_user_comment.reload).to be_hidden
      expect(other_user_debate.reload).to be_hidden
      expect(other_user_investment.reload).to be_hidden
      expect(other_user_proposal.reload).to be_hidden
      expect(other_user_proposal_notification.reload).to be_hidden
      expect(other_user_legislation_proposal.reload).to be_hidden
    end
  end

  describe "#add_subscriptions_token" do
    let(:user) { build(:user, subscriptions_token: nil) }

    it "generates a subscriptions token when the user doesn't have one" do
      user.add_subscriptions_token

      expect(user.subscriptions_token).to be_present
    end

    it "keeps the existing subscriptions token when the user already has one" do
      user.update!(subscriptions_token: "already_set")

      user.add_subscriptions_token

      expect(user.subscriptions_token).to eq "already_set"
    end
  end

  describe ".password_complexity" do
    it "returns no complexity when the secrets aren't configured" do
      expect(User.password_complexity).to eq({ digit: 0, lower: 0, symbol: 0, upper: 0 })
    end

    context "when secrets are configured" do
      before do
        stub_secrets(
          security: {
            password_complexity: true
          },
          tenants: {
            tolerant: {
              security: {
                password_complexity: false
              }
            }
          }
        )
      end

      it "uses the general secrets for the main tenant" do
        expect(User.password_complexity).to eq({ digit: 1, lower: 1, symbol: 0, upper: 1 })
      end

      it "uses the tenant secrets for a tenant" do
        allow(Tenant).to receive(:current_schema).and_return("tolerant")

        expect(User.password_complexity).to eq({ digit: 0, lower: 0, symbol: 0, upper: 0 })
      end
    end
  end

  describe ".maximum_attempts" do
    it "returns 20 as default when the secrets aren't configured" do
      expect(User.maximum_attempts).to eq 20
    end

    context "when secrets are configured" do
      before do
        stub_secrets(
          security: {
            lockable: { maximum_attempts: "14" }
          },
          tenants: {
            superstrict: {
              security: {
                lockable: { maximum_attempts: "1" }
              }
            }
          }
        )
      end

      it "uses the general secrets for the main tenant" do
        expect(User.maximum_attempts).to eq 14
      end

      it "uses the tenant secrets for a tenant" do
        allow(Tenant).to receive(:current_schema).and_return("superstrict")

        expect(User.maximum_attempts).to eq 1
      end
    end
  end

  describe ".unlock_in" do
    it "returns 1 as default when the secrets aren't configured" do
      expect(User.unlock_in).to eq 1.hour
    end

    context "when secrets are configured" do
      before do
        stub_secrets(
          security: {
            lockable: { unlock_in: "2" }
          },
          tenants: {
            superstrict: {
              security: {
                lockable: { unlock_in: "50" }
              }
            }
          }
        )
      end

      it "uses the general secrets for the main tenant" do
        expect(User.unlock_in).to eq 2.hours
      end

      it "uses the tenant secrets for a tenant" do
        allow(Tenant).to receive(:current_schema).and_return("superstrict")

        expect(User.unlock_in).to eq 50.hours
      end
    end
  end
end
