module Lutaml::Model::Serialize::ClassMethods

def add_custom_handling_methods_to_model(klass)

def add_custom_handling_methods_to_model(klass)
  Utils.add_boolean_accessor_if_not_defined(klass, :ordered)
  Utils.add_boolean_accessor_if_not_defined(klass, :mixed)
  Utils.add_accessor_if_not_defined(klass, :element_order)
  Utils.add_accessor_if_not_defined(klass, :encoding)
  Utils.add_method_if_not_defined(klass,
                                  :using_default_for) do |attribute_name|
    @using_default ||= {}
    @using_default[attribute_name] = true
  end
  Utils.add_method_if_not_defined(klass,
                                  :value_set_for) do |attribute_name|
    @using_default ||= {}
    @using_default[attribute_name] = false
  end
  Utils.add_method_if_not_defined(klass,
                                  :using_default?) do |attribute_name|
    @using_default ||= {}
    !!@using_default[attribute_name]
  end
end

def add_enum_getter_if_not_defined(klass, enum_name, collection)

def add_enum_getter_if_not_defined(klass, enum_name, collection)
  Utils.add_method_if_not_defined(klass, enum_name) do
    i = instance_variable_get(:"@#{enum_name}") || []
    if !collection && i.is_a?(Array)
      i.first
    else
      i.uniq
    end
  end
end

def add_enum_methods_to_model(klass, enum_name, values, collection: false)

def add_enum_methods_to_model(klass, enum_name, values, collection: false)
  add_enum_getter_if_not_defined(klass, enum_name, collection)
  add_enum_setter_if_not_defined(klass, enum_name, values, collection)
  return unless values.all?(::String)
  values.each do |value|
    Utils.add_method_if_not_defined(klass, "#{value}?") do
      curr_value = public_send(:"#{enum_name}")
      if collection
        curr_value.include?(value)
      else
        curr_value == value
      end
    end
    Utils.add_method_if_not_defined(klass, value.to_s) do
      public_send(:"#{value}?")
    end
    Utils.add_method_if_not_defined(klass, "#{value}=") do |val|
      value_set_for(enum_name)
      enum_vals = public_send(:"#{enum_name}")
      enum_vals = if !!val
                    if collection
                      enum_vals << value
                    else
                      [value]
                    end
                  elsif collection
                    enum_vals.delete(value)
                    enum_vals
                  else
                    instance_variable_get(:"@#{enum_name}") - [value]
                  end
      instance_variable_set(:"@#{enum_name}", enum_vals)
    end
    Utils.add_method_if_not_defined(klass, "#{value}!") do
      public_send(:"#{value}=", true)
    end
  end
end

def add_enum_setter_if_not_defined(klass, enum_name, _values, collection)

def add_enum_setter_if_not_defined(klass, enum_name, _values, collection)
  Utils.add_method_if_not_defined(klass, "#{enum_name}=") do |value|
    value = [] if value.nil?
    value = [value] if !value.is_a?(Array)
    value_set_for(enum_name)
    if collection
      curr_value = public_send(:"#{enum_name}")
      instance_variable_set(:"@#{enum_name}", curr_value + value)
    else
      instance_variable_set(:"@#{enum_name}", value)
    end
  end
end

def apply_mappings(doc, format, options = {})

def apply_mappings(doc, format, options = {})
  instance = options[:instance] || model.new
  return instance if Utils.blank?(doc)
  mappings = mappings_for(format)
  if mappings.polymorphic_mapping
    return resolve_polymorphic(doc, format, mappings, instance, options)
  end
  # options[:mappings] = mappings.mappings
  transformer = Lutaml::Model::Config.transformer_for(format)
  transformer.data_to_model(self, doc, format, options)
end

def apply_value_map(value, value_map, attr)

