# frozen_string_literal: true

class PostFlag < ApplicationRecord
  module Reasons
    UNAPPROVED = "Unapproved in three days"
    REJECTED = "Unapproved in three days after returning to moderation queue%"
  end

  dtext_attribute :reason, inline: true # defines :dtext_reason

  belongs_to :creator, class_name: "User"
  belongs_to :post

  before_validation { post.lock! }
  validates :reason, visible_string: true, length: { in: 1..140 }
  validate :validate_creator_is_not_limited, on: :create
  validate :validate_post, on: :create
  validates :creator_id, uniqueness: { scope: :post_id, on: :create, unless: :is_deletion, message: "have already flagged this post" }
  after_create :update_post
  after_create :prune_disapprovals
  attr_accessor :is_deletion

  enum :status, {
    pending: 0,
    succeeded: 1,
    rejected: 2,
  }

  scope :by_users, -> { where.not(creator: User.system) }
  scope :by_system, -> { where(creator: User.system) }
  scope :in_cooldown, -> { by_users.where("created_at >= ?", Danbooru.config.moderation_period.ago) }
  scope :expired, -> { pending.where("post_flags.created_at < ?", Danbooru.config.moderation_period.ago) }
  scope :active, -> { pending.or(rejected.in_cooldown) }

  module SearchMethods
    def category_matches(category)
      case category
      when "normal"
        where("reason NOT IN (?) AND reason NOT LIKE ?", [Reasons::UNAPPROVED], Reasons::REJECTED)
      when "unapproved"
        where(reason: Reasons::UNAPPROVED)
      when "rejected"
        where("reason LIKE ?", Reasons::REJECTED)
      when "deleted"
        where("reason = ? OR reason LIKE ?", Reasons::UNAPPROVED, Reasons::REJECTED)
      else
        none
      end
    end

    def search(params, current_user)
      q = search_attributes(params, [:id, :created_at, :updated_at, :reason, :status, :post, :creator], current_user: current_user)

      if params[:category]
        q = q.category_matches(params[:category])
      end

      q.apply_default_order(params)
    end
  end

  extend SearchMethods

  def category
    case reason
    when Reasons::UNAPPROVED
      :unapproved
    when /#{Reasons::REJECTED.gsub("%", ".*")}/
      :rejected
    else
      :normal
    end
  end

  def prune_disapprovals
    return if is_deletion
    PostDisapproval.where(post: post).delete_all
  end

  def update_post
    post.update_column(:is_flagged, true) if pending?
  end

  def validate_creator_is_not_limited
    errors.add(:creator, "have reached your flag limit") if creator.is_flag_limited? && !is_deletion
  end

  def validate_post
    errors.add(:post, "is pending and cannot be flagged") if post.is_pending? && !is_deletion
    errors.add(:post, "is deleted and cannot be flagged") if post.is_deleted? && creator != User.system # DanbooruBot is allowed to prune expired appeals
    errors.add(:post, "is already flagged") if post.is_flagged? && !is_deletion
    errors.add(:post, "cannot be flagged") if !post.visible?(creator)

    flag = post.flags.in_cooldown.last
    if !is_deletion && !creator.is_approver? && flag.present?
      errors.add(:post, "cannot be flagged more than once every #{Danbooru.config.moderation_period.inspect} (last flagged: #{flag.created_at.to_formatted_s(:long)})")
    end
  end

  def uploader_id
    post.uploader_id
  end

  def self.available_includes
    [:post]
  end
end
