# typed: strict
class Domain::Fa::PostEnqueuer
  extend T::Sig
  include HasBulkEnqueueJobs
  include HasColorLogger
  include HasMeasureDuration
  include Domain::Fa::HasCountFailedInQueue

  sig do
    params(
      start_at: Integer,
      stop_at: T.nilable(Integer),
      low_water_mark: Integer,
      high_water_mark: Integer,
    ).void
  end
  def initialize(start_at:, stop_at:, low_water_mark:, high_water_mark:)
    stop_at ||= 0
    @low_water_mark = low_water_mark
    @high_water_mark = high_water_mark
    raise if @high_water_mark <= @low_water_mark
    @post_iterator =
      T.let(
        Enumerator.new do |e|
          while start_at > stop_at
            if !Domain::Post::FaPost.exists?(fa_id: start_at)
              e << [nil, start_at, nil]
            end
            start_at -= 1
          end
        end,
        T::Enumerator[[T.nilable(Integer), Integer, T.nilable(String)]],
      )
  end

  sig { returns(T.nilable(Symbol)) }
  def run_once
    already_enqueued = enqueued_count
    if already_enqueued <= @low_water_mark
      to_enqueue = @high_water_mark - already_enqueued
      logger.info(
        "enqueuing #{to_enqueue.to_s.bold} more posts - #{already_enqueued.to_s.bold} already enqueued",
      )
      rows =
        measure("gathering posts...") do
          to_enqueue
            .times
            .map do
              @post_iterator.next
            rescue StopIteration
              nil
            end
            .compact
        end

      measure("enqueue jobs") do
        rows.each do |post_id, fa_id, file_url_str|
          if file_url_str.nil?
            Domain::Fa::Job::ScanPostJob.perform_later({ fa_id: fa_id })
            action = "page scan"
          else
            Domain::Fa::Job::ScanFileJob.perform_later({ fa_id: fa_id })
            action = "file scan"
          end

          if post_id.nil?
            model_info = "(not seen)"
          else
            model_info = "post #{post_id.to_s.bold}"
          end
          logger.info "[#{action}] [#{model_info}] fa@#{fa_id.to_s.bold}"
        end
      end
      raise StopIteration if rows.empty?
    else
      logger.info(
        "#{already_enqueued.to_s.bold} already enqueued (max #{@high_water_mark.to_s.bold}) - " +
          "waiting to fall below #{@low_water_mark.to_s.bold}",
      )
      :sleep
    end
  end

  private

  sig { returns(Integer) }
  def enqueued_count
    count_failed_in_queue(%w[static_file fa_post])
  end
end
