module ViewModel::Callbacks

def self.create(callbacks, view, context) # rubocop:disable Lint/NestedMethodDefinition

rubocop:disable Lint/NestedMethodDefinition
def self.create(callbacks, view, context) # rubocop:disable Lint/NestedMethodDefinition
  self.new(callbacks, view, context)
end

def self.wrap_deserialize(viewmodel, deserialize_context:)

def self.wrap_deserialize(viewmodel, deserialize_context:)
  hook_control = DeserializeHookControl.new
  wrap_serialize(viewmodel, context: deserialize_context) do
    deserialize_context.run_callback(ViewModel::Callbacks::Hook::BeforeDeserialize,
                                     viewmodel)
    val = yield(hook_control)
    if hook_control.changes.nil?
      raise ViewModel::DeserializationError::Internal.new(
              'Internal error: changes not recorded for deserialization of viewmodel',
              viewmodel.blame_reference)
    end
    deserialize_context.run_callback(ViewModel::Callbacks::Hook::AfterDeserialize,
                                     viewmodel,
                                     changes: hook_control.changes)
    val
  end
end

def self.wrap_serialize(viewmodel, context:)

def self.wrap_serialize(viewmodel, context:)
  context.run_callback(ViewModel::Callbacks::Hook::BeforeVisit, viewmodel)
  val = yield
  context.run_callback(ViewModel::Callbacks::Hook::AfterVisit, viewmodel)
  val
end

def add_callback(hook, view_name, &block)

def add_callback(hook, view_name, &block)
  valid_hook!(hook)
  hook_callbacks = (class_callbacks[hook] ||= {})
  view_callbacks = (hook_callbacks[view_name.to_s] ||= [])
  view_callbacks << block
end

def dsl_add_hook_name

def dsl_add_hook_name
  name.underscore
end

def dsl_viewmodel_callback_method

def dsl_viewmodel_callback_method
  name.underscore.to_sym
end

def each_callback(hook, view_name)

def each_callback(hook, view_name)
  valid_hook!(hook)
  return to_enum(__method__, hook, view_name) unless block_given?
  all_callbacks do |callbacks|
    if (hook_callbacks = callbacks[hook])
      hook_callbacks[view_name.to_s]&.each { |c| yield(c) }
      hook_callbacks[ALWAYS]&.each { |c| yield(c) }
    end
  end
end

def ineligible(view)

def ineligible(view)
  # ARVM synthetic views are considered part of their association and as such
  # are not visited by callbacks. Eligibility exclusion is intended to be
  # library-internal: subclasses should not attempt to extend this.
  view.is_a?(ViewModel) && view.class.synthetic
end

def inherited(subclass)

def inherited(subclass)
  subclass_callbacks = {}
  subclass.define_singleton_method(:class_callbacks) { subclass_callbacks }
  subclass.define_singleton_method(:all_callbacks) do |&block|
    return to_enum(__method__) unless block
    super(&block)
    block.call(subclass_callbacks)
  end
end

def init(context_name, *other_params)

def init(context_name, *other_params)
  @context_name    = context_name
  @required_params = other_params
  @env_class = Value.new(:_callbacks, :view, context_name, *other_params) do
    include CallbackEnvContext
    delegate :model, to: :view
    unless context_name == :context
      alias_method :context, context_name
    end
    # If we have any other params, generate a combined positional/keyword
    # constructor wrapper
    if other_params.present?
      params = other_params.map { |x| "#{x}:" }.join(', ')
      args   = other_params.join(', ')
      instance_eval(<<-SRC, __FILE__, __LINE__ + 1)
        def create(callbacks, view, context, #{params})
          self.new(callbacks, view, context, #{args})
        end
      SRC
    else
      def self.create(callbacks, view, context) # rubocop:disable Lint/NestedMethodDefinition
        self.new(callbacks, view, context)
      end
    end
  end
end

def run_callback(hook, view, context, **args)

def run_callback(hook, view, context, **args)
  return if ineligible(view)
  callback_env = hook.env_class.create(self, view, context, **args)
  view_name = view.class.view_name
  self.class.each_callback(hook, view_name) do |callback|
    callback_env.instance_exec(&callback)
  end
end

def updates_view!

def updates_view!
  define_singleton_method(:updates_view?) { true }
end

def updates_view?

def updates_view?
  false
end

def valid_hook!(hook)

def valid_hook!(hook)
  unless hook.is_a?(Hook)
    raise ArgumentError.new("Invalid hook: '#{hook}'")
  end
end