# frozen_string_literal: true

module ActiveFields
  module Casters
    class DateTimeCaster < BaseCaster
      def serialize(value)
        value = value.iso8601 if value.is_a?(Date)
        casted_value = caster.serialize(value)

        casted_value.iso8601(precision) if casted_value.acts_like?(:time)
      end

      def deserialize(value)
        value = value.iso8601 if value.is_a?(Date)
        casted_value = caster.deserialize(value)

        apply_precision(casted_value).in_time_zone if casted_value.acts_like?(:time)
      end

      private

      def caster
        ActiveRecord::Type::DateTime.new
      end

      # Use maximum precision by default to prevent the caster from truncating useful time information
      # before precision is applied later
      def precision
        [options[:precision], ActiveFields::MAX_DATETIME_PRECISION].compact.min
      end

      def apply_precision(value)
        number_of_insignificant_digits = 9 - precision
        round_power = 10**number_of_insignificant_digits
        rounded_off_nsec = value.nsec % round_power

        value.change(nsec: value.nsec - rounded_off_nsec)
      end
    end
  end
end
