# frozen_string_literal: true

#  Copyright (c) 2012-2022, 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.

class GroupAbility < AbilityDsl::Base
  include AbilityDsl::Constraints::Group

  on(Group) do # rubocop:disable Metrics/BlockLength
    permission(:any)
      .may(:read, :index_events, :"index_event/courses", :index_mailing_lists)
      .if_any_role

    permission(:contact_data).may(:index_people).all

    # local people are the ones not visible from above
    permission(:group_read).may(:show_details, :index_people, :index_local_people).in_same_group
    permission(:group_and_below_read)
      .may(:show_details, :index_people, :index_local_people)
      .in_same_group_or_below

    permission(:group_full)
      .may(:index_full_people, :export_events, :"export_event/courses", :reactivate,
        :deleted_subgroups)
      .in_same_group
    permission(:group_full).may(:update).in_same_group_if_active

    permission(:group_and_below_full)
      .may(:index_full_people, :reactivate, :export_events, :"export_event/courses",
        :deleted_subgroups)
      .in_same_group_or_below
    permission(:group_and_below_full).may(:update).in_same_group_or_below_if_active
    permission(:group_and_below_full).may(:create).with_parent_in_same_group_hierarchy
    permission(:group_and_below_full).may(:destroy).in_below_group

    permission(:layer_read)
      .may(:show_details, :index_people, :index_local_people, :index_full_people,
        :index_deep_full_people, :export_events, :"export_event/courses")
      .in_same_layer

    permission(:layer_full).may(:create).with_parent_in_same_layer
    permission(:layer_full).may(:destroy).in_same_layer_except_permission_giving
    permission(:layer_full).may(:index_service_tokens).service_token_in_same_layer
    permission(:layer_full)
      .may(:index_person_add_requests, :index_notes, :index_deleted_people, :show_statistics,
        :index_calendars, :deleted_subgroups).in_same_layer
    permission(:layer_full)
      .may(:update, :reactivate,
        :manage_person_tags, :activate_person_add_requests, :deactivate_person_add_requests)
      .in_same_layer_if_active

    permission(:layer_and_below_read)
      .may(:show_details, :index_people, :index_full_people, :index_deep_full_people,
        :export_subgroups, :export_events, :"export_event/courses")
      .in_same_layer_or_below
    permission(:layer_and_below_read).may(:index_local_people).in_same_layer

    permission(:layer_and_below_full).may(:create).with_parent_in_same_layer_or_below
    permission(:layer_and_below_full).may(:destroy).in_same_layer_or_below_except_permission_giving
    permission(:layer_and_below_full)
      .may(:update, :reactivate, :index_person_add_requests, :index_notes, :show_statistics,
        :manage_person_tags, :index_deleted_people, :deleted_subgroups).in_same_layer_or_below
    permission(:layer_and_below_full).may(:modify_superior).in_below_layers_if_active
    permission(:layer_and_below_full).may(:index_service_tokens).service_token_in_same_layer
    permission(:layer_and_below_full).may(:index_calendars).in_same_layer
    permission(:layer_and_below_full)
      .may(:activate_person_add_requests, :deactivate_person_add_requests)
      .in_same_layer_if_active

    permission(:see_invisible_from_above).may(:index_local_people).in_same_layer_or_below

    permission(:finance).may(:index_invoices).for_finance_layer_ids
    permission(:finance).may(:create_invoices_from_list).in_same_layer_or_below_if_active

    permission(:admin).may(:manage_person_duplicates).if_layer_group_if_active
    permission(:layer_and_below_full).may(:manage_person_duplicates).if_permission_in_layer

    permission(:manual_deletion)
      .may(:manually_delete_people)
      .if_permission_in_layer
    permission(:admin).may(:manually_delete_people).all

    permission(:layer_full).may(:log).in_same_layer_if_active
    permission(:layer_and_below_full).may(:log).in_same_layer_or_below_if_active
    permission(:group_full).may(:log).in_same_group_if_active
    permission(:group_and_below_full).may(:log).in_same_group_or_below_if_active

    permission(:admin).may(:set_main_self_registration_group).in_active_group
    permission(:admin).may(:sync_addresses).on_root_group

    general(:update).group_not_deleted
    general(:index_person_add_requests,
      :activate_person_add_requests,
      :deactivate_person_add_requests)
      .if_layer_group
  end

  def if_permission_in_layer
    group.layer? && permission_in_layer?(group.id)
  end

  def for_finance_layer_ids
    user_finance_layer_ids.include?(subject.id)
  end

  def with_parent_in_same_layer
    parent = group.parent
    !group.layer? && parent && !parent.deleted? && permission_in_layer?(parent.layer_group_id)
  end

  def with_parent_in_same_layer_or_below
    parent = group.parent
    parent && !parent.deleted? && permission_in_layers?(parent.layer_hierarchy.collect(&:id))
  end

  def with_parent_in_same_group_hierarchy
    parent = group.parent
    parent &&
      !parent.deleted? &&
      !group.layer? &&
      permission_in_groups?(parent.local_hierarchy.collect(&:id))
  end

  def in_below_group
    !permission_in_group?(group.id) &&
      permission_in_groups?(group.local_hierarchy.collect(&:id))
  end

  def in_same_layer_except_permission_giving
    in_same_layer && except_permission_giving
  end

  def in_same_layer_or_below_except_permission_giving
    in_same_layer_or_below && except_permission_giving
  end

  def except_permission_giving
    [:layer_and_below_full, :layer_full].none? do |permission|
      user_context.permission_group_ids(permission).include?(group.id) ||
        user_context.permission_layer_ids(permission).include?(group.id)
    end
  end

  def in_below_layers
    permission_in_layers?(group.upper_layer_hierarchy.collect(&:id))
  end

  def in_below_layers_if_active
    in_below_layers && in_active_group
  end

  # Member is a general role kind. Return true if user has any member role anywhere.
  def if_member
    user.roles.any? { |r| r.class.member? }
  end

  def service_token_in_same_layer
    in_same_layer
  end

  private

  def group
    subject
  end
end
