# frozen_string_literal: true

require "hanami/helpers/form_helper/html_node"
require "hanami/helpers/form_helper/values"
require "hanami/helpers/html_helper/html_builder"
require "hanami/helpers/escape_helper"
require "hanami/utils/string"

module Hanami
  module Helpers
    module FormHelper
      # Form builder
      #
      # @since 0.2.0
      #
      # @see Hanami::Helpers::HtmlHelper::HtmlBuilder
      class FormBuilder < ::Hanami::Helpers::HtmlHelper::HtmlBuilder
        # Set of HTTP methods that are understood by web browsers
        #
        # @since 0.2.0
        # @api private
        BROWSER_METHODS = %w[GET POST].freeze

        # Set of HTTP methods that should NOT generate CSRF token
        #
        # @since 0.2.0
        # @api private
        EXCLUDED_CSRF_METHODS = %w[GET].freeze

        # Checked attribute value
        #
        # @since 0.2.0
        # @api private
        #
        # @see Hanami::Helpers::FormHelper::FormBuilder#radio_button
        CHECKED = "checked"

        # Selected attribute value for option
        #
        # @since 0.2.0
        # @api private
        #
        # @see Hanami::Helpers::FormHelper::FormBuilder#select
        SELECTED = "selected"

        # Separator for accept attribute of file input
        #
        # @since 0.2.0
        # @api private
        #
        # @see Hanami::Helpers::FormHelper::FormBuilder#file_input
        ACCEPT_SEPARATOR = ","

        # Replacement for input id interpolation
        #
        # @since 0.2.0
        # @api private
        #
        # @see Hanami::Helpers::FormHelper::FormBuilder#_input_id
        INPUT_ID_REPLACEMENT = '-\k<token>'

        # Default value for unchecked check box
        #
        # @since 0.2.0
        # @api private
        #
        # @see Hanami::Helpers::FormHelper::FormBuilder#check_box
        DEFAULT_UNCHECKED_VALUE = "0"

        # Default value for checked check box
        #
        # @since 0.2.0
        # @api private
        #
        # @see Hanami::Helpers::FormHelper::FormBuilder#check_box
        DEFAULT_CHECKED_VALUE = "1"

        # ENCTYPE_MULTIPART = 'multipart/form-data'.freeze

        include Helpers::EscapeHelper

        self.html_node = ::Hanami::Helpers::FormHelper::HtmlNode

        # Instantiate a form builder
        #
        # @overload initialize(form, attributes, context, params, &blk)
        #   Top level form
        #   @param form [Hanami::Helpers:FormHelper::Form] the form
        #   @param attributes [::Hash] a set of HTML attributes
        #   @param context [Hanami::Helpers::FormHelper]
        #   @param params [Hash] optional set of params to override the ones that are coming from the view context
        #   @param blk [Proc] a block that describes the contents of the form
        #
        # @overload initialize(form, attributes, params, &blk)
        #   Nested form
        #   @param form [Hanami::Helpers:FormHelper::Form] the form
        #   @param attributes [Hanami::Helpers::FormHelper::Values] user defined
        #     values
        #   @param blk [Proc] a block that describes the contents of the form
        #
        # @return [Hanami::Helpers::FormHelper::FormBuilder] the form builder
        #
        # @since 0.2.0
        # @api private
        def initialize(form, attributes, context = nil, params = nil, &blk)
          super()

          @context    = context
          @blk        = blk
          @verb       = nil
          @csrf_token = nil

          # Nested form
          if @context.nil? && attributes.is_a?(Values)
            @values      = attributes
            @attributes  = {}
            @name        = form
          else
            @form        = form
            @name        = form.name
            @values      = Values.new(form.values, params || @context.params)
            @attributes  = attributes
            @verb_method = verb_method
            @csrf_token  = csrf_token
          end
        end

        # Resolves all the nodes and generates the markup
        #
        # @return [Hanami::Utils::Escape::SafeString] the output
        #
        # @since 0.2.0
        # @api private
        #
        # @see Hanami::Helpers::HtmlHelper::HtmlBuilder#to_s
        # @see http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Escape/SafeString
        def to_s
          if toplevel?
            _method_override!
            form(@blk, @attributes)
          end

          super
        end

        # Nested fields
        #
        # The inputs generated by the wrapped block will be prefixed with the given name
        # It supports infinite levels of nesting.
        #
        # @param name [Symbol] the nested name, it's used to generate input
        #   names, ids, and to lookup params to fill values.
        #
        # @yield [index]
        # @yieldparam [Integer] index iterative index (it starts from zero)
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     form_for :delivery, routes.deliveries_path do
        #       text_field :customer_name
        #
        #       fields_for :address do
        #         text_field :street
        #       end
        #
        #       submit 'Create'
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <form action="/deliveries" method="POST" accept-charset="utf-8" id="delivery-form">
        #     <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d">
        #     <input type="text" name="delivery[customer_name]" id="delivery-customer-name" value="">
        #     <input type="text" name="delivery[address][street]" id="delivery-address-street" value="">
        #
        #     <button type="submit">Create</button>
        #   </form>
        #
        # @example Multiple levels of nesting
        #   <%=
        #     form_for :delivery, routes.deliveries_path do
        #       text_field :customer_name
        #
        #       fields_for :address do
        #         text_field :street
        #
        #         fields_for :location do
        #           text_field :city
        #           text_field :country
        #         end
        #       end
        #
        #       submit 'Create'
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <form action="/deliveries" method="POST" accept-charset="utf-8" id="delivery-form">
        #     <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d">
        #     <input type="text" name="delivery[customer_name]" id="delivery-customer-name" value="">
        #     <input type="text" name="delivery[address][street]" id="delivery-address-street" value="">
        #     <input type="text" name="delivery[address][location][city]" id="delivery-address-location-city" value="">
        #     <input type="text" name="delivery[address][location][country]" id="delivery-address-location-country" value="">
        #
        #     <button type="submit">Create</button>
        #   </form>
        def fields_for(name, value = nil)
          current_name = @name
          @name        = _input_name(name)
          yield(name, value)
        ensure
          @name = current_name
        end

        # Nested collections
        #
        # Supports nesting for collections, with infinite
        # levels of nesting.
        #
        # @param name [Symbol] the nested name, it's used to generate input
        #   names, ids, and to lookup params to fill values.
        #
        # @yield [index, value]
        # @yieldparam [Integer] index iterative index (it starts from zero)
        # @yieldparam [Object] value an item of the collection
        #
        # @example Basic usage
        #   <%=
        #     form_for :delivery, routes.deliveries_path do
        #       text_field :customer_name
        #
        #       fields_for_collection :addresses do
        #         text_field :street
        #       end
        #
        #       submit 'Create'
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <form action="/deliveries" method="POST" accept-charset="utf-8" id="delivery-form">
        #     <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d">
        #     <input type="text" name="delivery[customer_name]" id="delivery-customer-name" value="">
        #     <input type="text" name="delivery[addresses][][street]" id="delivery-address-0-street" value="">
        #     <input type="text" name="delivery[addresses][][street]" id="delivery-address-1-street" value="">
        #
        #     <button type="submit">Create</button>
        #   </form>
        #
        # @example Yield index and value
        #   <%=
        #     form_for(:bill, routes.bill_path(id: bill.id), { bill: bill }, method: :patch, class: 'form-horizontal') do
        #       fieldset do
        #         legend "Addresses"
        #
        #         fields_for_collection :addresses do |i, address|
        #           div class: "form-group" do
        #             text "Address id: #{address.id}"
        #
        #             label :street
        #             input_text :street, class: "form-control", placeholder: "Street", "data-funky": "index-#{i}"
        #           end
        #         end
        #
        #         label :ensure_names
        #       end
        #
        #       submit submit_label, class: "btn btn-default"
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <form action="/bills/1" method="POST" accept-charset="utf-8" id="bill-form" class="form-horizontal">
        #     <input type="hidden" name="_method" value="PATCH">
        #     <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d">
        #     <fieldset>
        #       <legend>Addresses</legend>
        #
        #       <div class="form-group">
        #         Address id: 23
        #         <label for="bill-addresses-0-street">Street</label>
        #         <input type="text" name="bill[addresses][][street]" id="bill-addresses-0-street" value="5th Ave" class="form-control" placeholder="Street" data-funky="index-0">
        #       </div>
        #
        #       <div class="form-group">
        #         Address id: 42
        #         <label for="bill-addresses-1-street">Street</label>
        #         <input type="text" name="bill[addresses][][street]" id="bill-addresses-1-street" value="4th Ave" class="form-control" placeholder="Street" data-funky="index-1">
        #       </div>
        #
        #       <label for="bill-ensure-names">Ensure names</label>
        #     </fieldset>
        #
        #     <button type="submit" class="btn btn-default">Update</button>
        #   </form>
        def fields_for_collection(name, &block)
          current_name = @name
          base_value = _value(name)
          @name = _input_name(name)

          base_value.each_with_index do |value, index|
            fields_for(index, value, &block)
          end
        ensure
          @name = current_name
        end

        # Label tag
        #
        # The first param <tt>content</tt> can be a <tt>Symbol</tt> that represents
        # the target field (Eg. <tt>:extended_title</tt>), or a <tt>String</tt>
        # which is used as it is.
        #
        # @param content [Symbol,String] the field name or a content string
        # @param attributes [Hash] HTML attributes to pass to the label tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     label :extended_title
        #   %>
        #
        #   <!-- output -->
        #   <label for="book-extended-title">Extended title</label>
        #
        # @example HTML attributes
        #   <%=
        #     # ...
        #     label :title, class: "form-label"
        #   %>
        #
        #   <!-- output -->
        #   <label for="book-title" class="form-label">Title</label>
        #
        # @example Custom content
        #   <%=
        #     # ...
        #     label 'Title', for: :extended_title
        #   %>
        #
        #   <!-- output -->
        #   <label for="book-extended-title">Title</label>
        #
        # @example Custom "for" attribute
        #   <%=
        #     # ...
        #     label :extended_title, for: 'ext-title'
        #   %>
        #
        #   <!-- output -->
        #   <label for="ext-title">Extended title</label>
        #
        # @example Nested fields usage
        #   <%=
        #     # ...
        #     fields_for :address do
        #       label :city
        #       text_field :city
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <label for="delivery-address-city">City</label>
        #   <input type="text" name="delivery[address][city] id="delivery-address-city" value="">
        #
        # @example Block syntax
        #   <%=
        #     # ...
        #     label for: :free_shipping do
        #       text "Free shipping"
        #       abbr "*", title: "optional", "aria-label": "optional"
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <label for="book-free-shipping">
        #     Free Shipping
        #     <abbr title="optional" aria-label="optional">*</abbr>
        #   </label>
        def label(content = nil, **attributes, &blk)
          attributes = {for: _for(content, attributes.delete(:for))}.merge(attributes)
          content    = case content
                       when String, Hanami::Utils::String, NilClass
                         content
                       else
                         Utils::String.capitalize(content)
                       end

          super(content, attributes, &blk)
        end

        # Fieldset
        #
        # @param content [Symbol,String,NilClass] the content
        # @param attributes [Hash] HTML attributes to pass to the label tag
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     fieldset do
        #       legend "Author"
        #
        #       fields_for :author do
        #         label :name
        #         text_field :name
        #       end
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <fieldset>
        #     <legend>Author</legend>
        #     <label for="book-author-name">Name</label>
        #     <input type="text" name="book[author][name]" id="book-author-name" value="">
        #   </fieldset>
        def fieldset(content = nil, attributes = {})
          # This is here only for documentation purposes
          super
        end

        # Check box
        #
        # It renders a check box input.
        #
        # When a form is submitted, browsers don't send the value of unchecked
        # check boxes. If an user unchecks a check box, their browser won't send
        # the unchecked value. On the server side the corresponding value is
        # missing, so the application will assume that the user action never
        # happened.
        #
        # To solve this problem the form renders a hidden field with the
        # "unchecked value". When the user unchecks the input, the browser will
        # ignore it, but it will still send the value of the hidden input. See
        # the examples below.
        #
        # When editing a resource, the form automatically assigns the
        # <tt>checked="checked"</tt> attribute.
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        # @option attributes [String] :checked_value (defaults to "1")
        # @option attributes [String] :unchecked_value (defaults to "0")
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     check_box :free_shipping
        #   %>
        #
        #   <!-- output -->
        #   <input type="hidden" name="delivery[free_shipping]" value="0">
        #   <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="1">
        #
        # @example HTML Attributes
        #   <%=
        #     check_box :free_shipping, class: "form-check-input"
        #   %>
        #
        #   <!-- output -->
        #   <input type="hidden" name="delivery[free_shipping]" value="0">
        #   <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="1" class="form-check-input">
        #
        # @example Specify (un)checked values
        #   <%=
        #     check_box :free_shipping, checked_value: 'true', unchecked_value: 'false'
        #   %>
        #
        #   <!-- output -->
        #   <input type="hidden" name="delivery[free_shipping]" value="false">
        #   <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="true">
        #
        # @example Automatic "checked" attribute
        #   # For this example the params are:
        #   #
        #   #  { delivery: { free_shipping: '1' } }
        #   <%=
        #     check_box :free_shipping
        #   %>
        #
        #   <!-- output -->
        #   <input type="hidden" name="delivery[free_shipping]" value="0">
        #   <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="1" checked="checked">
        #
        # @example Force "checked" attribute
        #   # For this example the params are:
        #   #
        #   #  { delivery: { free_shipping: '0' } }
        #   <%=
        #     check_box :free_shipping, checked: 'checked'
        #   %>
        #
        #   <!-- output -->
        #   <input type="hidden" name="delivery[free_shipping]" value="0">
        #   <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="1" checked="checked">
        #
        # @example Multiple check boxes
        #   <%=
        #     check_box :languages, name: 'book[languages][]', value: 'italian', id: nil
        #     check_box :languages, name: 'book[languages][]', value: 'english', id: nil
        #   %>
        #
        #   <!-- output -->
        #   <input type="checkbox" name="book[languages][]" value="italian">
        #   <input type="checkbox" name="book[languages][]" value="english">
        #
        # @example Automatic "checked" attribute for multiple check boxes
        #   # For this example the params are:
        #   #
        #   #  { book: { languages: ['italian'] } }
        #   <%=
        #     check_box :languages, name: 'book[languages][]', value: 'italian', id: nil
        #     check_box :languages, name: 'book[languages][]', value: 'english', id: nil
        #   %>
        #
        #   <!-- output -->
        #   <input type="checkbox" name="book[languages][]" value="italian" checked="checked">
        #   <input type="checkbox" name="book[languages][]" value="english">
        def check_box(name, attributes = {})
          _hidden_field_for_check_box(name, attributes)
          input _attributes_for_check_box(name, attributes)
        end

        # Color input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     color_field :background
        #   %>
        #
        #   <!-- output -->
        #   <input type="color" name="user[background]" id="user-background" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     color_field :background, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="color" name="user[background]" id="user-background" value="" class="form-control">
        def color_field(name, attributes = {})
          input _attributes(:color, name, attributes)
        end

        # Date input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     date_field :birth_date
        #   %>
        #
        #   <!-- output -->
        #   <input type="date" name="user[birth_date]" id="user-birth-date" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     date_field :birth_date, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="date" name="user[birth_date]" id="user-birth-date" value="" class="form-control">
        def date_field(name, attributes = {})
          input _attributes(:date, name, attributes)
        end

        # Datetime input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     datetime_field :delivered_at
        #   %>
        #
        #   <!-- output -->
        #   <input type="datetime" name="delivery[delivered_at]" id="delivery-delivered-at" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     datetime_field :delivered_at, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="datetime" name="delivery[delivered_at]" id="delivery-delivered-at" value="" class="form-control">
        def datetime_field(name, attributes = {})
          input _attributes(:datetime, name, attributes)
        end

        # Datetime Local input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     datetime_local_field :delivered_at
        #   %>
        #
        #   <!-- output -->
        #   <input type="datetime-local" name="delivery[delivered_at]" id="delivery-delivered-at" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     datetime_local_field :delivered_at, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="datetime-local" name="delivery[delivered_at]" id="delivery-delivered-at" value="" class="form-control">
        def datetime_local_field(name, attributes = {})
          input _attributes(:"datetime-local", name, attributes)
        end

        # Time field
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     time_field :release_hour
        #   %>
        #
        #   <!-- output -->
        #   <input type="time" name="book[release_hour]" id="book-release-hour" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     time_field :release_hour, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="time" name="book[release_hour]" id="book-release-hour" value="" class="form-control">
        def time_field(name, attributes = {})
          input _attributes(:time, name, attributes)
        end

        # Month field
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     month_field :release_month
        #   %>
        #
        #   <!-- output -->
        #   <input type="month" name="book[release_month]" id="book-release-month" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     month_field :release_month, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="month" name="book[release_month]" id="book-release-month" value="" class="form-control">
        def month_field(name, attributes = {})
          input _attributes(:month, name, attributes)
        end

        # Week field
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     week_field :release_week
        #   %>
        #
        #   <!-- output -->
        #   <input type="week" name="book[release_week]" id="book-release-week" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     week_field :release_week, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="week" name="book[release_week]" id="book-release-week" value="" class="form-control">
        def week_field(name, attributes = {})
          input _attributes(:week, name, attributes)
        end

        # Email input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     email_field :email
        #   %>
        #
        #   <!-- output -->
        #   <input type="email" name="user[email]" id="user-email" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     email_field :email, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="email" name="user[email]" id="user-email" value="" class="form-control">
        def email_field(name, attributes = {})
          input _attributes(:email, name, attributes)
        end

        # URL input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     url_field :website
        #   %>
        #
        #   <!-- output -->
        #   <input type="url" name="user[website]" id="user-website" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     url_field :website, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="url" name="user[website]" id="user-website" value="" class="form-control">
        def url_field(name, attributes = {})
          attrs         = attributes.dup
          attrs[:value] = escape_url(attrs.fetch(:value) { _value(name) })

          input _attributes(:url, name, attrs)
        end

        # Telephone input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     tel_field :telephone
        #   %>
        #
        #   <!-- output -->
        #   <input type="tel" name="user[telephone]" id="user-telephone" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     telurl_field :telephone, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="tel" name="user[telephone]" id="user-telephone" value="" class="form-control">
        def tel_field(name, attributes = {})
          input _attributes(:tel, name, attributes)
        end

        # Hidden input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     hidden_field :customer_id
        #   %>
        #
        #   <!-- output -->
        #   <input type="hidden" name="delivery[customer_id]" id="delivery-customer-id" value="">
        def hidden_field(name, attributes = {})
          input _attributes(:hidden, name, attributes)
        end

        # File input
        #
        # **PLEASE REMEMBER TO ADD <tt>enctype: 'multipart/form-data'</tt> ATTRIBUTE TO THE FORM**
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        # @option attributes [String,Array] :accept Optional set of accepted MIME Types
        # @option attributes [TrueClass,FalseClass] :multiple Optional, allow multiple file upload
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     file_field :avatar
        #   %>
        #
        #   <!-- output -->
        #   <input type="file" name="user[avatar]" id="user-avatar">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     file_field :avatar, class: "avatar-upload"
        #   %>
        #
        #   <!-- output -->
        #   <input type="file" name="user[avatar]" id="user-avatar" class="avatar-upload">
        #
        # @example Accepted MIME Types
        #   <%=
        #     # ...
        #     file_field :resume, accept: 'application/pdf,application/ms-word'
        #   %>
        #
        #   <!-- output -->
        #   <input type="file" name="user[resume]" id="user-resume" accept="application/pdf,application/ms-word">
        #
        # @example Accepted MIME Types (as array)
        #   <%=
        #     # ...
        #     file_field :resume, accept: ['application/pdf', 'application/ms-word']
        #   %>
        #
        #   <!-- output -->
        #   <input type="file" name="user[resume]" id="user-resume" accept="application/pdf,application/ms-word">
        #
        # @example Accepted multiple file upload (as array)
        #   <%=
        #     # ...
        #     file_field :resume, multiple: true
        #   %>
        #
        #   <!-- output -->
        #   <input type="file" name="user[resume]" id="user-resume" multiple="multiple">
        def file_field(name, attributes = {})
          attributes[:accept] = Array(attributes[:accept]).join(ACCEPT_SEPARATOR) if attributes.key?(:accept)
          attributes = {type: :file, name: _displayed_input_name(name), id: _input_id(name)}.merge(attributes)

          input(attributes)
        end

        # Number input
        #
        # You can also make use of the `max`, `min`, and `step` attributes for
        # the HTML5 number field.
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the number input
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     number_field :percent_read
        #   %>
        #
        #   <!-- output -->
        #   <input type="number" name="book[percent_read]" id="book-percent-read" value="">
        #
        # @example Advanced attributes
        #   <%=
        #     # ...
        #     number_field :priority, min: 1, max: 10, step: 1
        #   %>
        #
        #   <!-- output -->
        #   <input type="number" name="book[percent_read]" id="book-precent-read" value="" min="1" max="10" step="1">
        def number_field(name, attributes = {})
          input _attributes(:number, name, attributes)
        end

        # Range input
        #
        # You can also make use of the `max`, `min`, and `step` attributes for
        # the HTML5 number field.
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the number input
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     range_field :discount_percentage
        #   %>
        #
        #   <!-- output -->
        #   <input type="range" name="book[discount_percentage]" id="book-discount-percentage" value="">
        #
        # @example Advanced attributes
        #   <%=
        #     # ...
        #     range_field :discount_percentage, min: 1, max: 10, step: 1
        #   %>
        #
        #   <!-- output -->
        #   <input type="number" name="book[discount_percentage]" id="book-discount-percentage" value="" min="1" max="10" step="1">
        def range_field(name, attributes = {})
          input _attributes(:range, name, attributes)
        end

        # Text-area input
        #
        # @param name [Symbol] the input name
        # @param content [String] the content of the textarea
        # @param attributes [Hash] HTML attributes to pass to the textarea tag
        #
        # @since 0.2.5
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     text_area :hobby
        #   %>
        #
        #   <!-- output -->
        #   <textarea name="user[hobby]" id="user-hobby"></textarea>
        #
        # @example Set content
        #   <%=
        #     # ...
        #     text_area :hobby, 'Football'
        #   %>
        #
        #   <!-- output -->
        #   <textarea name="user[hobby]" id="user-hobby">Football</textarea>
        #
        # @example Set content and HTML attributes
        #   <%=
        #     # ...
        #     text_area :hobby, 'Football', class: 'form-control'
        #   %>
        #
        #   <!-- output -->
        #   <textarea name="user[hobby]" id="user-hobby" class="form-control">Football</textarea>
        #
        # @example Omit content and specify HTML attributes
        #   <%=
        #     # ...
        #     text_area :hobby, class: 'form-control'
        #   %>
        #
        #   <!-- output -->
        #   <textarea name="user[hobby]" id="user-hobby" class="form-control"></textarea>
        #
        # @example Force blank value
        #   <%=
        #     # ...
        #     text_area :hobby, '', class: 'form-control'
        #   %>
        #
        #   <!-- output -->
        #   <textarea name="user[hobby]" id="user-hobby" class="form-control"></textarea>
        def text_area(name, content = nil, attributes = {})
          if content.respond_to?(:to_hash)
            attributes = content
            content    = nil
          end

          attributes = {name: _displayed_input_name(name), id: _input_id(name)}.merge(attributes)
          textarea(content || _value(name), attributes)
        end

        # Text input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     text_field :first_name
        #   %>
        #
        #   <!-- output -->
        #   <input type="text" name="user[first_name]" id="user-first-name" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     text_field :first_name, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="text" name="user[first_name]" id="user-first-name" value="" class="form-control">
        def text_field(name, attributes = {})
          input _attributes(:text, name, attributes)
        end
        alias_method :input_text, :text_field

        # Search input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     search_field :q
        #   %>
        #
        #   <!-- output -->
        #   <input type="search" name="search[q]" id="search-q" value="">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     search_field :q, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <input type="search" name="search[q]" id="search-q" value="" class="form-control">
        def search_field(name, attributes = {})
          input _attributes(:search, name, attributes)
        end

        # Radio input
        #
        # If request params have a value that corresponds to the given value,
        # it automatically sets the <tt>checked</tt> attribute.
        # This Hanami::Controller integration happens without any developer intervention.
        #
        # @param name [Symbol] the input name
        # @param value [String] the input value
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     radio_button :category, 'Fiction'
        #     radio_button :category, 'Non-Fiction'
        #   %>
        #
        #   <!-- output -->
        #   <input type="radio" name="book[category]" value="Fiction">
        #   <input type="radio" name="book[category]" value="Non-Fiction">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     radio_button :category, 'Fiction', class: "form-check"
        #     radio_button :category, 'Non-Fiction', class: "form-check"
        #   %>
        #
        #   <!-- output -->
        #   <input type="radio" name="book[category]" value="Fiction" class="form-check">
        #   <input type="radio" name="book[category]" value="Non-Fiction" class="form-check">
        #
        # @example Automatic checked value
        #   # Given the following params:
        #   #
        #   # book: {
        #   #   category: 'Non-Fiction'
        #   # }
        #
        #   <%=
        #     # ...
        #     radio_button :category, 'Fiction'
        #     radio_button :category, 'Non-Fiction'
        #   %>
        #
        #   <!-- output -->
        #   <input type="radio" name="book[category]" value="Fiction">
        #   <input type="radio" name="book[category]" value="Non-Fiction" checked="checked">
        def radio_button(name, value, attributes = {})
          attributes = {type: :radio, name: _displayed_input_name(name), value: value}.merge(attributes)
          attributes[:checked] = CHECKED if _value(name).to_s == value.to_s
          input(attributes)
        end

        # Password input
        #
        # @param name [Symbol] the input name
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     password_field :password
        #   %>
        #
        #   <!-- output -->
        #   <input type="password" name="signup[password]" id="signup-password" value="">
        def password_field(name, attributes = {})
          input({type: :password, name: _displayed_input_name(name), id: _input_id(name), value: nil}.merge(attributes))
        end

        # Select input
        #
        # @param name [Symbol] the input name
        # @param values [Hash] a Hash to generate <tt><option></tt> tags.
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # Values is used to generate the list of <tt>&lt;option&gt;</tt> tags, it is an
        # <tt>Enumerable</tt> of pairs of content (the displayed text) and value (the tag's
        # attribute), in that respective order (please refer to the examples for more clarity).
        #
        # If request params have a value that corresponds to one of the given values,
        # it automatically sets the <tt>selected</tt> attribute on the <tt><option></tt> tag.
        # This Hanami::Controller integration happens without any developer intervention.
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     select :store, values
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store]" id="book-store">
        #     <option value="it">Italy</option>
        #     <option value="us">United States</option>
        #   </select>
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     select :store, values, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store]" id="book-store" class="form-control">
        #     <option value="it">Italy</option>
        #     <option value="us">United States</option>
        #   </select>
        #
        # @example Automatic selected option
        #   # Given the following params:
        #   #
        #   # book: {
        #   #   store: 'it'
        #   # }
        #
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     select :store, values
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store]" id="book-store">
        #     <option value="it" selected="selected">Italy</option>
        #     <option value="us">United States</option>
        #   </select>
        #
        # @example Prompt option
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     select :store, values, options: { prompt: 'Select a store' }
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store]" id="book-store">
        #     <option>Select a store</option>
        #     <option value="it">Italy</option>
        #     <option value="us">United States</option>
        #   </select>
        #
        # @example Selected option
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     select :store, values, options: { selected: book.store }
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store]" id="book-store">
        #     <option value="it" selected="selected">Italy</option>
        #     <option value="us">United States</option>
        #   </select>
        #
        # @example Prompt option and HTML attributes
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     select :store, values, options: { prompt: 'Select a store' }, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store]" id="book-store" class="form-control">
        #     <option disabled="disabled">Select a store</option>
        #     <option value="it">Italy</option>
        #     <option value="us">United States</option>
        #   </select>
        #
        # @example Multiple select
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     select :stores, values, multiple: true
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store][]" id="book-store" multiple="multiple">
        #    <option value="it">Italy</option>
        #     <option value="us">United States</option>
        #   </select>
        #
        # @example Multiple select and HTML attributes
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     select :stores, values, multiple: true, class: "form-control"
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store][]" id="book-store" multiple="multiple" class="form-control">
        #     <option value="it">Italy</option>
        #     <option value="us">United States</option>
        #   </select>
        #
        # @example Array with repeated entries
        #   <%=
        #     # ...
        #     values = [['Italy', 'it'],
        #               ['---', ''],
        #               ['Afghanistan', 'af'],
        #               ...
        #               ['Italy', 'it'],
        #               ...
        #               ['Zimbabwe', 'zw']]
        #     select :stores, values
        #   %>
        #
        #   <!-- output -->
        #   <select name="book[store]" id="book-store">
        #     <option value="it">Italy</option>
        #     <option value="">---</option>
        #     <option value="af">Afghanistan</option>
        #     ...
        #     <option value="it">Italy</option>
        #     ...
        #     <option value="zw">Zimbabwe</option>
        #   </select>
        def select(name, values, attributes = {})
          options     = attributes.delete(:options) { {} }
          multiple    = attributes[:multiple]
          attributes  = {name: _select_input_name(name, multiple), id: _input_id(name)}.merge(attributes)
          prompt      = options.delete(:prompt)
          selected    = options.delete(:selected)
          input_value = _value(name)

          super(attributes) do
            option(prompt, disabled: true) if prompt

            already_selected = nil
            values.each do |content, value|
              if (multiple || !already_selected) && (already_selected = _select_option_selected?(value, selected, input_value, multiple))
                option(content, {value: value, selected: SELECTED}.merge(options))
              else
                option(content, {value: value}.merge(options))
              end
            end
          end
        end

        # Datalist input
        #
        # @param name [Symbol] the input name
        # @param values [Array,Hash] a collection that is transformed into <tt><option></tt> tags.
        # @param list [String] the name of list for the text input, it's also the id of datalist
        # @param attributes [Hash] HTML attributes to pass to the input tag
        #
        # @since 0.4.0
        #
        # @example Basic Usage
        #   <%=
        #     # ...
        #     values = ['Italy', 'United States']
        #     datalist :stores, values, 'books'
        #   %>
        #
        #   <!-- output -->
        #   <input type="text" name="book[store]" id="book-store" value="" list="books">
        #   <datalist id="books">
        #     <option value="Italy"></option>
        #     <option value="United States"></option>
        #   </datalist>
        #
        # @example Options As Hash
        #   <%=
        #     # ...
        #     values = Hash['Italy' => 'it', 'United States' => 'us']
        #     datalist :stores, values, 'books'
        #   %>
        #
        #   <!-- output -->
        #   <input type="text" name="book[store]" id="book-store" value="" list="books">
        #   <datalist id="books">
        #     <option value="Italy">it</option>
        #     <option value="United States">us</option>
        #   </datalist>
        #
        # @example Specify Custom Attributes For Datalist Input
        #   <%=
        #     # ...
        #     values = ['Italy', 'United States']
        #     datalist :stores, values, 'books', datalist: { class: 'form-control' }
        #   %>
        #
        #   <!-- output -->
        #   <input type="text" name="book[store]" id="book-store" value="" list="books">
        #   <datalist id="books" class="form-control">
        #     <option value="Italy"></option>
        #     <option value="United States"></option>
        #   </datalist>
        #
        # @example Specify Custom Attributes For Options List
        #   <%=
        #     # ...
        #     values = ['Italy', 'United States']
        #     datalist :stores, values, 'books', options: { class: 'form-control' }
        #   %>
        #
        #   <!-- output -->
        #   <input type="text" name="book[store]" id="book-store" value="" list="books">
        #   <datalist id="books">
        #     <option value="Italy" class="form-control"></option>
        #     <option value="United States" class="form-control"></option>
        #   </datalist>
        def datalist(name, values, list, attributes = {})
          attrs    = attributes.dup
          options  = attrs.delete(:options)  || {}
          datalist = attrs.delete(:datalist) || {}

          attrs[:list]  = list
          datalist[:id] = list

          text_field(name, attrs)
          super(datalist) do
            values.each do |value, content|
              option(content, {value: value}.merge(options))
            end
          end
        end

        # Button
        #
        # @overload button(content, attributes = {})
        #   Use string content
        #   @param content [String] The content
        #   @param attributes [Hash] HTML attributes to pass to the button tag
        #
        # @overload button(attributes = {}, &blk)
        #   Use block content
        #   @param attributes [Hash] HTML attributes to pass to the button tag
        #   @param blk [Proc] the block content
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     button 'Click me'
        #   %>
        #
        #   <!-- output -->
        #   <button>Click me</button>
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     button 'Click me', class: "btn btn-secondary"
        #   %>
        #
        #   <!-- output -->
        #   <button class="btn btn-secondary">Click me</button>
        #
        # @example Block
        #   <%=
        #     # ...
        #     button class: "btn btn-secondary" do
        #       span class: 'oi oi-check'
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <button class="btn btn-secondary">
        #     <span class="oi oi-check"></span>
        #   </button>
        def button(content, attributes = {}, &blk)
          if content.is_a?(::Hash)
            attributes = content
            content = nil
          end

          super
        end

        # Image button
        #
        # Visual submit button
        #
        # **Please note:** for security reasons, please use the absolute URL of the image
        #
        # @param source [String] The **absolute URL** of the image
        # @param attributes [Hash] HTML attributes to pass to the button tag
        #
        # @since 1.0.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     image_button "https://hanamirb.org/assets/button.png"
        #   %>
        #
        #   <!-- output -->
        #   <input type="image" src="https://hanamirb.org/assets/button.png">
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     image_button "https://hanamirb.org/assets/button.png", name: "image", width: "50"
        #   %>
        #
        #   <!-- output -->
        #   <input name="image" width="50" type="image" src="https://hanamirb.org/assets/button.png">
        def image_button(source, attributes = {})
          attrs = attributes.dup
          attrs[:type] = :image
          attrs[:src]  = escape_url(source)

          input attrs
        end

        # Submit button
        #
        # @overload submit(content, attributes = {})
        #   Use string content
        #   @param content [String] The content
        #   @param attributes [Hash] HTML attributes to pass to the button tag
        #
        # @overload submit(attributes = {}, &blk)
        #   Use block content
        #   @param attributes [Hash] HTML attributes to pass to the button tag
        #   @param blk [Proc] the block content
        #
        # @since 0.2.0
        #
        # @example Basic usage
        #   <%=
        #     # ...
        #     submit 'Create'
        #   %>
        #
        #   <!-- output -->
        #   <button type="submit">Create</button>
        #
        # @example HTML Attributes
        #   <%=
        #     # ...
        #     submit 'Create', class: "btn btn-primary"
        #   %>
        #
        #   <!-- output -->
        #   <button type="submit" class="btn btn-primary">Create</button>
        #
        # @example Block
        #   <%=
        #     # ...
        #     button class: "btn btn-primary" do
        #       span class: 'oi oi-check'
        #     end
        #   %>
        #
        #   <!-- output -->
        #   <button type="submit" class="btn btn-primary">
        #     <span class="oi oi-check"></span>
        #   </button>
        def submit(content, attributes = {}, &blk)
          if content.is_a?(::Hash)
            attributes = content
            content = nil
          end

          attributes = {type: :submit}.merge(attributes)
          button(content, attributes, &blk)
        end

        protected

        # A set of options to pass to the sub form helpers.
        #
        # @api private
        # @since 0.2.0
        def options
          ::Hash[name: @name, values: @values, verb: @verb, csrf_token: @csrf_token]
        end

        private

        # Check the current builder is top-level
        #
        # @api private
        # @since 0.2.0
        def toplevel?
          @attributes.any?
        end

        # Prepare for method override
        #
        # @api private
        # @since 0.2.0
        def _method_override!
          if BROWSER_METHODS.include?(@verb_method)
            @attributes[:method] = @verb_method
          else
            @attributes[:method] = DEFAULT_METHOD
            @verb                = @verb_method
          end
        end

        # Return the method from attributes
        #
        # @api private
        def verb_method
          (@attributes.fetch(:method) { DEFAULT_METHOD }).to_s.upcase
        end

        # Return CSRF Protection token from view context
        #
        # @api private
        # @since 0.2.0
        def csrf_token
          @context.csrf_token if @context.respond_to?(:csrf_token) && !EXCLUDED_CSRF_METHODS.include?(@verb_method)
        end

        # Return a set of default HTML attributes
        #
        # @api private
        # @since 0.2.0
        def _attributes(type, name, attributes)
          attrs = {type: type, name: _displayed_input_name(name), id: _input_id(name), value: _value(name)}
          attrs.merge!(attributes)
          attrs[:value] = escape_html(attrs[:value])
          attrs
        end

        # Full input name, used to construct the input
        # attributes.
        #
        # @api private
        # @since 0.2.0
        def _input_name(name)
          "#{@name}[#{name}]"
        end

        # Input <tt>name</tt> HTML attribute
        #
        # @api private
        # @since 1.0.0
        def _displayed_input_name(name)
          _input_name(name).gsub(/\[\d+\]/, "[]")
        end

        # Input <tt>id</tt> HTML attribute
        #
        # @api private
        # @since 0.2.0
        def _input_id(name)
          name = _input_name(name).gsub(/\[(?<token>[[[:word:]]\-]*)\]/, INPUT_ID_REPLACEMENT)
          Utils::String.dasherize(name)
        end

        # Input <tt>value</tt> HTML attribute
        #
        # @api private
        # @since 0.2.0
        def _value(name)
          @values.get(
            *_input_name(name).split(/[\[\]]+/).map(&:to_sym)
          )
        end

        # Input <tt>for</tt> HTML attribute
        #
        # @api private
        # @since 0.2.0
        def _for(content, name)
          case name
          when String, Hanami::Utils::String
            name
          else
            _input_id(name || content)
          end
        end

        # Hidden field for check box
        #
        # @api private
        # @since 0.2.0
        #
        # @see Hanami::Helpers::FormHelper::FormBuilder#check_box
        def _hidden_field_for_check_box(name, attributes)
          return unless attributes[:value].nil? || !attributes[:unchecked_value].nil?

          input(
            type: :hidden,
            name: attributes[:name] || _displayed_input_name(name),
            value: attributes.delete(:unchecked_value) || DEFAULT_UNCHECKED_VALUE
          )
        end

        # HTML attributes for check box
        #
        # @api private
        # @since 0.2.0
        #
        # @see Hanami::Helpers::FormHelper::FormBuilder#check_box
        def _attributes_for_check_box(name, attributes)
          attributes = {
            type: :checkbox,
            name: _displayed_input_name(name),
            id: _input_id(name),
            value: attributes.delete(:checked_value) || DEFAULT_CHECKED_VALUE
          }.merge(attributes)

          attributes[:checked] = CHECKED if _check_box_checked?(attributes[:value], _value(name))

          attributes
        end

        # @api private
        def _select_input_name(name, multiple)
          select_name = _displayed_input_name(name)
          select_name = "#{select_name}[]" if multiple
          select_name
        end

        # @api private
        def _select_option_selected?(value, selected, input_value, multiple)
          if input_value && selected.nil?
            value.to_s == input_value.to_s
          else
            (value == selected) ||
              _is_in_selected_values?(multiple, selected, value) ||
              _is_current_value?(input_value, value) ||
              _is_in_input_values?(multiple, input_value, value)
          end
        end

        # @api private
        # @since 1.2.0
        def _is_current_value?(input_value, value)
          return unless input_value

          value.to_s == input_value.to_s
        end

        # @api private
        # @since 1.2.0
        def _is_in_selected_values?(multiple, selected, value)
          return unless multiple && selected.is_a?(Array)

          selected.include?(value)
        end

        # @api private
        # @since 1.2.0
        def _is_in_input_values?(multiple, input_value, value)
          return unless multiple && input_value.is_a?(Array)

          input_value.include?(value)
        end

        # @api private
        def _check_box_checked?(value, input_value)
          !input_value.nil? &&
            (input_value.to_s == value.to_s || input_value.is_a?(TrueClass) ||
            (input_value.is_a?(Array) && input_value.include?(value)))
        end
      end
    end
  end
end
