#  Copyright (c) 2012-2013, Jungwacht Blauring 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 GroupAbility do
  subject { ability }

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

  before do
    allow(FeatureGate).to receive(:enabled?).and_return(true)
  end

  context "layer and below full" do
    let(:role) { Fabricate(Group::TopGroup::Leader.name.to_sym, group: groups(:top_group)) }

    before do
      allow(Group::TopGroup::Leader).to receive(:permissions).and_return([:layer_and_below_full])
    end

    context "without specific group" do
      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, Group.new)
      end
    end

    context "in own group" do
      let(:group) { role.group }

      it "may create subgroup" do
        is_expected.to be_able_to(:create, group.children.new)
      end

      it "may edit group" do
        is_expected.to be_able_to(:update, group)
      end

      it "may not modify superior" do
        is_expected.not_to be_able_to(:modify_superior, group)
      end

      it "may destroy group" do
        other = Fabricate(Group::TopGroup.name.to_sym, parent: group.parent)
        is_expected.to be_able_to(:destroy, other)
      end

      it "may not destroy permission giving group" do
        is_expected.not_to be_able_to(:destroy, group)
      end

      it "may show person notes" do
        is_expected.to be_able_to(:index_notes, group)
      end

      it "may show statistics" do
        is_expected.to be_able_to(:show_statistics, group)
      end

      it "may show deleted subgroups" do
        is_expected.to be_able_to(:deleted_subgroups, group)
      end

      it "may show service_tokens" do
        is_expected.to be_able_to(:index_service_tokens, group)
      end

      it "may manage person tags" do
        is_expected.to be_able_to(:manage_person_tags, group)
      end

      it "may view log" do
        is_expected.to be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.to_not be_able_to(:manually_delete_people, group)
      end
    end

    context "in group from lower layer" do
      let(:group) { groups(:bottom_layer_one) }

      it "may create subgroup" do
        is_expected.to be_able_to(:create, group.children.new)
      end

      it "may edit group" do
        is_expected.to be_able_to(:update, group)
      end

      it "may modify superior" do
        is_expected.to be_able_to(:modify_superior, group)
      end

      it "may modify superior in new group" do
        g = Group::BottomLayer.new
        g.parent = group.parent
        is_expected.to be_able_to(:modify_superior, g)
      end

      it "may destroy group" do
        is_expected.to be_able_to(:destroy, group)
      end

      it "may show person notes" do
        is_expected.to be_able_to(:index_notes, group)
      end

      it "may show statistics" do
        is_expected.to be_able_to(:show_statistics, group)
      end

      it "may show deleted subgroups" do
        is_expected.to be_able_to(:deleted_subgroups, group)
      end

      it "may not index service tokens" do
        is_expected.not_to be_able_to(:index_service_tokens, group)
      end

      it "may manage person tags" do
        is_expected.to be_able_to(:manage_person_tags, group)
      end

      it "may view log" do
        is_expected.to be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.to_not be_able_to(:manually_delete_people, group)
      end
    end
  end

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

    before do
      allow(Group::BottomLayer::Leader).to receive(:permissions).and_return([:layer_and_below_full])
    end

    context "in own group" do
      let(:group) { role.group }

      it "may edit group" do
        is_expected.to be_able_to(:update, group)
      end

      it "may not modify superior" do
        is_expected.not_to be_able_to(:modify_superior, group)
      end

      it "may show person notes" do
        is_expected.to be_able_to(:index_notes, group)
      end

      it "may show statistics" do
        is_expected.to be_able_to(:show_statistics, group)
      end

      it "may show deleted subgroups" do
        is_expected.to be_able_to(:deleted_subgroups, group)
      end

      it "may show service tokens" do
        is_expected.to be_able_to(:index_service_tokens, group)
      end

      it "may manage person tags" do
        is_expected.to be_able_to(:manage_person_tags, group)
      end

      it "may view log" do
        is_expected.to be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.not_to be_able_to(:manually_delete_people, group)
      end
    end

    context "in top layer" do
      let(:group) { groups(:top_group) }

      it "may not show statistics" do
        is_expected.not_to be_able_to(:show_statistics, group)
      end

      it "may not show deleted subgroups" do
        is_expected.not_to be_able_to(:deleted_subgroups, group)
      end

      it "may not show person notes" do
        is_expected.not_to be_able_to(:index_notes, group)
      end

      it "may not show service tokens" do
        is_expected.not_to be_able_to(:index_service_tokens, group)
      end

      it "may not manage person tags" do
        is_expected.not_to be_able_to(:manage_person_tags, group)
      end

      it "may not view log" do
        is_expected.to_not be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.to_not be_able_to(:manually_delete_people, group)
      end
    end
  end

  context "layer full" do
    let(:role) { Fabricate(Group::TopGroup::LocalGuide.name.to_sym, group: groups(:top_group)) }

    context "without specific group" do
      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, Group.new)
      end
    end

    context "in own group" do
      let(:group) { role.group }

      it "may create subgroup" do
        is_expected.to be_able_to(:create, group.children.new)
      end

      it "may edit group" do
        is_expected.to be_able_to(:update, group)
      end

      it "may not destroy permission giving group" do
        is_expected.not_to be_able_to(:destroy, group)
      end

      it "may not modify superior" do
        is_expected.not_to be_able_to(:modify_superior, group)
      end

      it "may not create sublayer" do
        is_expected.not_to be_able_to(:create, Group::BottomLayer.new(parent_id: group.layer_group_id))
      end

      it "may show person notes" do
        is_expected.to be_able_to(:index_notes, group)
      end

      it "may show statistics" do
        is_expected.to be_able_to(:show_statistics, group)
      end

      it "may show deleted subgroups" do
        is_expected.to be_able_to(:deleted_subgroups, group)
      end

      it "may show service tokens" do
        is_expected.to be_able_to(:index_service_tokens, group)
      end

      it "may manage person tags" do
        is_expected.to be_able_to(:manage_person_tags, group)
      end

      it "may view log" do
        is_expected.to be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.to_not be_able_to(:manually_delete_people, group)
      end
    end

    context "in group from same layer" do
      let(:group) { Fabricate(Group::TopGroup.name.to_sym, parent: groups(:top_layer)) }

      it "may create subgroup" do
        is_expected.to be_able_to(:create, group.children.new)
      end

      it "may edit group" do
        is_expected.to be_able_to(:update, group)
      end

      it "may destroy group" do
        is_expected.to be_able_to(:destroy, group)
      end

      it "may show person notes" do
        is_expected.to be_able_to(:index_notes, group)
      end

      it "may show statistics" do
        is_expected.to be_able_to(:show_statistics, group)
      end

      it "may show deleted subgroups" do
        is_expected.to be_able_to(:deleted_subgroups, group)
      end

      it "may show service tokens" do
        is_expected.to be_able_to(:index_service_tokens, group)
      end

      it "may manage person tags" do
        is_expected.to be_able_to(:manage_person_tags, group)
      end

      it "may view log" do
        is_expected.to be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.to_not be_able_to(:manually_delete_people, group)
      end
    end

    context "in group from lower layer" do
      let(:group) { groups(:bottom_layer_one) }

      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, group.children.new)
      end

      it "may not edit group" do
        is_expected.not_to be_able_to(:update, group)
      end

      it "may not destroy group" do
        is_expected.not_to be_able_to(:destroy, group)
      end

      it "may not show person notes" do
        is_expected.not_to be_able_to(:index_notes, group)
      end

      it "may not show statistics" do
        is_expected.not_to be_able_to(:show_statistics, group)
      end

      it "may not show deleted subgroups" do
        is_expected.not_to be_able_to(:deleted_subgroups, group)
      end

      it "may not show service tokens" do
        is_expected.not_to be_able_to(:index_service_tokens, group)
      end

      it "may not manage person tags" do
        is_expected.not_to be_able_to(:manage_person_tags, group)
      end

      it "may not view log" do
        is_expected.to_not be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.to_not be_able_to(:manually_delete_people, group)
      end
    end
  end

  context "group and below full" do
    let(:role) { Fabricate(Group::TopLayer::TopAdmin.name.to_sym, group: groups(:top_layer)) }

    context "in own group" do
      let(:group) { role.group }

      it "may create subgroup" do
        is_expected.to be_able_to(:create, group.children.new)
      end

      it "may not create sub layer" do
        is_expected.not_to be_able_to(:create, Group::BottomLayer.new(parent: role.group))
      end

      it "may edit group" do
        is_expected.to be_able_to(:update, group)
      end

      it "may edit below group" do
        is_expected.to be_able_to(:update, groups(:toppers))
      end

      it "may not edit below layer" do
        is_expected.not_to be_able_to(:update, groups(:bottom_layer_one))
      end

      it "may not destroy group" do
        is_expected.not_to be_able_to(:destroy, group)
      end

      it "may destroy below group" do
        is_expected.to be_able_to(:destroy, groups(:toppers))
      end

      it "may not modify superior" do
        is_expected.not_to be_able_to(:modify_superior, group)
      end

      it "may not modify superior in below group" do
        is_expected.not_to be_able_to(:modify_superior, groups(:toppers))
      end

      it "may not show person notes" do
        is_expected.not_to be_able_to(:index_notes, group)
      end

      it "may not show statistics" do
        is_expected.not_to be_able_to(:show_statistics, group)
      end

      it "may show deleted subgroups" do
        is_expected.to be_able_to(:deleted_subgroups, group)
      end

      it "mayi not show service tokens" do
        is_expected.not_to be_able_to(:index_service_tokens, group)
      end

      it "may not manage person tags" do
        is_expected.not_to be_able_to(:manage_person_tags, group)
      end

      it "may view log" do
        is_expected.to be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.to_not be_able_to(:manually_delete_people, group)
      end
    end

    context "without specific group" do
      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, Group.new)
      end

      it "may not show deleted subgroups" do
        is_expected.not_to be_able_to(:deleted_subgroups, Group.new)
      end
    end

    context "in other group from same layer" do
      let(:group) { groups(:top_group) }

      it "may create subgroup" do
        is_expected.to be_able_to(:create, group.children.new)
      end

      it "may not show deleted subgroups" do
        is_expected.not_to be_able_to(:deleted_subgroups, group.children.new)
      end
    end

    context "in group from lower layer" do
      let(:group) { groups(:bottom_layer_one) }

      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, group.children.new)
      end

      it "may not show deleted subgroups" do
        is_expected.not_to be_able_to(:deleted_subgroups, group)
      end
    end
  end

  context "group full" do
    let(:role) { Fabricate(Group::GlobalGroup::Leader.name.to_sym, group: groups(:toppers)) }

    context "in own group" do
      let(:group) { role.group }

      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, group.children.new)
      end

      it "may edit group" do
        is_expected.to be_able_to(:update, group)
      end

      it "may not destroy group" do
        is_expected.not_to be_able_to(:destroy, group)
      end

      it "may not modify superior" do
        is_expected.not_to be_able_to(:modify_superior, group)
      end

      it "may not show person notes" do
        is_expected.not_to be_able_to(:index_notes, group)
      end

      it "may not show statistics" do
        is_expected.not_to be_able_to(:show_statistics, group)
      end

      it "may show deleted subgroups" do
        is_expected.to be_able_to(:deleted_subgroups, group)
      end

      it "may not show service tokens" do
        is_expected.not_to be_able_to(:index_service_tokens, group)
      end

      it "may not manage person tags" do
        is_expected.not_to be_able_to(:manage_person_tags, group)
      end

      it "may view log" do
        is_expected.to be_able_to(:log, group)
      end

      it "may not manually delete people" do
        is_expected.to_not be_able_to(:manually_delete_people, group)
      end
    end

    context "without specific group" do
      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, Group.new)
      end

      it "may not show deleted subgroups" do
        is_expected.not_to be_able_to(:deleted_subgroups, Group.new)
      end
    end

    context "in other group from same layer" do
      let(:group) { groups(:top_group) }

      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, group.children.new)
      end

      it "may not show deleted subgroups" do
        is_expected.not_to be_able_to(:deleted_subgroups, group)
      end
    end

    context "in group from lower layer" do
      let(:group) { groups(:bottom_layer_one) }

      it "may not create subgroup" do
        is_expected.not_to be_able_to(:create, group.children.new)
      end

      it "may not show deleted subgroups" do
        is_expected.not_to be_able_to(:deleted_subgroups, group)
      end
    end
  end

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

    it "may not index invoices on random group" do
      is_expected.not_to be_able_to(:index_invoices, Group.new)
    end

    it "may not index in own group" do
      is_expected.not_to be_able_to(:index_invoices, groups(:top_group))
    end

    it "may not index in bottom layer group" do
      is_expected.not_to be_able_to(:index_invoices, groups(:bottom_layer_one))
    end

    it "may index in top layer layer group" do
      is_expected.to be_able_to(:index_invoices, groups(:top_layer))
    end
  end

  context "see_invisible_from_above" do
    let(:role) { Fabricate(Group::TopGroup::InvisiblePeopleManager.name.to_sym, group: groups(:top_group)) }

    it "may index_local_people in own group" do
      is_expected.to be_able_to(:index_local_people, groups(:top_group))
    end

    it "may index_local_people in same layer group" do
      other = Fabricate(Group::TopGroup.name.to_sym, parent: groups(:top_layer))
      is_expected.to be_able_to(:index_local_people, other)
    end

    it "may index_local_people in lower layer group" do
      is_expected.to be_able_to(:index_local_people, groups(:bottom_group_one_one))
    end

    it "may not index_local_people in group outside own layer hierarchy" do
      other_layer = Fabricate(Group::TopLayer.name.to_sym)
      other = Fabricate(Group::TopGroup.name.to_sym, parent: other_layer)
      is_expected.not_to be_able_to(:index_local_people, other)
    end
  end

  context "deleted group" do
    let(:group) { groups(:bottom_layer_two) }
    let(:role) { Fabricate(Group::BottomLayer::Leader.name.to_sym, group: group) }

    before do
      group.children.each { |g| g.destroy }
      group.destroy
    end

    it "may not create subgroup" do
      is_expected.not_to be_able_to(:create, group.children.new)
    end

    it "may not update group" do
      is_expected.not_to be_able_to(:update, group)
    end

    it "may reactivate group" do
      is_expected.to be_able_to(:reactivate, group)
    end
  end

  context :manage_person_duplicates do
    let(:top_layer) { groups(:top_layer) }
    let(:top_group) { groups(:top_group) }
    let(:bottom_layer) { groups(:bottom_layer_one) }
    let(:bottom_group) { groups(:bottom_group_one_one) }

    context :permission_admin do
      before do
        allow(Group::TopLayer::TopAdmin).to receive(:permissions)
          .and_return([:admin])
      end

      let(:role) { Fabricate("Group::TopLayer::TopAdmin", group: top_layer) }

      it "may list duplicates on top layer" do
        is_expected.to be_able_to(:manage_person_duplicates, top_layer)
      end

      it "may list duplicates on lower layer" do
        is_expected.to be_able_to(:manage_person_duplicates, bottom_layer)
      end

      it "may not list duplicates on non layer" do
        is_expected.not_to be_able_to(:manage_person_duplicates, top_group)
      end
    end

    context :permission_layer_and_below_full do
      let(:role) { Fabricate("Group::BottomLayer::Leader", group: bottom_layer) }

      it "may not list duplicates on top layer" do
        is_expected.not_to be_able_to(:manage_person_duplicates, top_layer)
      end

      it "may list duplicates on bottom layer" do
        is_expected.to be_able_to(:manage_person_duplicates, bottom_layer)
      end

      it "may not list duplicates on non layer" do
        is_expected.not_to be_able_to(:manage_person_duplicates, bottom_group)
      end
    end
  end

  context "person without roles" do
    let(:person_without_roles) { Fabricate(:person, primary_group: groups(:top_layer)) }
    let(:ability) { Ability.new(person_without_roles) }

    it "may not read any group" do
      is_expected.not_to be_able_to(:read, groups(:bottom_layer_one))
    end

    it "may not show deleted subgroups" do
      is_expected.not_to be_able_to(:deleted_subgroups, groups { :bottom_layer_one })
    end

    it "may not show_details any group" do
      is_expected.not_to be_able_to(:show_details, groups(:bottom_layer_one))
    end
  end

  describe :sync_addresses do
    it "admin permission may sync on root group" do
      expect(Ability.new(people(:top_leader))).to be_able_to(:sync_addresses, groups(:top_layer))
    end

    it "admin permission may not sync on non root group" do
      expect(Ability.new(people(:top_leader))).not_to be_able_to(:sync_addresses, groups(:top_group))
    end

    it "layer_full permission may not sync on root group" do
      expect(Ability.new(people(:bottom_member))).not_to be_able_to(:sync_addresses, groups(:top_layer))
    end
  end

  describe :manual_deletion do
    let(:role) { Fabricate(Group::TopLayer::TopAdmin.name.to_sym, group: groups(:top_layer)) }

    before do
      allow(Group::TopLayer::TopAdmin).to receive(:permissions).and_return([:manual_deletion])
    end

    it "may manually delete people" do
      is_expected.to be_able_to(:manually_delete_people, groups(:top_layer))
    end

    it "may not manually delete people in layer below" do
      is_expected.not_to be_able_to(:manually_delete_people, groups(:bottom_layer_one))
    end
  end
end
