#  Copyright (c) 2012-2015, Pfadibewegung Schweiz. This file is part of
#  hitobito and licensed under the Affero General Public License version 3
#  or later. See the COPYING file at the top-level directory or at
#  https://github.com/hitobito/hitobito.

require "spec_helper"

describe Person::AddRequestAbility do
  subject { ability }

  let(:ability) { Ability.new(role.person.reload) }

  context :layer_and_below_full do
    let(:role) { Fabricate(Group::TopGroup::Leader.name, group: groups(:top_group)) }

    it "allowed with person in same layer" do
      other = Fabricate(Group::TopGroup::Member.name, group: groups(:top_group)).person
      request = create_request(other)

      is_expected.to be_able_to(:approve, request)
      is_expected.to be_able_to(:reject, request)
      is_expected.to be_able_to(:add_without_request, request)
      is_expected.to be_able_to(:index_person_add_requests, groups(:top_layer))
    end

    it "allowed with person in below layer" do
      other = Fabricate(Group::BottomLayer::Member.name, group: groups(:bottom_layer_one)).person
      request = create_request(other)

      is_expected.to be_able_to(:approve, request)
      is_expected.to be_able_to(:reject, request)
      is_expected.to be_able_to(:add_without_request, request)
      is_expected.to be_able_to(:index_person_add_requests, groups(:bottom_layer_one))
    end

    it "not allowed with non-visible person in below layer" do
      other = Fabricate(Group::BottomGroup::Member.name, group: groups(:bottom_group_one_one)).person
      request = create_request(other)

      is_expected.not_to be_able_to(:approve, request)
      is_expected.not_to be_able_to(:reject, request)
      is_expected.to be_able_to(:add_without_request, request)
    end

    it "allowed with person deleted in below layer" do
      other = Fabricate(Group::BottomLayer::Member.name, group: groups(:bottom_layer_one), start_on: 1.year.ago, end_on: 1.month.ago).person
      request = create_request(other)

      is_expected.to be_able_to(:approve, request)
      is_expected.to be_able_to(:reject, request)
      is_expected.to be_able_to(:add_without_request, request)
      is_expected.to be_able_to(:index_person_add_requests, groups(:bottom_layer_one))
    end

    context "in below layer" do
      let(:role) { Fabricate(Group::BottomLayer::Leader.name, group: groups(:bottom_layer_one)) }

      it "allowed with person in same layer" do
        other = Fabricate(Group::BottomGroup::Member.name, group: groups(:bottom_group_one_one)).person
        request = create_request(other)

        is_expected.to be_able_to(:approve, request)
        is_expected.to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
        is_expected.to be_able_to(:index_person_add_requests, groups(:bottom_layer_one))
      end

      it "allowed with person deleted in same layer" do
        other = Fabricate(Group::BottomGroup::Member.name, group: groups(:bottom_group_one_one)).person
        other.roles.first.update!(start_on: 1.year.ago, end_on: 1.month.ago)
        request = create_request(other)

        is_expected.to be_able_to(:approve, request)
        is_expected.to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
      end

      it "not allowed with person in neighbour layer" do
        other = Fabricate(Group::BottomLayer::Member.name, group: groups(:bottom_layer_two)).person
        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.not_to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:bottom_layer_two))
      end

      it "not allowed with person in neighbour layer and deleted role in same layer" do
        other = Fabricate(Group::BottomLayer::Member.name, group: groups(:bottom_layer_two)).person
        Fabricate(Group::BottomGroup::Member.name,
          group: groups(:bottom_group_one_one),
          person: other,
          start_on: 1.year.ago,
          end_on: 1.month.ago)
        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.not_to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:bottom_layer_two))
      end

      it "allowed with person in neighbour layer with contact data" do
        other = Fabricate(Group::BottomLayer::Leader.name, group: groups(:bottom_layer_two)).person
        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
      end

      it "allowed with person in neighbour layer where user has a simple role" do
        Fabricate(Group::BottomLayer::Member.name, group: groups(:bottom_layer_two), person: role.person)
        other = Fabricate(Group::BottomLayer::Leader.name, group: groups(:bottom_layer_two)).person
        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:bottom_layer_two))
      end

      it "not allowed with deleted person in neighbour layer where user has a simple role" do
        Fabricate(Group::BottomLayer::Member.name, group: groups(:bottom_layer_two), person: role.person)
        other = Fabricate(Group::BottomLayer::Leader.name, group: groups(:bottom_layer_two), start_on: 1.year.ago).person
        other.roles.first.destroy!

        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.not_to be_able_to(:add_without_request, request)
      end
    end
  end

  context :layer_full do
    let(:role) { Fabricate(Group::TopGroup::LocalGuide.name, group: groups(:top_group)) }

    it "allowed with person in same layer" do
      other = Fabricate(Group::TopLayer::TopAdmin.name, group: groups(:top_layer)).person
      request = create_request(other)

      is_expected.to be_able_to(:approve, request)
      is_expected.to be_able_to(:reject, request)
      is_expected.to be_able_to(:add_without_request, request)
      is_expected.to be_able_to(:index_person_add_requests, groups(:top_layer))
    end

    it "not allowed with person in below layer" do
      other = Fabricate(Group::BottomLayer::Member.name, group: groups(:bottom_layer_one)).person
      request = create_request(other)

      is_expected.not_to be_able_to(:approve, request)
      is_expected.not_to be_able_to(:reject, request)
      is_expected.not_to be_able_to(:add_without_request, request)
      is_expected.not_to be_able_to(:index_person_add_requests, groups(:bottom_layer_one))
    end

    it "allowed with person deleted in same layer" do
      other = Fabricate(Group::TopLayer::TopAdmin.name, group: groups(:top_layer), start_on: 1.year.ago, end_on: 1.month.ago).person
      request = create_request(other)

      is_expected.to be_able_to(:approve, request)
      is_expected.to be_able_to(:reject, request)
      is_expected.to be_able_to(:add_without_request, request)
      is_expected.to be_able_to(:index_person_add_requests, groups(:top_layer))
    end
  end

  context :group_full do
    context "with layer and below read" do
      let(:role) { Fabricate(Group::TopGroup::Secretary.name, group: groups(:top_group)) }

      it "allowed with person in same group" do
        other = Fabricate(Group::TopGroup::Member.name, group: groups(:top_group)).person
        request = create_request(other)

        is_expected.to be_able_to(:approve, request)
        is_expected.to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:top_group))
      end

      it "allowed with person deleted in same group" do
        other = Fabricate(Group::TopGroup::Member.name, group: groups(:top_group), start_on: 1.year.ago, end_on: 1.month.ago).person
        request = create_request(other)

        is_expected.to be_able_to(:approve, request)
        is_expected.to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:top_group))
      end

      it "not allowed with person in same layer" do
        other = Fabricate(Group::TopLayer::TopAdmin.name, group: groups(:top_layer)).person
        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:top_layer))
      end

      it "not allowed with person in below layer" do
        other = Fabricate(Group::BottomLayer::Member.name, group: groups(:bottom_layer_one)).person
        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:bottom_layer_one))
      end

      it "not allowed with non-visible person in below layer" do
        other = Fabricate(Group::BottomGroup::Member.name, group: groups(:bottom_group_one_one)).person
        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
      end
    end

    context "with layer and below read" do
      let(:role) { Fabricate(Group::BottomGroup::Leader.name, group: groups(:bottom_group_one_one)) }

      it "allowed with person in same group" do
        other = Fabricate(Group::BottomGroup::Member.name, group: groups(:bottom_group_one_one)).person
        request = create_request(other)

        is_expected.to be_able_to(:approve, request)
        is_expected.to be_able_to(:reject, request)
        is_expected.to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:bottom_group_one_one))
      end

      it "not allowed with person in same layer" do
        other = Fabricate(Group::BottomGroup::Leader.name, group: groups(:bottom_group_one_two)).person
        request = create_request(other)

        is_expected.not_to be_able_to(:approve, request)
        is_expected.not_to be_able_to(:reject, request)
        is_expected.not_to be_able_to(:add_without_request, request)
        is_expected.not_to be_able_to(:index_person_add_requests, groups(:bottom_layer_one))
      end
    end

    context "herself" do
      let(:role) { Fabricate(Group::BottomLayer::BasicPermissionsOnly.name, group: groups(:bottom_layer_one)) }

      it "allowed" do
        request = create_request(role.person)

        is_expected.to be_able_to(:approve, request)
        is_expected.to be_able_to(:reject, request)
      end
    end

    context "her own" do
      let(:role) { Fabricate(Group::BottomLayer::BasicPermissionsOnly.name, group: groups(:bottom_layer_one)) }

      it "allowed to reject" do
        other = Fabricate(Group::BottomGroup::Leader.name, group: groups(:bottom_group_one_two)).person
        request = create_request(other, requester: role.person)

        is_expected.to be_able_to(:reject, request)
      end
    end
  end

  def create_request(person, requester: people(:bottom_member))
    Person::AddRequest::Group.create!(
      person: person,
      requester: requester,
      body: groups(:bottom_layer_one),
      role_type: Group::BottomLayer::Member.sti_name
    )
  end
end
