# typed: strict
class Domain::Post::E621Post < Domain::Post
  attr_json :state, :string
  attr_json :e621_id, :integer
  # When was the post's /posts/<post_id>/favorites pages scanned?
  # Used to identify users with a significant number of favorites, setting
  # their `num_other_favs_cached` attribute
  attr_json :scanned_post_favs_at, ActiveModelUtcTimeValue.new
  attr_json :rating, :string
  attr_json :tags_array, ActiveModel::Type::Value.new
  attr_json :flags_array, :string, array: true
  attr_json :pools_array, :string, array: true
  attr_json :sources_array, :string, array: true
  attr_json :artists_array, :string, array: true

  attr_json :e621_updated_at, ActiveModelUtcTimeValue.new
  attr_json :parent_post_e621_id, :integer
  attr_json :last_index_page_id, :integer
  attr_json :caused_by_entry_id, :integer
  attr_json :scan_log_entry_id, :integer
  attr_json :index_page_ids, :integer, array: true
  attr_json :description, :string
  attr_json :score, :integer
  attr_json :score_up, :integer
  attr_json :score_down, :integer
  attr_json :num_favorites, :integer
  attr_json :num_comments, :integer
  attr_json :change_seq, :integer
  attr_json :md5, :string
  attr_json :prev_md5s, :string, array: true
  attr_json :scan_error, :string
  attr_json :uploader_user_id, :integer

  has_single_file!
  has_faving_users! Domain::User::E621User
  belongs_to_groups! :pools,
                     Domain::PostGroup::E621Pool,
                     Domain::PostGroupJoin::E621PoolJoin

  belongs_to :parent_post,
             class_name: "Domain::Post::E621Post",
             foreign_key: :parent_post_e621_id,
             primary_key: :e621_id,
             optional: true

  belongs_to :uploader_user,
             class_name: "::Domain::User::E621User",
             inverse_of: :uploaded_posts,
             optional: true

  belongs_to :last_index_page, class_name: "HttpLogEntry", optional: true
  belongs_to :scan_log_entry, class_name: "HttpLogEntry", optional: true
  belongs_to :caused_by_entry, class_name: "HttpLogEntry", optional: true

  validates :state, inclusion: { in: %w[ok removed scan_error file_error] }
  validates :rating,
            inclusion: {
              in: %w[s q e],
            },
            if: ->(p) { p.rating.present? }

  validates :e621_id, presence: true

  after_initialize do
    self.state ||= "ok"
    self.flags_array ||= []
    self.pools_array ||= []
    self.sources_array ||= []
    self.artists_array ||= []
  end

  sig { params(log_entry: HttpLogEntry).void }
  def set_index_page_entry(log_entry)
    self.last_index_page = log_entry
    self.index_page_ids ||= []
    self.index_page_ids << log_entry.id
  end

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

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

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

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

  sig { override.returns(T.nilable(String)) }
  def title
    self
      .sources_array
      .lazy
      .filter_map do |source|
        model =
          T.cast(
            T.unsafe(ApplicationController.helpers).link_for_source(source),
            T.nilable(Domain::PostsHelper::LinkForSource),
          )&.model
        return model.title if model && model.is_a?(Domain::Post)
      end
      .first || "E621 Post #{self.e621_id}"
  end

  sig { override.returns(T.nilable(String)) }
  def primary_creator_name_fallback_for_view
    self
      .sources_array
      .lazy
      .filter_map do |source|
        model =
          T.cast(
            T.unsafe(ApplicationController.helpers).link_for_source(source),
            T.nilable(Domain::PostsHelper::LinkForSource),
          )&.model
        if model && model.is_a?(Domain::Post) && model.class.has_creators?
          return T.unsafe(model).creator&.name_for_view
        elsif model && model.is_a?(Domain::User)
          return model.name_for_view
        end
      end
      .first ||
      self.class.first_valid_artist_tag_in(self.tags_array&.dig("artist")) ||
      self.class.first_valid_artist_tag_in(self.artists_array)
  end

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

  INVALID_ARTIST_TAGS = %w[unknown unknown_artist sound_warning].freeze

  sig { params(list: T.nilable(T::Array[String])).returns(T.nilable(String)) }
  def self.first_valid_artist_tag_in(list)
    list &&
      list
        .lazy
        .filter_map { |tag| INVALID_ARTIST_TAGS.include?(tag) ? nil : tag }
        .first
  end

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

  sig { override.returns(T.nilable(HttpLogEntry)) }
  def scanned_post_log_entry_for_view
    self.scan_log_entry || self.last_index_page || self.caused_by_entry
  end

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

  sig { override.returns(T.nilable(Addressable::URI)) }
  def external_url_for_view
    if self.e621_id.present?
      Addressable::URI.parse("https://e621.net/posts/#{self.e621_id}")
    end
  end

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

  sig { returns(String) }
  def rating_for_view
    case self.rating
    when nil
      "None"
    when "s"
      "Safe"
    when "q"
      "Questionable"
    when "e"
      "Explicit"
    else
      "Unknown (#{self.rating})"
    end
  end
end
