require "rails_helper"

RSpec.describe User, type: :model do
  describe "associations" do
    it { is_expected.to have_many(:accounts).through(:account_auths) }
    it { is_expected.to have_many(:account_auths).dependent(:destroy) }
    it { is_expected.to have_many(:project_memberships).dependent(:destroy) }
  end

  describe "validations" do
    it { is_expected.to validate_presence_of(:email) }
    it { is_expected.to validate_presence_of(:password_digest) }
    it { is_expected.to validate_presence_of(:time_zone) }

    describe "#email uniqueness validation" do
      subject { build(:user) }
      it { is_expected.to validate_uniqueness_of(:email) }
    end
  end

  describe "#associate?" do
    it "returns true if the user is an associate for the given account" do
      account_auth = create(:account_auth, role: :associate)
      user = account_auth.user
      account = account_auth.account

      expect(user.associate?(account)).to be_truthy
    end

    context "when user does not belong to the given account" do
      it "raises an error" do
        account = build(:account)
        user = build(:user)

        expect { user.associate?(account) }.to raise_error(ActiveRecord::RecordNotFound)
      end
    end
  end

  describe "#manager?" do
    it "returns true if the user is a project manager for the given account" do
      account_auth = create(:account_auth, role: :project_manager)
      user = account_auth.user
      account = account_auth.account

      expect(user.manager?(account)).to be_truthy
    end

    context "when user does not belong to the given account" do
      it "raises an error" do
        account = build(:account)
        user = build(:user)

        expect { user.manager?(account) }.to raise_error(ActiveRecord::RecordNotFound)
      end
    end
  end

  describe "#account_manager?" do
    it "returns true if the user is an account manager for the given account" do
      account_auth = create(:account_auth, role: :account_manager)
      user = account_auth.user
      account = account_auth.account

      expect(user.account_manager?(account)).to be_truthy
    end

    context "when user does not belong to the given account" do
      it "raises an error" do
        account = build(:account)
        user = build(:user)

        expect { user.account_manager?(account) }.to raise_error(ActiveRecord::RecordNotFound)
      end
    end
  end

  describe "#role_for" do
    it "returns the name of the role for the given account" do
      account_auth = create(:account_auth, role: :associate)
      user = account_auth.user
      account = account_auth.account

      expect(user.role_for(account)).to eq "associate"

      account_auth.update(role: :project_manager)
      expect(user.role_for(account)).to eq "project_manager"

      account_auth.update(role: :account_manager)
      expect(user.role_for(account)).to eq "account_manager"
    end

    context "when the user does not belong to the given account" do
      it "raises an error" do
        account = build(:account)
        user = build(:user)

        expect { user.role_for(account) }.to raise_error(ActiveRecord::RecordNotFound)
      end
    end
  end

  describe "#primary_account" do
    it "returns the first account the user has access to" do
      user = build(:user, accounts: build_list(:account, 2))

      expect(user.primary_account).to eq user.primary_account
    end

    it "adds a given account to the collection" do
      account = build(:account)
      user = build(:user, accounts: [])

      user.primary_account = account

      expect(user.accounts).to include account
    end
  end

  describe "#accessible_projects_for" do
    context "when user is an account manager" do
      it "returns all projects belonging to that account" do
        account = create(:account)
        account_manager = create(:user, :account_manager, primary_account: account)
        member_project = create(:project, name: "Member Project", account: account)
        non_member_project = create(:project, name: "Non-Member Project", account: account)
        outside_project = create(:project, name: "Outside Project", account: build(:account))

        results = account_manager.accessible_projects_for(account)

        expect(results).to include(member_project)
        expect(results).to include(non_member_project)
        expect(results).not_to include(outside_project)
      end
    end

    context "when user is not an account manager" do
      it "returns all projects for which the user has a membership" do
        account = create(:account)
        associate = create(:user, :associate, primary_account: account)
        member_project = create(:project, members: associate, name: "Member Project", account: account)
        non_member_project = create(:project, members: [], name: "Non-Member Project", account: account)
        outside_project = create(:project, name: "Outside Project", account: build(:account))

        results = associate.accessible_projects_for(account)

        expect(results).to include(member_project)
        expect(results).not_to include(non_member_project)
        expect(results).not_to include(outside_project)
      end
    end
  end
end
