# frozen_string_literal: true
# rubocop:todo all

module Mongoid
  module Extensions

    # Adds type-casting behavior to Time class.
    module Time

      # Mongoizes a Time into a time.
      #
      # Time always mongoize into Time instances
      # (which are themselves).
      #
      # @return [ Time ] self.
      def __mongoize_time__
        self
      end

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

      module ClassMethods

        # Convert the object from its mongo friendly ruby type to this type.
        #
        # @example Demongoize the object.
        #   Time.demongoize(object)
        #
        # @param [ Time ] object The time from Mongo.
        #
        # @return [ Time | nil ] The object as a time.
        def demongoize(object)
          return if object.blank?
          time = if object.acts_like?(:time)
            Mongoid::Config.use_utc? ? object : object.getlocal
          elsif object.acts_like?(:date)
            ::Date.demongoize(object).to_time
          elsif object.is_a?(String)
            begin
              object.__mongoize_time__
            rescue ArgumentError
              nil
            end
          elsif object.is_a?(BSON::Timestamp)
            ::Time.at(object.seconds)
          end

          return if time.nil?

          time.in_time_zone(Mongoid.time_zone)
        end

        # Turn the object from the ruby type we deal with to a Mongo friendly
        # type.
        #
        # @example Mongoize the object.
        #   Time.mongoize("2012-1-1")
        #
        # @param [ Object ] object The object to mongoize.
        #
        # @return [ Time | nil ] The object mongoized or nil.
        def mongoize(object)
          return if object.blank?
          begin
            time = object.respond_to?(:__mongoize_time__) ? object.__mongoize_time__ : nil
          rescue ArgumentError
            return
          end

          if time.acts_like?(:time)
            if object.respond_to?(:sec_fraction)
              ::Time.at(time.to_i, object.sec_fraction * 10**6).utc
            elsif time.respond_to?(:subsec)
              ::Time.at(time.to_i, time.subsec * 10**6).utc
            else
              ::Time.at(time.to_i, time.usec).utc
            end
          end
        end
      end
    end
  end
end

::Time.__send__(:include, Mongoid::Extensions::Time)
::Time.extend(Mongoid::Extensions::Time::ClassMethods)
