# frozen_string_literal: true

module Entitlements
  class Util
    class Override
      include ::Contracts::Core
      C = ::Contracts

      # Handle override from plugin. Return hash, compatible with `upsert` method, that
      # defines the necessary differences.
      #
      # plugin - A Hash with the plugin configuration, both filename and target class (can be nil)
      # group  - The Entitlements::Models::Group
      # ldap   - Reference to the underlying Entitlements::Service::LDAP object
      #
      # Returns a Hash or nil.
      Contract C::Or[C::HashOf[String => String], nil], Entitlements::Models::Group, Entitlements::Service::LDAP => C::Or[nil, C::HashOf[String => C::Any]]
      def self.override_hash_from_plugin(plugin, group, ldap)
        return unless plugin

        # Plugin hash should consist of a Hash with 2 keys: "file" the absolute path to the file or relative path compared to
        # the entitlements configuration file, and "class" the class name that's contained within the file. If the filename
        # has no "/" then it is treated as a built-in plugin, under the "plugins" directory within this gem.
        unless plugin.key?("file")
          raise ArgumentError, "plugin configuration hash must contain 'file' key"
        end

        file = if plugin["file"] !~ %r{/}
                 File.expand_path(File.join("../plugins", plugin["file"]), File.dirname(__FILE__))
        elsif plugin["file"].start_with?("/")
          plugin["file"]
        else
          File.expand_path(plugin["file"], File.dirname(Entitlements.config_file))
        end

        unless File.file?(file)
          raise ArgumentError, "Could not locate plugin for #{plugin['file'].inspect} at #{file.inspect}"
        end

        unless plugin.key?("class")
          raise ArgumentError, "plugin configuration hash must contain 'class' key"
        end

        require file

        clazz = Kernel.const_get("Entitlements::Plugins::#{plugin['class']}")

        unless clazz.respond_to?(:loaded?) && clazz.loaded?
          raise ArgumentError, "Plugin Entitlements::Plugins::#{plugin['class']} should inherit Entitlements::Plugins"
        end

        unless clazz.respond_to?(:override_hash)
          raise ArgumentError, "Plugin Entitlements::Plugins::#{plugin['class']} must implement override_hash method"
        end

        override_hash = clazz.override_hash(group, plugin, ldap)
        return override_hash if override_hash.is_a?(Hash)

        type = override_hash.class
        raise ArgumentError, "Plugin Entitlements::Plugins::#{plugin['class']}.override_hash must return hash, not #{type}"
      end
    end
  end
end
