# typed: strict
class Domain::Fa::Job::ScanPostJob < Domain::Fa::Job::Base
  queue_as :fa_post
  queue_with_priority(-15)

  sig { override.params(args: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
  def perform(args)
    post = post_from_args!(build_post: true)
    logger.push_tags(make_arg_tag(post))

    logger.info("scanning post")

    if force_scan? || (post.state_ok? && !post.scanned_at.present?) ||
         post.file&.state_file_error?
      ReduxApplicationRecord.transaction do
        scan_post(post)
        post.save!
        post.reload
      end
    end

    file = post.file
    logger.push_tags(make_arg_tag(file)) if file.present?

    if force_scan? ||
         (file.present? && (file.state == "pending") && file.url_str.present?)
      logger.info(
        format_tags("enqueue file job", make_tag("url", file&.url_str)),
      )
      defer_job(Domain::Fa::Job::ScanFileJob, { file: post.file })
    end

    if creator = post.creator
      logger.push_tags(make_arg_tag(creator))
      enqueue_user_scan(creator, at_most_one_scan: true)
    end

    if (fle = first_log_entry) && (fle.status_code == 200)
      suppress_deferred_job(Domain::Fa::Job::ScanPostJob, { fa_id: post.fa_id })
      suppress_deferred_job(Domain::Fa::Job::ScanPostJob, { post: })
      enqueue_jobs_from_found_links(fle)
    end

    logger.info format_tags("finished post scan")
  ensure
    post.save! if post
  end

  private

  sig { params(post: Domain::Post::FaPost).void }
  def scan_post(post)
    response =
      http_client.get("https://www.furaffinity.net/view/#{post.fa_id}/")
    post.last_submission_log_entry = response.log_entry

    if response.status_code == 404
      post.state = "scan_error"
      logger.error(format_tags("post does not exist"))
      return
    end

    if response.status_code != 200
      fatal_error(
        "error scanning fa_id #{post.fa_id}: #{response.status_code}, log entry #{response.log_entry.id}",
      )
    end

    page =
      Domain::Fa::Parser::Page.from_log_entry(
        response.log_entry,
        require_logged_in: false,
      )
    if page.submission_not_found?
      logger.error(format_tags("post was removed"))
      post.state = "removed"
      return
    end

    post.state = "ok"
    fatal_error("submission page is not logged in") unless page.logged_in?

    unless page.probably_submission?
      if response.body =~
           /The page you are trying to reach is currently pending deletion/
        logger.error(format_tags("post is pending deletion"))
        post.state = "removed"
        return
      else
        fatal_error("not a submission page")
      end
    end

    submission = page.submission

    unless submission.id.to_i == post.fa_id
      fatal_error("id mismatch: #{submission.id} != #{post.fa_id}")
    end

    # save before any changes so post has an id for any files
    post.state_ok!
    post.save!

    post.title = submission.title
    creator =
      Domain::User::FaUser.find_or_build_from_submission_parser(submission)
    creator.save!
    post.creator = creator
    post.category = submission.category
    post.description =
      submission.description_html.encode(
        "UTF-8",
        invalid: :replace,
        undef: :replace,
      )
    post.keywords = submission.keywords_array
    uri = Addressable::URI.parse(submission.full_res_img)
    uri.scheme = "https" if uri.scheme.blank?

    if (file = post.file) && (file.url_str != uri.to_s)
      file = post.files.build(url_str: uri.to_s)
    else
      file = post.file || post.build_file(url_str: uri.to_s)
    end
    if file.url_str_changed?
      file.enqueue_job_after_save(
        Domain::Fa::Job::ScanFileJob,
        {
          file:,
          caused_by_entry: causing_log_entry,
          caused_by_job_id: self.job_id,
        },
      )
    end
    file.save!
    post.theme = submission.theme
    post.species = submission.species
    post.gender = submission.gender
    post.num_favorites = submission.num_favorites
    post.num_comments = submission.num_comments
    post.num_views = submission.num_views
    post.posted_at = submission.posted_date&.in_time_zone("UTC")
    post.scanned_at = Time.now
  end
end
