# frozen_string_literal: true
# rubocop:todo all

module Mongoid
  module Extensions
    # Adds type-casting behavior to Range class.
    module Range
      def self.included(base)
        base.extend(ClassMethods)
      end

      # Get the range as arguments for a find.
      #
      # @example Get the range as find args.
      #   range.__find_args__
      #
      # @return [ Array ] The range as an array.
      # @deprecated
      def __find_args__
        to_a
      end
      Mongoid.deprecate(self, :__find_args__)

      # Turn the object from the ruby type we deal with to a Mongo friendly
      # type.
      #
      # @example Mongoize the object.
      #   range.mongoize
      #
      # @return [ Hash | nil ] The object mongoized or nil.
      def mongoize
        ::Range.mongoize(self)
      end

      # Is this a resizable object.
      #
      # @example Is this resizable?
      #   range.resizable?
      #
      # @return [ true ] True.
      def resizable?
        true
      end

      module ClassMethods
        # Convert the object from its mongo friendly ruby type to this type.
        #
        # @example Demongoize the object.
        #   Range.demongoize({ "min" => 1, "max" => 5 })
        #
        # @param [ Hash ] object The object to demongoize.
        #
        # @return [ Range | nil ] The range, or nil if object cannot be represented as range.
        def demongoize(object)
          return if object.nil?
          if object.is_a?(Hash)
            hash = object.slice('min', 'max', 'exclude_end', :min, :max, :exclude_end)
            unless hash.blank?
              begin
                ::Range.new(hash["min"] || hash[:min],
                            hash["max"] || hash[:max],
                            hash["exclude_end"] || hash[:exclude_end])
              rescue ArgumentError
                nil
              end
            end
          end
        end

        # Turn the object from the ruby type we deal with to a Mongo friendly
        # type.
        #
        # @example Mongoize the object.
        #   Range.mongoize(1..3)
        #
        # @param [ Object ] object The object to mongoize.
        #
        # @return [ Hash | nil ] The object mongoized or nil.
        def mongoize(object)
          return if object.nil?
          case object
          when Hash then __mongoize_hash__(object)
          when Range then __mongoize_range__(object)
          end
        end

        private

        def __mongoize_hash__(object)
          hash = object.stringify_keys
          hash.slice!('min', 'max', 'exclude_end')
          hash.compact!
          hash.transform_values!(&:mongoize)
          hash.blank? ? nil : hash
        end

        def __mongoize_range__(object)
          hash = {}
          hash['min'] = object.begin.mongoize if object.begin
          hash['max'] = object.end.mongoize if object.end
          if object.respond_to?(:exclude_end?) && object.exclude_end?
            hash['exclude_end'] = true
          end
          hash
        end
      end
    end
  end
end

Range.include(Mongoid::Extensions::Range)
