#  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 MailingListAbility do
  let(:user) { role.person }
  let(:group) { role.group }
  let(:list) { Fabricate(:mailing_list, group: group) }

  subject { Ability.new(user.reload) }

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

    context "in own group" do
      it "may show mailing lists" do
        is_expected.to be_able_to(:show, list)
      end

      it "may update mailing lists" do
        is_expected.to be_able_to(:update, list)
      end

      it "may index subscriptions" do
        is_expected.to be_able_to(:index_subscriptions, list)
      end

      it "may create subscriptions" do
        is_expected.to be_able_to(:create, list.subscriptions.new)
      end

      it "may export empty subscriptions" do
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with role types from lower groups" do
        create_group_subscription(Group::TopGroup::Leader, Group::GlobalGroup::Member)
        is_expected.to be_able_to(:export_subscriptions, list)
      end
    end

    context "in group in same layer" do
      let(:group) { groups(:top_layer) }

      it "may show mailing lists" do
        is_expected.to be_able_to(:show, list)
      end

      it "may update mailing lists" do
        is_expected.to be_able_to(:update, list)
      end

      it "may index subscriptions" do
        is_expected.to be_able_to(:index_subscriptions, list)
      end

      it "may create subscriptions" do
        is_expected.to be_able_to(:create, list.subscriptions.new)
      end

      it "may export empty subscriptions" do
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with role types from lower layers" do
        create_group_subscription(Group::TopGroup::Leader, Group::BottomGroup::Member, Group::GlobalGroup::Leader)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with events from lower layers" do
        create_event_subscription(groups(:bottom_layer_one))
        is_expected.to be_able_to(:export_subscriptions, list)
      end
    end

    context "in global group in same layer" do
      let(:group) { groups(:toppers) }

      it "may export subscriptions with role types from local layers" do
        create_group_subscription(Group::GlobalGroup::Member)
        is_expected.to be_able_to(:export_subscriptions, list)
      end
    end

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

      it "may not show mailing lists" do
        is_expected.not_to be_able_to(:show, list)
      end

      it "may not update mailing lists" do
        is_expected.not_to be_able_to(:update, list)
      end

      it "may not index subscriptions" do
        is_expected.not_to be_able_to(:index_subscriptions, list)
      end

      it "may not export subscriptions" do
        is_expected.not_to be_able_to(:export_subscriptions, list)
      end

      it "may not create subscriptions" do
        is_expected.not_to be_able_to(:create, list.subscriptions.new)
      end
    end

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

      it "may not show mailing lists" do
        is_expected.not_to be_able_to(:show, list)
      end

      it "may not update mailing lists" do
        is_expected.not_to be_able_to(:update, list)
      end

      it "may not index subscriptions" do
        is_expected.not_to be_able_to(:index_subscriptions, list)
      end

      it "may not export subscriptions" do
        is_expected.not_to be_able_to(:export_subscriptions, list)
      end

      it "may not create subscriptions" do
        is_expected.not_to be_able_to(:create, list.subscriptions.new)
      end
    end
  end

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

    context "in own group" do
      it "may show mailing lists" do
        is_expected.to be_able_to(:show, list)
      end

      it "may update mailing lists" do
        is_expected.to be_able_to(:update, list)
      end

      it "may index subscriptions" do
        is_expected.to be_able_to(:index_subscriptions, list)
      end

      it "may create subscriptions" do
        is_expected.to be_able_to(:create, list.subscriptions.new)
      end

      it "may export empty subscriptions" do
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with role types from local groups" do
        create_group_subscription(Group::BottomLayer::Member, Group::BottomGroup::Member)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with role types from global groups if bottom layer" do
        create_group_subscription(Group::GlobalGroup::Leader)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with events from lower groups" do
        create_event_subscription(groups(:bottom_group_one_one))
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with events shared with neighbouring group" do
        create_event_subscription(*groups(:bottom_layer_one, :bottom_layer_two))
        is_expected.to be_able_to(:export_subscriptions, list)
      end
    end

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

      it "may export subscriptions with role types from local groups" do
        create_group_subscription(Group::TopGroup::Member, Group::TopLayer::TopAdmin)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may not export subscriptions with role types from below groups" do
        create_group_subscription(Group::BottomLayer::Member, Group::BottomGroup::Member)
        is_expected.not_to be_able_to(:export_subscriptions, list)
      end

      it "may not export subscriptions with role types from global groups" do
        create_group_subscription(Group::GlobalGroup::Leader)
        is_expected.not_to be_able_to(:export_subscriptions, list)
      end

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

        it "may not export subscriptions with events from lower layers" do
          create_event_subscription(groups(:bottom_layer_one))
          is_expected.not_to be_able_to(:export_subscriptions, list)
        end
      end
    end

    context "in global group in same layer" do
      let(:group) { Fabricate(Group::GlobalGroup.name, parent: groups(:bottom_layer_one)) }

      it "may export subscriptions with role types from local layers" do
        create_group_subscription(Group::GlobalGroup::Member)
        is_expected.to be_able_to(:export_subscriptions, list)
      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
      it "may show mailing lists" do
        is_expected.to be_able_to(:show, list)
      end

      it "may update mailing lists" do
        is_expected.to be_able_to(:update, list)
      end

      it "may index subscriptions" do
        is_expected.to be_able_to(:index_subscriptions, list)
      end

      it "may create subscriptions" do
        is_expected.to be_able_to(:create, list.subscriptions.new)
      end

      it "may export empty subscriptions" do
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with role types from local groups" do
        create_group_subscription(Group::TopGroup::Member)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with role types from global groups" do
        create_group_subscription(Group::TopGroup::Member, Group::GlobalGroup::Leader)
        is_expected.not_to be_able_to(:export_subscriptions, list)
      end

      it "may not export subscriptions with role types from lower layers" do
        create_group_subscription(Group::TopGroup::Leader, Group::BottomGroup::Leader)
        is_expected.not_to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with events from lower groups" do
        create_event_subscription(groups(:top_group))
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may not export subscriptions with events from lower layer" do
        create_event_subscription(groups(:bottom_group_one_one))
        is_expected.not_to be_able_to(:export_subscriptions, list)
      end
    end

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

      it "may show mailing lists" do
        is_expected.to be_able_to(:show, list)
      end

      it "may update mailing lists" do
        is_expected.to be_able_to(:update, list)
      end

      it "may index subscriptions" do
        is_expected.to be_able_to(:index_subscriptions, list)
      end

      it "may create subscriptions" do
        is_expected.to be_able_to(:create, list.subscriptions.new)
      end

      it "may export empty subscriptions" do
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with role types from local layers" do
        create_group_subscription(Group::TopGroup::Member)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with events" do
        create_event_subscription(groups(:top_group))
        is_expected.to be_able_to(:export_subscriptions, list)
      end
    end

    context "in global group below" do
      let(:group) { groups(:toppers) }

      it "may export subscriptions with role types from local layers" do
        create_group_subscription(Group::GlobalGroup::Member)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with events" do
        create_event_subscription(groups(:toppers))
        is_expected.to be_able_to(:export_subscriptions, list)
      end
    end

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

      it "may not show mailing lists" do
        is_expected.not_to be_able_to(:show, list)
      end

      it "may not update mailing lists" do
        is_expected.not_to be_able_to(:update, list)
      end

      it "may not index subscriptions" do
        is_expected.not_to be_able_to(:index_subscriptions, list)
      end

      it "may not export subscriptions" do
        is_expected.not_to be_able_to(:export_subscriptions, list)
      end

      it "may not create subscriptions" do
        is_expected.not_to be_able_to(:create, list.subscriptions.new)
      end
    end

    context "for destroyed group" do
      let(:group) { groups(:toppers) }

      before {
        list
        group.destroy
      }

      it "may not create mailing list" do
        is_expected.not_to be_able_to(:create, list)
      end

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

      it "may not create subscription" do
        is_expected.not_to be_able_to(:create, list.subscriptions.new)
      end
    end
  end

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

    context "in own group" do
      it "may show mailing lists" do
        is_expected.to be_able_to(:show, list)
      end

      it "may update mailing lists" do
        is_expected.to be_able_to(:update, list)
      end

      it "may index subscriptions" do
        is_expected.to be_able_to(:index_subscriptions, list)
      end

      it "may export empty subscriptions" do
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with local role types" do
        create_group_subscription(Group::GlobalGroup::Member)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may not export subscriptions with lower role types" do
        group = groups(:top_group)
        role = Fabricate(Group::TopGroup::Secretary.name.to_sym, group: group)
        list = Fabricate(:mailing_list, group: group)
        ability = Ability.new(role.person.reload)
        Subscription.create!(mailing_list: list, subscriber: group, role_types: [Group::TopGroup::Member, Group::GlobalGroup::Member])

        expect(ability).not_to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with events" do
        create_event_subscription(groups(:toppers))
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may export subscriptions with events shared with siblings" do
        other = Fabricate(Group::GlobalGroup.name, parent: groups(:top_layer))
        create_event_subscription(group, other)
        is_expected.to be_able_to(:export_subscriptions, list)
      end

      it "may not export subscriptions with events in lower groups" do
        group = groups(:top_group)
        role = Fabricate(Group::TopGroup::Secretary.name.to_sym, group: group)
        list = Fabricate(:mailing_list, group: group)
        ability = Ability.new(role.person.reload)
        child = Fabricate(Group::GlobalGroup.name, parent: group)
        event = Fabricate(:event, groups: [child], dates: [Fabricate(:event_date, start_at: Time.zone.today)])
        group.reload
        Subscription.create!(mailing_list: list, subscriber: event)

        expect(ability).not_to be_able_to(:export_subscriptions, list)
      end

      it "may create subscriptions" do
        is_expected.to be_able_to(:create, list.subscriptions.new)
      end
    end

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

      it "may not show mailing lists" do
        is_expected.not_to be_able_to(:show, list)
      end

      it "may not update mailing lists" do
        is_expected.not_to be_able_to(:update, list)
      end

      it "may not index subscriptions" do
        is_expected.not_to be_able_to(:index_subscriptions, list)
      end

      it "may not create subscriptions" do
        is_expected.not_to be_able_to(:create, list.subscriptions.new)
      end
    end

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

      it "may not show mailing lists" do
        is_expected.not_to be_able_to(:show, list)
      end

      it "may not update mailing lists" do
        is_expected.not_to be_able_to(:update, list)
      end

      it "may not index subscriptions" do
        is_expected.not_to be_able_to(:index_subscriptions, list)
      end

      it "may not create subscriptions" do
        is_expected.not_to be_able_to(:create, list.subscriptions.new)
      end
    end
  end

  def create_group_subscription(*role_types)
    Subscription.create!(mailing_list: list, subscriber: group, role_types: role_types)
  end

  def create_event_subscription(*groups)
    event = Fabricate(:event, groups: groups, dates: [Fabricate(:event_date, start_at: Time.zone.today)])
    Subscription.create!(mailing_list: list, subscriber: event)
  end
end
