# frozen_string_literal: true

module WPScan
  module DB
    module DynamicFinders
      class Plugin < Base
        # @return [ Hash ]
        def self.df_data
          @df_data ||= all_df_data['plugins'] || {}
        end

        def self.version_finder_module
          Finders::PluginVersion
        end

        # @param [ Symbol ] finder_class
        # @param [ Boolean ] aggressive
        # @return [ Hash ]
        def self.finder_configs(finder_class, aggressive: false)
          configs = {}

          return configs unless allowed_classes.include?(finder_class)

          df_data.each do |slug, finders|
            # Quite sure better can be done with some kind of logic statement in the select
            fs = if aggressive
                   finders.reject { |_f, c| c['path'].nil? }
                 else
                   finders.select { |_f, c| c['path'].nil? }
                 end

            fs.each do |finder_name, config|
              klass = config['class'] || finder_name

              next unless klass.to_sym == finder_class

              configs[slug] ||= {}
              configs[slug][finder_name] = config
            end
          end

          configs
        end

        # @return [ Hash ]
        def self.versions_finders_configs
          return @versions_finders_configs if @versions_finders_configs

          @versions_finders_configs = {}

          df_data.each do |slug, finders|
            finders.each do |finder_name, config|
              next unless config.key?('version')

              @versions_finders_configs[slug] ||= {}
              @versions_finders_configs[slug][finder_name] = config
            end
          end

          @versions_finders_configs
        end

        # @param [ String ] slug
        # @return [ Constant ]
        def self.maybe_create_module(slug)
          # What about slugs such as js_composer which will be done as JsComposer, just like js-composer
          constant_name = classify_slug(slug)

          unless version_finder_module.const_defined?(constant_name, false)
            version_finder_module.const_set(constant_name, Module.new)
          end

          version_finder_module.const_get(constant_name)
        end

        # Create the dynamic finders related to the given slug, and return the created classes
        #
        # @param [ String ] slug
        #
        # @return [ Array<Class> ] The created classes
        def self.create_versions_finders(slug)
          created = []
          mod     = maybe_create_module(slug)

          versions_finders_configs[slug]&.each do |finder_class, config|
            klass = config['class'] || finder_class

            # Instead of raising exceptions, skip unallowed/already defined finders
            # So that, when new DF configs are put in the .yml
            # users with old version of WPScan will still be able to scan blogs
            # when updating the DB but not the tool

            next unless allowed_classes.include?(klass.to_sym)

            created << if mod.const_defined?(finder_class.to_sym, false)
                         mod.const_get(finder_class.to_sym)
                       else
                         version_finder_super_class(klass).create_child_class(mod, finder_class.to_sym, config)
                       end
          end

          created
        end

        # The idea here would be to check if the class exist in
        # the Finders::DynamicFinders::Plugins/Themes::klass or WpItemVersion::klass
        # and return the related constant when one has been found.
        #
        # So far, the Finders::DynamicFinders::WPItemVersion is enought
        # as nothing else is used
        #
        # @param [ String, Symbol ] klass
        # @return [ Constant ]
        def self.version_finder_super_class(klass)
          "WPScan::Finders::DynamicFinder::WpItemVersion::#{klass}".constantize
        end
      end
    end
  end
end
