# frozen_string_literal: true

require "yaml"

module PaperTrail
  module Serializers
    # The default serializer for, e.g. `versions.object`.
    module YAML
      extend self # makes all instance methods become module methods as well

      def load(string)
        if use_safe_load?
          ::YAML.safe_load(
            string,
            permitted_classes: yaml_column_permitted_classes,
            aliases: true
          )
        elsif ::YAML.respond_to?(:unsafe_load)
          ::YAML.unsafe_load(string)
        else
          ::YAML.load(string)
        end
      end

      # @param object (Hash | HashWithIndifferentAccess) - Coming from
      # `recordable_object` `object` will be a plain `Hash`. However, due to
      # recent [memory optimizations](https://github.com/paper-trail-gem/paper_trail/pull/1189),
      # when coming from `recordable_object_changes`, it will be a `HashWithIndifferentAccess`.
      def dump(object)
        object = object.to_hash if object.is_a?(ActiveSupport::HashWithIndifferentAccess)
        ::YAML.dump object
      end

      # Returns a SQL LIKE condition to be used to match the given field and
      # value in the serialized object.
      def where_object_condition(arel_field, field, value)
        arel_field.matches("%\n#{field}: #{value}\n%")
      end

      private

      def use_safe_load?
        if ::ActiveRecord.gem_version >= Gem::Version.new("7.0.3.1")
          # `use_yaml_unsafe_load` may be removed in the future, at which point
          # safe loading will be the default.
          !defined?(ActiveRecord.use_yaml_unsafe_load) || !ActiveRecord.use_yaml_unsafe_load
        elsif defined?(ActiveRecord::Base.use_yaml_unsafe_load)
          # Rails 5.2.8.1, 6.0.5.1, 6.1.6.1
          !ActiveRecord::Base.use_yaml_unsafe_load
        else
          false
        end
      end

      def yaml_column_permitted_classes
        if defined?(ActiveRecord.yaml_column_permitted_classes)
          # Rails >= 7.0.3.1
          ActiveRecord.yaml_column_permitted_classes
        elsif defined?(ActiveRecord::Base.yaml_column_permitted_classes)
          # Rails 5.2.8.1, 6.0.5.1, 6.1.6.1
          ActiveRecord::Base.yaml_column_permitted_classes
        else
          []
        end
      end
    end
  end
end
