require 'solargraph'
require 'logger'
require 'active_support'
require 'active_support/core_ext/string/inflections'

require_relative 'solargraph/rails/util'
require_relative 'solargraph/rails/schema'
require_relative 'solargraph/rails/annotate'
require_relative 'solargraph/rails/autoload'
require_relative 'solargraph/rails/model'
require_relative 'solargraph/rails/devise'
require_relative 'solargraph/rails/walker'
require_relative 'solargraph/rails/rails_api'
require_relative 'solargraph/rails/delegate'
require_relative 'solargraph/rails/storage'
require_relative 'solargraph/rails/puma'
require_relative 'solargraph/rails/importmap'
require_relative 'solargraph/rails/debug'
require_relative 'solargraph/rails/version'

module Solargraph
  module Rails
    class NodeParser
      extend Solargraph::Parser::Legacy::ClassMethods
    end

    class Convention < Solargraph::Convention::Base
      # @param yard_map [Solargraph::DocMap]
      #
      # @return [Solargraph::Environ]
      def global(yard_map)
        Solargraph::Environ.new(
          pins: Solargraph::Rails::RailsApi.instance.global(yard_map)
        )
      rescue => error
        Solargraph.logger.warn(
          error.message + "\n" + error.backtrace.join("\n")
        )
        EMPTY_ENVIRON
      end

      # @param source_map [Solargraph::SourceMap]
      #
      # @return [Solargraph::Environ]
      def local(source_map)
        pins = []
        ds =
          source_map.document_symbols.select do |n|
            n.is_a?(Solargraph::Pin::Namespace)
          end
        ns = ds.first

        basename = File.basename(source_map.filename)

        environ = Solargraph::Environ.new
        Puma.instance.add_dsl(environ, basename)
        Importmap.instance.add_dsl(environ, basename)

        return environ unless ns

        pins += run_feature { Schema.instance.process(source_map, ns) }
        pins += run_feature { Annotate.instance.process(source_map, ns) }
        pins += run_feature { Model.instance.process(source_map, ns) }
        pins += run_feature { Storage.instance.process(source_map, ns) }
        pins += run_feature { Autoload.instance.process(source_map, ns, ds) }
        pins += run_feature { Devise.instance.process(source_map, ns) }
        pins += run_feature { Delegate.instance.process(source_map, ns) } if Delegate.supported?
        pins += run_feature { RailsApi.instance.local(source_map, ns) }

        environ = Solargraph::Environ.new(pins: pins)
        Puma.instance.add_dsl(environ, basename)
        environ
      end

      private

      # @yieldreturn [Array<Solargraph::Pin::Base>]
      #
      # @return [Array<Solargraph::Pin::Base>]
      def run_feature(&block)
        yield
      rescue => error
        Solargraph.logger.warn(
          error.message + "\n" + error.backtrace.join("\n")
        )
        []
      end
    end
  end
end

Solargraph::Convention.register(Solargraph::Rails::Convention)
