module Lutaml::Model::Serialize::ClassMethods

def apply_child_mappings(hash, child_mappings)

def apply_child_mappings(hash, child_mappings)
  return hash unless child_mappings
  hash.map do |key, value|
    child_mappings.to_h do |attr_name, path|
      attr_value = if path == :key
                     key
                   elsif path == :value
                     value
                   else
                     path = [path] unless path.is_a?(Array)
                     value.dig(*path.map(&:to_s))
                   end
      [attr_name, attr_value]
    end
  end
end

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

def apply_mappings(doc, format, options = {})
  instance = options[:instance] || model.new
  return instance if !doc || doc.empty?
  return apply_xml_mapping(doc, instance, options) if format == :xml
  mappings = mappings_for(format).mappings
  mappings.each do |rule|
    attr = if rule.delegate
             attributes[rule.delegate].type.attributes[rule.to]
           else
             attributes[rule.to]
           end
    raise "Attribute '#{rule.to}' not found in #{self}" unless attr
    value = if doc.key?(rule.name) || doc.key?(rule.name.to_sym)
              doc[rule.name] || doc[rule.name.to_sym]
            else
              attr.default
            end
    if rule.custom_methods[:from]
      if value && !value.empty?
        value = new.send(rule.custom_methods[:from], instance,
                         value)
      end
      next
    end
    value = apply_child_mappings(value, rule.child_mappings)
    value = attr.cast(value, format)
    if rule.delegate
      if instance.public_send(rule.delegate).nil?
        instance.public_send(:"#{rule.delegate}=",
                             attributes[rule.delegate].type.new)
      end
      instance.public_send(rule.delegate).public_send(:"#{rule.to}=",
                                                      value)
    else
      instance.public_send(:"#{rule.to}=", value)
    end
  end
  instance
end

def apply_xml_mapping(doc, instance, options = {})

def apply_xml_mapping(doc, instance, options = {})
  return instance unless doc
  mappings = mappings_for(:xml).mappings
  if doc.is_a?(Array)
    raise "May be `collection: true` is" \
          "missing for #{self} in #{options[:caller_class]}"
  end
  if instance.respond_to?(:ordered=) && doc.is_a?(Lutaml::Model::MappingHash)
    instance.element_order = doc.item_order
    instance.ordered = mappings_for(:xml).mixed_content? || options[:mixed_content]
  end
  mappings.each do |rule|
    attr = attributes[rule.to]
    raise "Attribute '#{rule.to}' not found in #{self}" unless attr
    is_content_mapping = rule.name.nil?
    value = if is_content_mapping
              doc["text"]
            else
              doc[rule.name.to_s] || doc[rule.name.to_sym]
            end
    value = [value].compact if attr.collection? && !value.is_a?(Array)
    if value.is_a?(Array)
      value = value.map do |v|
        v.is_a?(Hash) && !(attr.type <= Serialize) ? v["text"] : v
      end
    elsif !(attr.type <= Serialize) && value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash
      value = value["text"]
    end
    unless is_content_mapping
      value = attr.cast(
        value,
        :xml,
        caller_class: self,
        mixed_content: rule.mixed_content,
      )
    end
    if rule.custom_methods[:from]
      new.send(rule.custom_methods[:from], instance, value)
    else
      instance.public_send(:"#{rule.to}=", value)
    end
  end
  instance
end

def attr_value(attrs, name, attr_rule)

def attr_value(attrs, name, attr_rule)
  value = if attrs.key?(name.to_sym)
            attrs[name.to_sym]
          elsif attrs.key?(name.to_s)
            attrs[name.to_s]
          else
            attr_rule.default
          end
  if attr_rule.collection? || value.is_a?(Array)
    (value || []).map do |v|
      if v.is_a?(Hash)
        attr_rule.type.new(v)
      else
        # TODO: This code is problematic because Type.cast does not know
        # about all the types.
        Lutaml::Model::Type.cast(v, attr_rule.type)
      end
    end
  else
    # TODO: This code is problematic because Type.cast does not know
    # about all the types.
    Lutaml::Model::Type.cast(value, attr_rule.type)
  end
end

def attr_value_valid?(name, value)

Check if the value to be assigned is valid for the attribute
def attr_value_valid?(name, value)
  attr = attributes[name]
  return true unless attr.options[:values]
  # Allow nil values if there's no default
  return true if value.nil? && !attr.default
  # Use the default value if the value is nil
  value = attr.default if value.nil?
  attr.options[:values].include?(value)
end

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

Define an attribute for the model
def attribute(name, type, options = {})
  attr = Attribute.new(name, type, options)
  attributes[name] = attr
  define_method(name) do
    instance_variable_get(:"@#{name}")
  end
  define_method(:"#{name}=") do |value|
    instance_variable_set(:"@#{name}", value)
    validate
  end
end

def default_mappings(format)

def default_mappings(format)
  klass = format == :xml ? XmlMapping : KeyValueMapping
  klass.new.tap do |mapping|
    attributes&.each do |name, attr|
      mapping.map_element(
        name.to_s,
        to: name,
        render_nil: attr.render_nil?,
      )
    end
  end
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 generate_hash_from_child_mappings(value, child_mappings)

def generate_hash_from_child_mappings(value, child_mappings)
  return value unless child_mappings
  hash = {}
  value.each do |child_obj|
    map_key = nil
    map_value = {}
    child_mappings.each do |attr_name, path|
      if path == :key
        map_key = child_obj.send(attr_name)
      elsif path == :value
        map_value = child_obj.send(attr_name)
      else
        path = [path] unless path.is_a?(Array)
        path[0...-1].inject(map_value) do |acc, k|
          acc[k.to_s] ||= {}
        end.public_send(:[]=, path.last.to_s, child_obj.send(attr_name))
      end
    end
    hash[map_key] = map_value
  end
  hash
end

def handle_delegate(instance, rule, hash, format)

def handle_delegate(instance, rule, hash, format)
  name = rule.to
  value = instance.send(rule.delegate).send(name)
  return if value.nil? && !rule.render_nil
  attribute = instance.send(rule.delegate).class.attributes[name]
  hash[rule.from] = attribute.serialize(value, format)
end

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

def hash_representation(instance, format, options = {})
  only = options[:only]
  except = options[:except]
  mappings = mappings_for(format).mappings
  mappings.each_with_object({}) do |rule, hash|
    name = rule.to
    next if except&.include?(name) || (only && !only.include?(name))
    next handle_delegate(instance, rule, hash, format) if rule.delegate
    if rule.custom_methods[:to]
      next instance.send(rule.custom_methods[:to], instance, hash)
    end
    value = instance.send(name)
    next if value.nil? && !rule.render_nil
    attribute = attributes[name]
    hash[rule.from] = if rule.child_mappings
                        generate_hash_from_child_mappings(value, rule.child_mappings)
                      else
                        attribute.serialize(value, format, options)
                      end
  end
end

def inherited(subclass)

def inherited(subclass)
  super
  @mappings ||= {}
  @attributes ||= {}
  subclass.instance_variable_set(:@attributes, @attributes.dup)
  subclass.instance_variable_set(:@mappings, @mappings.dup)
  subclass.instance_variable_set(:@model, subclass)
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
  else
    @model
  end
end