# frozen_string_literal: true

require_relative 'abstract_handler'
require_relative '../namespace_cache/namespace_cache'
require_relative '../type_cache/type_cache'

module Hypertube
  module Core
    module Handler
      class GetTypeHandler < Hypertube::Core::Handler::AbstractHandler
        def initialize
          @required_parameters_count = 1
          @namespace_cache = Hypertube::Core::NamespaceCache::NamespaceCache.instance
          @type_cache = Hypertube::Core::TypeCache::TypeCache.instance
        end

        def process(command)
          validate(command, @required_parameters_count, self.class.name)

          type_to_return = _get_type_from_payload(command)

          if type_to_return.nil?
            message = "Hypertube::Utils::Type #{command.payload[0]} not found.\n"
            message += "\nLoaded libraries:\n"
            loaded_libraries = Hypertube::Core::Handler::LoadLibraryHandler.get_loaded_libraries
            # find types in loaded libraries
            loaded_libraries.each do |library|
              message += "#{library}\n"
            end
            raise ArgumentError, message
          end

          if (@namespace_cache.is_namespace_cache_empty? && @type_cache.is_type_cache_empty?) || # both caches are empty
             @namespace_cache.is_type_allowed(type_to_return) || # namespace is allowed
             @type_cache.is_type_allowed(type_to_return)
            # continue - type is allowed
          else
            allowed_namespaces = @namespace_cache.get_cached_namespaces.join(', ')
            allowed_types = @type_cache.get_cached_types.join(', ')
            raise "Hypertube::Utils::Type #{type_to_return.name} not allowed. \nAllowed namespaces: #{allowed_namespaces}\nAllowed types: #{allowed_types}"
          end

          type_to_return
        rescue NameError => e
          message = e.message
          message += "\nLoaded libraries:\n"
          loaded_libraries = Hypertube::Core::Handler::LoadLibraryHandler.get_loaded_libraries
          # find types in loaded libraries
          loaded_libraries.each do |library|
            message += "#{library}\n"
          end
          raise ArgumentError, message
        end

        private

        def _get_type_from_payload(command)
          if command.payload.length == 1
            type_name = command.payload[0].split('::')
            if type_name.length == 1
              Object.const_get(type_name[0])
            else
              _get_type_from_nested_payload(type_name)
            end
          else
            _get_type_from_nested_payload(command.payload)
          end
        end

        def _get_type_from_nested_payload(payload)
          loaded_module = Object.const_get(payload[0..-2].join('::'))
          loaded_module.const_get(payload[-1])
        end
      end
    end
  end
end