def apply_value_map(value, value_map, attr)
  if value.nil?
    value_for_option(value_map[:nil], attr)
  elsif Utils.empty?(value)
    value_for_option(value_map[:empty], attr, value)
  elsif Utils.uninitialized?(value)
    value_for_option(value_map[:omitted], attr)
  else
    value
  end
end

def as(format, instance, options = {})

def as(format, instance, options = {})
  if instance.is_a?(Array)
    return instance.map { |item| public_send(:"as_#{format}", item) }
  end
  unless instance.is_a?(model)
    msg = "argument is a '#{instance.class}' but should be a '#{model}'"
    raise Lutaml::Model::IncorrectModelError, msg
  end
  transformer = Lutaml::Model::Config.transformer_for(format)
  transformer.model_to_data(self, instance, format, options)
end

def attribute(name, type, options = {})

Define an attribute for the model
def attribute(name, type, options = {})
  if type.is_a?(Hash)
    options[:method_name] = type[:method]
    type = nil
  end
  attr = Attribute.new(name, type, options)
  attributes[name] = attr
  define_attribute_methods(attr)
  attr
end

def cast(value)

def cast(value)
  value
end

def choice(min: 1, max: 1, &block)

def choice(min: 1, max: 1, &block)
  @choice_attributes << Choice.new(self, min, max).tap do |c|
    c.instance_eval(&block)
  end
end

def default_mappings(format)

def default_mappings(format)
  klass = ::Lutaml::Model::Config.mappings_class_for(format)
  mappings = klass.new
  mappings.tap do |mapping|
    attributes&.each_key do |name|
      mapping.map_element(
        name.to_s,
        to: name,
      )
    end
    mapping.root(to_s.split("::").last) if format == :xml
  end
end

def define_attribute_methods(attr)

def define_attribute_methods(attr)
  name = attr.name
  if attr.enum?
    add_enum_methods_to_model(
      model,
      name,
      attr.options[:values],
      collection: attr.options[:collection],
    )
  elsif attr.derived? && name != attr.method_name
    define_method(name) do
      public_send(attr.method_name)
    end
  else
    define_method(name) do
      instance_variable_get(:"@#{name}")
    end
    define_method(:"#{name}=") do |value|
      value_set_for(name)
      instance_variable_set(:"@#{name}", attr.cast_value(value))
    end
  end
end

def empty_object(attr)

def empty_object(attr)
  return [] if attr.collection?
  ""
end

def ensure_utf8(value)

def ensure_utf8(value)
  case value
  when String
    value.encode("UTF-8", invalid: :replace, undef: :replace,
                          replace: "")
  when Array
    value.map { |v| ensure_utf8(v) }
  when Hash
    value.transform_keys do |k|
      ensure_utf8(k)
    end.transform_values do |v|
      ensure_utf8(v)
    end
  else
    value
  end
end

def enums

def enums
  attributes.select { |_, attr| attr.enum? }
end

def from(format, data, options = {})

def from(format, data, options = {})
  return data if Utils.uninitialized?(data)
  adapter = Lutaml::Model::Config.adapter_for(format)
  doc = adapter.parse(data, options)
  public_send(:"of_#{format}", doc, options)
end

def handle_key_value_mappings(mapping, format)

def handle_key_value_mappings(mapping, format)
  @mappings[format] ||= KeyValueMapping.new
  @mappings[format].mappings.concat(mapping.mappings)
end

def import_model(model)

def import_model(model)
  import_model_with_root_error(model)
  import_model_attributes(model)
  import_model_mappings(model)
end

def import_model_attributes(model)

def import_model_attributes(model)
  model.attributes.each_value do |attr|
    define_attribute_methods(attr)
  end
  @choice_attributes.concat(Utils.deep_dup(model.choice_attributes))
  @attributes.merge!(Utils.deep_dup(model.attributes))
end

def import_model_mappings(model)

