# frozen_string_literal: true
# Is someone in an LDAP group?

module Entitlements
  module Extras
    class LDAPGroup
      class Rules
        class LDAPGroup < Entitlements::Data::Groups::Calculated::Rules::Base
          include ::Contracts::Core
          C = ::Contracts

          # Interface method: Get a Set[Entitlements::Models::Person] matching this condition.
          #
          # value    - The value to match.
          # filename - Name of the file resulting in this rule being called
          # options  - Optional hash of additional method-specific options
          #
          # Returns a Set[Entitlements::Models::Person].
          Contract C::KeywordArgs[
            value: String,
            filename: C::Maybe[String],
            options: C::Optional[C::HashOf[Symbol => C::Any]]
          ] => C::SetOf[Entitlements::Models::Person]
          def self.matches(value:, filename: nil, options: {})
            Entitlements.cache[:ldap_cache] ||= {}
            Entitlements.cache[:ldap_cache][value] ||= begin
              entry = ldap.read(value)
              unless entry
                message = if filename
                            "Failed to read ldap_group = #{value} (referenced in #{filename})"
                else
                  # :nocov:
                  "Failed to read ldap_group = #{value}"
                  # :nocov:
                end
                raise Entitlements::Data::Groups::GroupNotFoundError, message
              end
              Entitlements::Service::LDAP.entry_to_group(entry)
            end
            Entitlements.cache[:ldap_cache][value].members(people_obj: Entitlements.cache[:people_obj])
          end

          # Object to communicate with an LDAP backend.
          #
          # Takes no arguments.
          #
          # Returns a Entitlements::Service::LDAP object.
          # :nocov:
          Contract C::None => Entitlements::Service::LDAP
          def self.ldap
            @ldap ||= begin
              config = Entitlements::Extras::LDAPGroup::Base.config
              opts = {
                addr: config.fetch("ldap_uri"),
                binddn: config.fetch("ldap_binddn"),
                bindpw: config.fetch("ldap_bindpw"),
                ca_file: config.fetch("ldap_ca_file", ENV["LDAP_CACERT"]),
                person_dn_format: config.fetch("person_dn_format")
              }
              opts[:disable_ssl_verification] = true if config.fetch("disable_ssl_verification", false)
              if RubyVersionCheck.ruby_version2?
                Entitlements::Service::LDAP.new_with_cache(opts)
              else
                Entitlements::Service::LDAP.new_with_cache(**opts)
              end
            end
          end
          # :nocov:
        end
      end
    end
  end
end
