# frozen_string_literal: true

#  Copyright (c) 2012-2023, 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"

# Missing translation number by rails default
MISSING_TRANSLATION = -1152921504606846976

describe GroupDecorator, :draper_with_helpers do
  include Rails.application.routes.url_helpers

  let(:context) { double("context") }
  let(:model) { groups(:top_group) }

  subject(:decorator) { GroupDecorator.new(model) }

  describe "possible roles" do
    let(:role_types) {
      [Role::External,
        Group::TopGroup::GroupManager,
        Group::TopGroup::InvisiblePeopleManager,
        Group::TopGroup::Leader,
        Group::TopGroup::LocalGuide,
        Group::TopGroup::LocalSecretary,
        Group::TopGroup::Member,
        Group::TopGroup::Secretary]
    }

    before do
      allow(view_context).to receive(:action_name).and_return("update")
    end

    let(:view_context) { Draper::ViewContext.current }

    def build_role(type) = model.roles.build(type: type)

    it "contains all types if all checks pass" do
      allow(view_context).to receive(:can?).and_return(true)
      expect(decorator.possible_roles).to eq role_types
    end

    it "excludes restricted role" do
      allow(view_context).to receive(:can?).and_return(true)
      allow(role_types.first).to receive(:restricted?).and_return(true)
      expect(decorator.possible_roles).to eq role_types[1..]
    end

    describe "visible from above and index_local_people" do
      it "includes role not visible from above if user can index_local_people" do
        allow(view_context).to receive(:can?).and_return(true)
        expect(decorator.possible_roles).to eq role_types
      end

      it "excludes role not visible from above if user cannot index_local_people" do
        allow(view_context).to receive(:can?) do |action, subject|
          next false if action == :index_local_people
          true
        end
        expect(decorator.possible_roles).to eq role_types[1..]
      end
    end

    describe "action based permissions" do
      it "excludes roles not permitted for action" do
        allow(view_context).to receive(:can?) do |action, subject|
          next false unless action == :update && subject.type == "Group::TopGroup::Leader"
          true
        end
        expect(decorator.possible_roles).to eq [Group::TopGroup::Leader]
      end

      [:new, :define_mapping, :preview].each do |action|
        it "excludes roles not permitted for inferred #{action} create like action" do
          allow(view_context).to receive(:action_name).and_return(action.to_s)

          allow(view_context).to receive(:can?) do |action, subject|
            next false unless action == :create && subject.type == "Group::TopGroup::Leader"
            true
          end
          expect(decorator.possible_roles).to eq [Group::TopGroup::Leader]
        end
      end

      [:edit].each do |action|
        it "excludes roles not permitted for inferred #{action} update like action" do
          allow(view_context).to receive(:action_name).and_return(action.to_s)

          allow(view_context).to receive(:can?) do |action, subject|
            next false unless action == :update && subject.type == "Group::TopGroup::Leader"
            true
          end
          expect(decorator.possible_roles).to eq [Group::TopGroup::Leader]
        end
      end
    end
  end

  context "Top Group Roles" do
    let(:group) { groups(:top_group) }

    it "sorts the list alphabetically by label" do
      common_arguments = {count: 1, scope: [:activerecord, :models]}

      [[:"group/top_group/leader", "Leader", "X"],
        [:"group/top_group/local_guide", "Local guide", "F"],
        [:"group/top_group/secretary", "Secretary", "D"],
        [:"group/top_group/local_secretary", "Local secretary", "L"],
        [:"group/top_group/group_manager", "Group manager", "E"],
        [:"group/top_group/member", "Member", "M"],
        [:"group/top_group/invisible_people_manager", "Invisible people manager", "G"],
        [:"role/external", "External", "H"]].each do |class_path, class_name, sort_key|
        # Key is written into defaults hash by default per rails 7.1
        expect(I18n).to receive(:translate).with(class_path,
          {default: [:role, MISSING_TRANSLATION]}.merge(common_arguments))
          .twice.and_return(sort_key)
      end

      expected_role_list_label = %w[D E F G H L M X]
      expected_role_list_class = %w[Group::TopGroup::Secretary Group::TopGroup::GroupManager
        Group::TopGroup::LocalGuide
        Group::TopGroup::InvisiblePeopleManager
        Role::External
        Group::TopGroup::LocalSecretary
        Group::TopGroup::Member
        Group::TopGroup::Leader]

      role_types = subject.role_types
      expect(role_types.map(&:label)).to eq(expected_role_list_label)
      expect(role_types.map(&:name)).to eq(expected_role_list_class)
    end
  end

  describe "allowed_roles_for_self_registration" do
    let(:group) { model }

    subject { decorator.allowed_roles_for_self_registration }

    it { is_expected.to eq [Role::External] }

    describe "allowed_roles_for_self_registration with override in wagon" do
      let(:group) { groups(:bottom_group_one_one) }

      before do
        stub_const("Role::Types::AllowedPermissionsForSelfRegistration", [:group_read])
      end

      it "should include roles which have no permissions" do
        expect(Role::Types::AllowedPermissionsForSelfRegistration).to contain_exactly(:group_read)
        is_expected.to contain_exactly(Role::External)
      end
    end
  end

  describe "supports_self_registration?" do
    it "returns true if it has any allowed_roles_for_self_registration" do
      allow(subject).to receive(:allowed_roles_for_self_registration).and_return([double])
      expect(subject.supports_self_registration?).to be true
    end

    it "returns false if it has no allowed_roles_for_self_registration" do
      allow(subject).to receive(:allowed_roles_for_self_registration).and_return([])
      expect(subject.supports_self_registration?).to be false
    end
  end

  describe "selecting attributes" do
    class DummyGroup < Group # rubocop:disable Lint/ConstantDefinitionInBlock
      self.used_attributes += [:foo, :bar]
    end

    let(:model) { DummyGroup.new }

    before do
      allow(subject).to receive_messages(h: context)
    end

    it "#used_attributes selects via .attr_used?" do
      expect(subject.used_attributes(:foo, :bar)).to eq %w[foo bar]
    end

    it "#modifiable_attributes we can :modify_superior" do
      expect(context).to receive(:can?).with(:modify_superior, subject).and_return(true)
      expect(subject.modifiable_attributes(:foo, :bar)).to eq %w[foo bar]
    end

    it "#modifiable_attributes filters attributes if we cannot :modify_superior" do
      allow(model.class).to receive_messages(superior_attributes: [:foo])
      expect(context).to receive(:can?).with(:modify_superior, subject).and_return(false)
      expect(subject.modifiable_attributes(:foo, :bar)).to eq %w[bar]
    end

    it "#modifiable? we can :modify_superior" do
      expect(context).to receive(:can?).with(:modify_superior, subject).and_return(true)
      expect(subject.modifiable?(:foo) { |val| val }).to eq %w[foo]
    end

    it "#modifiable? filters attributes if we cannot :modify_superior" do
      allow(model.class).to receive_messages(superior_attributes: [:foo])
      expect(context).to receive(:can?).with(:modify_superior, subject).and_return(false)
      expect(subject.modifiable_attributes(:foo) { |val| val }).to eq %w[]
    end
  end

  describe "subgroups" do
    let(:model) { groups(:bottom_layer_one) }

    its(:subgroup_ids) do
      should match_array(
        [
          groups(:bottom_layer_one),
          groups(:bottom_group_one_one),
          groups(:bottom_group_one_one_one),
          groups(:bottom_group_one_two)
        ].collect(&:id)
      )
    end
  end

  describe "primary_group_toggle_link" do
    let(:person) { people(:top_leader) }
    let(:html) { GroupDecorator.new(model).primary_group_toggle_link(person, model) }

    subject(:node) { Capybara::Node::Simple.new(html) }

    it "renders link with icon and text" do
      expect(node).to have_link "Hauptgruppe setzen"
      expect(node).to have_css "a i.fas.fa-star[filled=true]"
      expect(node.find("a")["href"]).to eq(primary_group_group_person_path(model, person,
        primary_group_id: model.id))
    end
  end

  context "archived groups" do
    let(:model) do
      groups(:bottom_group_one_two).tap { |g| g.update(archived_at: 1.day.ago) }
    end

    it "suffix to_s" do
      expect(subject.to_s).to end_with(" (archiviert)")
    end

    it "suffix to_s with argument" do
      expect(subject.to_s(:default)).to end_with(" (archiviert)")
    end

    it "suffix to_s with argument" do
      expect(subject.to_s(:long_format)).to end_with(" (archiviert)")
    end

    it "suffix display_name" do
      expect(subject.display_name).to end_with(" (archiviert)")
    end
  end

  context "not archived groups" do
    let(:model) do
      groups(:bottom_group_one_two).tap { |g| g.update(archived_at: nil) }
    end

    it "suffix to_s" do
      expect(subject.to_s).to_not end_with(" (archiviert)")
    end

    it "suffix to_s with argument" do
      expect(subject.to_s(:default)).to_not end_with(" (archiviert)")
    end

    it "suffix to_s with argument" do
      expect(subject.to_s(:long_format)).to_not end_with(" (archiviert)")
    end

    it "suffix display_name" do
      expect(subject.display_name).to_not end_with(" (archiviert)")
    end
  end

  it "reads the nextcloud_url from the next organizing group in the hierarchy" do
    expect(subject.nextcloud_url).to be_nil

    subject.layer_group.nextcloud_url = "http://example.org"
    subject.layer_group.save!
    subject.reload

    expect(subject.nextcloud_organizer).to eq subject.layer_group
    expect(subject.nextcloud_organizer.nextcloud_url).to eq "http://example.org"
  end
end
