# frozen_string_literal: true

require "active_support/concern"

module Decidim
  # A factory to build forms. This is done to add a context when possible, so
  # that forms can act based on data that varies between requests. This is
  # mostly done so that i18n form fields can be properly validated with the
  # `current_organization` available locales, instead of doing so against all
  # the available locales in the platform. The `current_organization` varies
  # between requests, so the form need some way to access this data.
  #
  # Examples:
  #
  #   # in a controller, mostly in a `create` action:
  #   form(MyFormClass).form_params(params)
  #
  #   # in a controller, mostly in a `new` action:
  #   form(MyFormClass).instance
  #
  #   # in a controller, mostly in an `edit` action:
  #   form(MyFormClass).form_model(@my_resource)
  #
  module FormFactory
    extend ActiveSupport::Concern

    included do
      # Initializes a factory for the given Form Object class.
      #
      # klass - a Form object class name. Must be a constant, not a String.
      def form(klass)
        Class.new do
          # Initializes the form factory object.
          #
          # klass - the class name of the Form object that will be initialized
          # controller - the Controller where the form is built.
          def initialize(klass, controller)
            @klass = klass
            @controller = controller
          end

          # Returns a simple instance of the form klass.
          #
          # extra_context - A Hash with optional extra context data.
          def instance(extra_context = {})
            @klass.new.with_context(context.merge(extra_context))
          end

          # Initializes a form object from a model. Delegates the functionality
          # to the form object class method.
          #
          # model         - the model instance from which the form object will be
          #                 initialized.
          # extra_context - a Hash with optional context data.
          def from_model(model, extra_context = {})
            @klass.from_model(model).with_context(context.merge(extra_context))
          end

          # Initializes a form object instance from params, and it
          # automatically adds some context. Context can be extended.
          #
          # params        - a Hash with params. Mostly a set of params from a form.
          # extra_context - a Hash with optional context data.
          def from_params(params, extra_context = {})
            @klass.from_params(params).with_context(context.merge(extra_context))
          end

          # Sets a base context from the current controller. Since this can be
          # used from some controllers that do not respond to the helper
          # methods used here, this Hash can have different keys depending on
          # the controller that uses it.
          def context
            {
              current_organization: @controller.try(:current_organization),
              current_component: @controller.try(:current_component),
              current_user: @controller.try(:current_user),
              current_participatory_space: @controller.try(:current_participatory_space)
            }
          end
        end.new(klass, self)
      end
    end
  end
end
