# typed: strict
class Domain::Post::FaPost < Domain::Post
  include AttrJsonRecordAliases

  attr_json :title, :string
  attr_json :state, :string
  attr_json :fa_id, :integer
  attr_json :category, :string
  attr_json :theme, :string
  attr_json :species, :string
  attr_json :gender, :string
  attr_json :description, :string
  attr_json :keywords, :string, array: true, default: []
  attr_json :num_favorites, :integer
  attr_json :num_comments, :integer
  attr_json :num_views, :integer
  attr_json :scanned_at, ActiveModelUtcTimeValue.new
  attr_json :scan_file_error, :string
  attr_json :last_user_page_id, :integer
  attr_json :first_browse_page_id, :integer
  attr_json :first_gallery_page_id, :integer
  attr_json :first_seen_entry_id, :integer

  # TODO - convert `file` to Domain::PostFile::FaPostFile and
  # move this to Domain::PostFile::FaPostFile
  attr_json :tried_from_fur_archiver, :boolean, default: false

  belongs_to :last_user_page, class_name: "::HttpLogEntry", optional: true
  belongs_to :first_browse_page, class_name: "::HttpLogEntry", optional: true
  belongs_to :first_gallery_page, class_name: "::HttpLogEntry", optional: true
  belongs_to :first_seen_entry, class_name: "::HttpLogEntry", optional: true

  has_single_file!
  has_single_creator! Domain::User::FaUser
  has_faving_users! Domain::User::FaUser

  after_initialize { self.state ||= "ok" }

  enum :state,
       {
         ok: "ok",
         removed: "removed",
         scan_error: "scan_error",
         file_error: "file_error",
       },
       prefix: "state"
  validates :state, presence: true
  validates :fa_id, presence: true

  sig { override.returns([String, Symbol]) }
  def self.param_prefix_and_attribute
    ["fa", :fa_id]
  end

  sig { override.returns(String) }
  def self.view_prefix
    "fa"
  end

  sig { override.returns(Symbol) }
  def self.post_order_attribute
    :fa_id
  end

  sig { override.returns(Domain::DomainType) }
  def self.domain_type
    Domain::DomainType::Fa
  end

  sig { override.returns(T.nilable(String)) }
  def title
    super
  end

  sig { override.returns(T.nilable(Domain::User)) }
  def primary_creator_for_view
    self.creator
  end

  sig { override.returns(T.nilable(T.any(String, Integer))) }
  def domain_id_for_view
    self.fa_id
  end

  sig { override.returns(T.nilable(Addressable::URI)) }
  def external_url_for_view
    if self.fa_id.present?
      Addressable::URI.parse("https://www.furaffinity.net/view/#{self.fa_id}")
    end
  end

  sig { override.returns(T.nilable(Domain::PostFile)) }
  def primary_file_for_view
    self.file
  end

  sig { override.returns(T.nilable(String)) }
  def description_html_for_view
    description
  end

  sig { override.returns(String) }
  def description_html_base_domain
    "furaffinity.net"
  end

  sig { override.returns(T.nilable(HttpLogEntry)) }
  def guess_last_submission_log_entry
    @guessed_last_submission_log_entry ||=
      T.let(
        [
          "https://www.furaffinity.net/view/#{self.fa_id}",
          "https://www.furaffinity.net/view/#{self.fa_id}/",
        ].lazy
          .map { |uri_str| HttpLogEntry.find_by_uri_host_path(uri_str) }
          .find(&:present?),
        T.nilable(HttpLogEntry),
      )
  end

  sig { override.returns(T.nilable(String)) }
  def description_for_view
    self.description
  end

  sig { override.returns(T.nilable(Integer)) }
  def num_favorites_for_view
    num_favorites
  end

  sig { override.returns(T.nilable(HttpLogEntry)) }
  def scanned_post_log_entry_for_view
    self.last_submission_log_entry || self.guess_last_submission_log_entry
  end

  sig { returns(T.nilable(String)) }
  def status_for_view
    case self.state
    when "ok"
      "OK"
    when "removed"
      "Removed"
    when "scan_error"
      "Scan error"
    when "file_error"
      "File error"
    end
  end

  sig do
    params(
      submission: Domain::Fa::Parser::ListedSubmissionParserHelper,
      first_seen_log_entry: T.nilable(HttpLogEntry),
    ).returns(Domain::Post::FaPost)
  end
  def self.find_or_initialize_by_submission_parser(
    submission,
    first_seen_log_entry: nil
  )
    creator =
      Domain::User::FaUser.find_or_create_by!(
        { url_name: submission.artist_url_name },
      ) { |user| user.name = submission.artist }

    post =
      Domain::Post::FaPost.find_or_initialize_by(fa_id: submission.id) do |post|
        post.first_seen_entry = first_seen_log_entry
      end

    post.creator ||= creator
    post.title ||= submission.title
    post
  end

  sig { override.returns(T.nilable(ActiveSupport::TimeWithZone)) }
  def posted_at
    pa = super
    return pa unless pa.nil?

    log_entry = guess_last_submission_log_entry
    if log_entry&.response_bytes
      parser =
        Domain::Fa::Parser::Page.from_log_entry(
          log_entry,
          require_logged_in: false,
        )
      if parser.probably_submission?
        parser.submission.posted_date&.in_time_zone("UTC")
      end
    end
  end

  sig { override.returns(T.nilable(T::Array[TagForView])) }
  def tags_for_view
    keywords.map { |value| TagForView.new(category: :general, value:) }
  end
end