def import_model_mappings(model)
  import_model_with_root_error(model)
  Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
    next unless model.mappings.key?(format)
    mapping = model.mappings_for(format)
    mapping = Utils.deep_dup(mapping)
    klass = ::Lutaml::Model::Config.mappings_class_for(format)
    @mappings[format] ||= klass.new
    if format == :xml
      @mappings[format].merge_mapping_attributes(mapping)
      @mappings[format].merge_mapping_elements(mapping)
      @mappings[format].merge_elements_sequence(mapping)
    else
      @mappings[format].mappings.concat(mapping.mappings)
    end
  end
end

def import_model_with_root_error(model)

def import_model_with_root_error(model)
  return unless model.mappings.key?(:xml) && model.root?
  raise Lutaml::Model::ImportModelWithRootError.new(model)
end

def included(base)

def included(base)
  base.extend(ClassMethods)
  base.initialize_attrs(self)
end

def inherited(subclass)

def inherited(subclass)
  super
  subclass.initialize_attrs(self)
end

def initialize_attrs(source_class)

def initialize_attrs(source_class)
  @mappings = Utils.deep_dup(source_class.instance_variable_get(:@mappings)) || {}
  @attributes = Utils.deep_dup(source_class.instance_variable_get(:@attributes)) || {}
  @choice_attributes = Utils.deep_dup(source_class.instance_variable_get(:@choice_attributes)) || []
  instance_variable_set(:@model, self)
end

def key_value(&block)

def key_value(&block)
  Lutaml::Model::Config::KEY_VALUE_FORMATS.each do |format|
    mappings[format] ||= KeyValueMapping.new(format)
    mappings[format].instance_eval(&block)
  end
end

def mappings_for(format)

def mappings_for(format)
  mappings[format] || default_mappings(format)
end

def model(klass = nil)

def model(klass = nil)
  if klass
    @model = klass
    add_custom_handling_methods_to_model(klass)
  else
    @model
  end
end

def of(format, doc, options = {})

def of(format, doc, options = {})
  if doc.is_a?(Array)
    return doc.map { |item| send(:"of_#{format}", item) }
  end
  if format == :xml
    raise Lutaml::Model::NoRootMappingError.new(self) unless root?
    options[:encoding] = doc.encoding
  end
  transformer = Lutaml::Model::Config.transformer_for(format)
  transformer.data_to_model(self, doc, format, options)
end

def process_mapping(format, &block)

def process_mapping(format, &block)
  # klass = Lutaml::Model.const_get("#{format.to_s.capitalize}Mapping")
  # mappings[format] ||= klass.new
  # mappings[format].instance_eval(&block)
  # handle_root_assignment(mappings, format)
  klass = ::Lutaml::Model::Config.mappings_class_for(format)
  mappings[format] ||= klass.new
  mappings[format].instance_eval(&block)
  if mappings[format].respond_to?(:finalize)
    mappings[format].finalize(self)
  end
end

def resolve_polymorphic(doc, format, mappings, instance, options = {})

def resolve_polymorphic(doc, format, mappings, instance, options = {})
  polymorphic_mapping = mappings.polymorphic_mapping
  return instance if polymorphic_mapping.polymorphic_map.empty?
  klass_key = doc[polymorphic_mapping.name]
  klass_name = polymorphic_mapping.polymorphic_map[klass_key]
  klass = Object.const_get(klass_name)
  klass.apply_mappings(doc, format, options)
end

def root?

def root?
  mappings_for(:xml)&.root?
end

def to(format, instance, options = {})

def to(format, instance, options = {})
  value = public_send(:"as_#{format}", instance, options)
  adapter = Lutaml::Model::Config.adapter_for(format)
  options[:mapper_class] = self if format == :xml
  adapter.new(value).public_send(:"to_#{format}", options)
end

def value_for_option(option, attr, empty_value = nil)

def value_for_option(option, attr, empty_value = nil)
  return nil if option == :nil
  return empty_value || empty_object(attr) if option == :empty
  Lutaml::Model::UninitializedClass.instance
end