# frozen_string_literal: true

module Decidim
  # This class can be used to resolve the mounted route based on the current
  # routes in any provided context.
  class EngineResolver
    # Initializes the engine resolver instance.
    #
    # @param current_routes [ActionDispatch::Routing::RouteSet] the route set
    #  for the context
    def initialize(current_routes)
      @current_routes = current_routes
    end

    # Resolves the mounted route name for the provided context.
    #
    # @return [String] The resolved route name or "decidim" if it could not be
    #   resolved
    def mounted_name
      return "main_app" if base_engine.routes == current_routes

      route = find_mounted_route(base_engine)
      route&.name || "decidim"
    end

    private

    attr_reader :current_routes

    # Provides the base engine from which the search is started from.
    #
    # @return [Rails::Engine] The engine to start the search from
    def base_engine
      Rails.application
    end

    # Finds the mounted route for current context based on the current routes.
    #
    # @param target_engine [Rails::Engine] The engine from which the search is
    #   performed.
    # @return [ActionDispatch::Journey::Route] The defined route that matches
    #   the current context based on the current routes provided for the
    #   initializer.
    def find_mounted_route(target_engine)
      target_engine.routes.set.each do |route|
        # If the route is a dispatcher
        # (ActionDispatch::Routing::RouteSet::Dispatcher), it will not have an
        # engine app mapped to it in which case the app will not respond to the
        # `routes` method.
        next if route.dispatcher?
        next unless route.app.app.is_a?(Class)
        next unless route.app.app < Rails::Engine

        # route.app -> ActionDispatch::Routing::Mapper::Constraints
        # route.app.app -> the target engine
        return route if route.app.app.routes == current_routes

        # Test the sub-engines if any engines are mounted to this engine.
        sub = find_mounted_route(route.app.app)
        return sub if sub
      end

      nil
    end
  end
end
