# frozen_string_literal: true

require "action_dispatch/routing/polymorphic_routes"

module ActionView
  module RoutingUrlFor
    # Returns the URL for the set of +options+ provided. This takes the
    # same options as +url_for+ in Action Controller (see the
    # documentation for ActionDispatch::Routing::UrlFor#url_for). Note that by default
    # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative <tt>"/controller/action"</tt>
    # instead of the fully qualified URL like <tt>"http://example.com/controller/action"</tt>.
    #
    # ==== Options
    # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
    # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
    # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in <tt>"/archive/2005/"</tt>. Note that this
    #   is currently not recommended since it breaks caching.
    # * <tt>:host</tt> - Overrides the default (current) host if provided.
    # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
    # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
    # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
    #
    # ==== Relying on named routes
    #
    # Passing a record (like an Active Record) instead of a hash as the options parameter will
    # trigger the named route for that record. The lookup will happen on the name of the class. So passing a
    # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
    # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
    #
    # ==== Implicit Controller Namespacing
    #
    # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
    #
    # ==== Examples
    #   <%= url_for(action: 'index') %>
    #   # => /blogs/
    #
    #   <%= url_for(action: 'find', controller: 'books') %>
    #   # => /books/find
    #
    #   <%= url_for(action: 'login', controller: 'members', only_path: false, protocol: 'https') %>
    #   # => https://www.example.com/members/login/
    #
    #   <%= url_for(action: 'play', anchor: 'player') %>
    #   # => /messages/play/#player
    #
    #   <%= url_for(action: 'jump', anchor: 'tax&ship') %>
    #   # => /testing/jump/#tax&ship
    #
    #   <%= url_for(Workshop) %>
    #   # => /workshops
    #
    #   <%= url_for(Workshop.new) %>
    #   # relies on Workshop answering a persisted? call (and in this case returning false)
    #   # => /workshops
    #
    #   <%= url_for(@workshop) %>
    #   # calls @workshop.to_param which by default returns the id
    #   # => /workshops/5
    #
    #   # to_param can be re-defined in a model to provide different URL names:
    #   # => /workshops/1-workshop-name
    #
    #   <%= url_for("http://www.example.com") %>
    #   # => http://www.example.com
    #
    #   <%= url_for(:back) %>
    #   # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
    #   # => http://www.example.com
    #
    #   <%= url_for(:back) %>
    #   # if request.env["HTTP_REFERER"] is not set or is blank
    #   # => javascript:history.back()
    #
    #   <%= url_for(action: 'index', controller: 'users') %>
    #   # Assuming an "admin" namespace
    #   # => /admin/users
    #
    #   <%= url_for(action: 'index', controller: '/users') %>
    #   # Specify absolute path with beginning slash
    #   # => /users
    def url_for(options = nil)
      case options
      when String
        options
      when nil
        super(only_path: _generate_paths_by_default)
      when Hash
        options = options.symbolize_keys
        ensure_only_path_option(options)

        super(options)
      when ActionController::Parameters
        ensure_only_path_option(options)

        super(options)
      when :back
        _back_url
      when Array
        components = options.dup
        options = components.extract_options!
        ensure_only_path_option(options)

        if options[:only_path]
          polymorphic_path(components, options)
        else
          polymorphic_url(components, options)
        end
      else
        method = _generate_paths_by_default ? :path : :url
        builder = ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.public_send(method)

        case options
        when Symbol
          builder.handle_string_call(self, options)
        when Class
          builder.handle_class_call(self, options)
        else
          builder.handle_model_call(self, options)
        end
      end
    end

    def url_options # :nodoc:
      return super unless controller.respond_to?(:url_options)
      controller.url_options
    end

    private
      def _routes_context
        controller
      end

      def optimize_routes_generation?
        controller.respond_to?(:optimize_routes_generation?, true) ?
          controller.optimize_routes_generation? : super
      end

      def _generate_paths_by_default
        true
      end

      def ensure_only_path_option(options)
        unless options.key?(:only_path)
          options[:only_path] = _generate_paths_by_default unless options[:host]
        end
      end
  end
end
