require_dependency 'token_action/application_controller'

module TokenAction
  class TokensController < ApplicationController
    def redeem
      @token = TokenAction::Token.to_adapter.find_first(token: params[:token])
      if @token
        begin
          # Do this in the controller's scope to allow access to request methods.
          ActiveSupport::Inflector.constantize(@token.kind).redeem_token(*@token.args)
          redirect_to success_url, notice: translate(:success)
        rescue => e
          key = ActiveSupport::Inflector.underscore(e.class.name).gsub('/', I18n.default_separator)
          logger.warn "TokenAction failed to perform the action for the token '#{params[:token]}': #{e.message}"
          redirect_to failure_url, alert: translate(key, :failure)
        end
      else
        logger.info "TokenAction failed to find the token '#{params[:token]}'"
        redirect_to failure_url, alert: translate(:not_found)
      end
    end

  private

    # Returns a translation.
    #
    # @param [Array] args translation keys
    # @return [String] a translation
    def translate(*args)
      path = params[:path].gsub('/', I18n.default_separator)
      keys = []

      # Respect the order of precedence described in the readme.
      args.each do |key|
        [path, :default].each do |scope|
          keys << [:token_action, :tokens, scope, key]
        end
      end
      keys.map! do |key|
        key.join(I18n.default_separator).to_sym
      end

      key, *default = *keys
      I18n.translate(key, default: default)
    end

    # Returns the success URL.
    #
    # @return [String] a URL
    def success_url
      redirect_url([@token && @token.success_url, TokenAction.success_url])
    end

    # Returns the failure URL.
    #
    # @return [String] a URL
    def failure_url
      redirect_url([@token && @token.failure_url, TokenAction.failure_url])
    end

    # Returns the routeable URL with the highest preference.
    #
    # @param [Array<String>] a list of URLs in order of preference
    # @return [String] the routable URL with the highest preference
    def redirect_url(urls)
      urls.find do |url|
        url.present? && routable?(url)
      end || main_app.root_path
    end

    # Returns whether a path is routable.
    #
    # @param [String] path a path
    # @return [Boolean] whether the path is routable
    def routable?(path)
      !!Rails.application.routes.recognize_path(path, method: :get)
    rescue ActionController::RoutingError
      false
    end
  end
end
