# typed: strict
# frozen_string_literal: true

module HasTimestampsWithDueAt
  extend T::Sig
  extend T::Helpers
  extend ActiveSupport::Concern
  requires_ancestor { ReduxApplicationRecord }

  class TimeHelper
    extend ActionView::Helpers::DateHelper
  end

  class TimestampScanInfo < T::ImmutableStruct
    extend T::Sig

    const :at, T.nilable(T.any(Time, ActiveSupport::TimeWithZone))
    const :interval, ActiveSupport::Duration

    sig { returns(String) }
    def ago_in_words
      at = self.at
      at ? TimeHelper.time_ago_in_words(at) + " ago" : "never"
    end

    sig { returns(T::Boolean) }
    def due?
      at = self.at
      at ? at < interval.ago : true
    end
  end

  protected

  included do
    T.unsafe(self).class_attribute :due_at_timestamp_fields, default: []

    sig(:final) do
      params(field: Symbol, interval: ActiveSupport::Duration).void
    end
    def self.due_timestamp(field, interval)
      attr_json_due_timestamp(field, interval, json_field: false)
    end

    sig(:final) do
      params(
        field: Symbol,
        interval: ActiveSupport::Duration,
        json_field: T::Boolean,
      ).void
    end
    def self.attr_json_due_timestamp(field, interval, json_field: true)
      field_pattern = /scanned_(.*)_at/
      field_name = field_pattern.match(field.to_s)&.[](1)&.to_sym
      raise "Invalid field name: #{field}" if field_name.nil?

      # define the attribute
      T.unsafe(self).attr_json(field, ActiveModelUtcTimeValue.new) if json_field

      T
        .unsafe(self)
        .define_method(:"#{field_name}_scan") do
          T.bind(self, HasTimestampsWithDueAt)
          at = read_attribute(field.to_s)
          TimestampScanInfo.new(at:, interval:)
        end

      T.unsafe(self).due_at_timestamp_fields =
        (T.unsafe(self).due_at_timestamp_fields || {}).merge(
          { field_name => interval },
        )
    end
  end
end
