module Kemal
  # `Kemal::Handler` is a subclass of `HTTP::Handler`.
  #
  # It adds `only`, `only_match?`, `exclude`, `exclude_match?`.
  # These methods are useful for the conditional execution of custom handlers .
  class Handler
    include HTTP::Handler

    @@only_routes_tree = Radix::Tree(String).new
    @@exclude_routes_tree = Radix::Tree(String).new

    macro only(paths, method = "GET")
      class_name = {{@type.name}}
      method_downcase = {{method.downcase}}
      class_name_method = "#{class_name}/#{method_downcase}"
      ({{paths}}).each do |path|
        @@only_routes_tree.add class_name_method + path, '/' + method_downcase + path
      end
    end

    macro exclude(paths, method = "GET")
      class_name = {{@type.name}}
      method_downcase = {{method.downcase}}
      class_name_method = "#{class_name}/#{method_downcase}"
      ({{paths}}).each do |path|
        @@exclude_routes_tree.add class_name_method + path, '/' + method_downcase + path
      end
    end

    def call(context : HTTP::Server::Context)
      call_next(context)
    end

    # Processes the path based on `only` paths which is a `Array(String)`.
    # If the path is not found on `only` conditions the handler will continue processing.
    # If the path is found in `only` conditions it'll stop processing and will pass the request
    # to next handler.
    #
    # However this is not done automatically. All handlers must inherit from `Kemal::Handler`.
    #
    # ```
    # class OnlyHandler < Kemal::Handler
    #   only ["/"]
    #
    #   def call(env)
    #     return call_next(env) unless only_match?(env)
    #     puts "If the path is / i will be doing some processing here."
    #   end
    # end
    # ```
    def only_match?(env : HTTP::Server::Context)
      @@only_routes_tree.find(radix_path(env.request.method, env.request.path)).found?
    end

    # Processes the path based on `exclude` paths which is a `Array(String)`.
    # If the path is not found on `exclude` conditions the handler will continue processing.
    # If the path is found in `exclude` conditions it'll stop processing and will pass the request
    # to next handler.
    #
    # However this is not done automatically. All handlers must inherit from `Kemal::Handler`.
    #
    # ```
    # class ExcludeHandler < Kemal::Handler
    #   exclude ["/"]
    #
    #   def call(env)
    #     return call_next(env) if exclude_match?(env)
    #     puts "If the path is not / i will be doing some processing here."
    #   end
    # end
    # ```
    def exclude_match?(env : HTTP::Server::Context)
      @@exclude_routes_tree.find(radix_path(env.request.method, env.request.path)).found?
    end

    private def radix_path(method : String, path : String)
      "#{self.class}/#{method.downcase}#{path}"
    end
  end
end
