# typed: strict
class Domain::E621::Job::ScanPostFavsJob < Domain::E621::Job::Base
  MAX_PAGES = T.let(200, Integer)
  MAX_USERS_PER_PAGE = T.let(Rails.env.test? ? 10 : 320, Integer)
  MAX_USERS_PER_SLICE = 1000

  class UserRow < T::Struct
    include T::Struct::ActsAsComparable

    const :e621_id, Integer
    const :name, String
    const :num_other_favs, Integer
  end

  sig { override.params(args: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
  def perform(args)
    post = T.cast(args[:post], Domain::Post::E621Post)
    page = 1
    breaker = 0
    total_created_users = 0
    logger.info("scanning post #{post.e621_id} favs")
    loop do
      break if breaker > MAX_PAGES
      logger.info("requesting page #{page}")
      url =
        "https://e621.net/posts/#{post.e621_id}/favorites?limit=#{MAX_USERS_PER_PAGE}&page=#{page}"
      response = http_client.get(url)
      if response.status_code != 200
        fatal_error("#{response.status_code} - bailing")
      end

      e621_id_to_user_row = T.let({}, T::Hash[Integer, UserRow])
      html = T.cast(Nokogiri.HTML(response.body), Nokogiri::HTML4::Document)
      rows = html.css("tbody tr")
      rows.each do |row_elem|
        user_member_elem = row_elem.css("td:first-child a")&.first
        e621_id = user_member_elem["href"].split("/").last.to_i
        e621_id_to_user_row[e621_id] = UserRow.new(
          e621_id: e621_id,
          name: user_member_elem.text,
          num_other_favs: row_elem.css("td:last-child").text.to_i,
        )
      end

      ReduxApplicationRecord.transaction do
        e621_id_to_user =
          T.cast(
            Domain::User::E621User.where(
              e621_id: e621_id_to_user_row.keys,
            ).index_by(&:e621_id),
            T::Hash[Integer, Domain::User::E621User],
          )
        e621_id_to_user_row.values.each do |user_row|
          user =
            e621_id_to_user[user_row.e621_id] ||
              Domain::User::E621User.new(
                e621_id: user_row.e621_id,
                name: user_row.name,
              )
          user.num_other_favs_cached = user_row.num_other_favs
          total_created_users += 1 if user.new_record?
          user.save!
        end

        logger.info(
          "[update user fav counts: #{e621_id_to_user_row.size}] [created users: #{total_created_users}]",
        )
      end

      if rows.count < MAX_USERS_PER_PAGE
        logger.info(
          "[no more users faving post, bailing] [total users: #{total_created_users}]",
        )
        break
      end

      page += 1
      breaker += 1
    end

    post.scanned_post_favs_at = Time.now
    post.save!
  end
end
