# frozen_string_literal: true

class Bookmark < ActiveRecord::Base
  BOOKMARK_LIMIT = 2000

  self.ignored_columns = [
    "delete_when_reminder_sent" # TODO(2021-07-22): remove
  ]

  belongs_to :user
  belongs_to :post
  belongs_to :topic

  def self.reminder_types
    @reminder_types ||= Enum.new(
      later_today: 1,
      next_business_day: 2,
      tomorrow: 3,
      next_week: 4,
      next_month: 5,
      custom: 6,
      start_of_next_business_week: 7,
      later_this_week: 8
    )
  end

  def self.auto_delete_preferences
    @auto_delete_preferences ||= Enum.new(
      never: 0,
      when_reminder_sent: 1,
      on_owner_reply: 2
    )
  end

  validates :reminder_at, presence: {
    message: I18n.t("bookmarks.errors.time_must_be_provided"),
    if: -> { reminder_type.present? }
  }

  validate :unique_per_post_for_user
  validate :ensure_sane_reminder_at_time
  validate :bookmark_limit_not_reached
  validates :name, length: { maximum: 100 }

  # we don't care whether the post or topic is deleted,
  # they hold important information about the bookmark
  def post
    Post.unscoped { super }
  end

  def topic
    Topic.unscoped { super }
  end

  def unique_per_post_for_user
    existing_bookmark = Bookmark.find_by(user_id: user_id, post_id: post_id)
    return if existing_bookmark.blank? || existing_bookmark.id == id
    self.errors.add(:base, I18n.t("bookmarks.errors.already_bookmarked_post"))
  end

  def ensure_sane_reminder_at_time
    return if reminder_at.blank?
    if reminder_at < Time.zone.now
      self.errors.add(:base, I18n.t("bookmarks.errors.cannot_set_past_reminder"))
    end
    if reminder_at > 10.years.from_now.utc
      self.errors.add(:base, I18n.t("bookmarks.errors.cannot_set_reminder_in_distant_future"))
    end
  end

  def bookmark_limit_not_reached
    return if user.bookmarks.count < BOOKMARK_LIMIT
    self.errors.add(:base, I18n.t("bookmarks.errors.too_many", user_bookmarks_url: "#{Discourse.base_url}/my/activity/bookmarks"))
  end

  def no_reminder?
    self.reminder_at.blank? && self.reminder_type.blank?
  end

  def auto_delete_when_reminder_sent?
    self.auto_delete_preference == Bookmark.auto_delete_preferences[:when_reminder_sent]
  end

  def auto_delete_on_owner_reply?
    self.auto_delete_preference == Bookmark.auto_delete_preferences[:on_owner_reply]
  end

  def clear_reminder!
    update!(
      reminder_at: nil,
      reminder_type: nil,
      reminder_last_sent_at: Time.zone.now,
      reminder_set_at: nil
    )
  end

  scope :pending_reminders, ->(before_time = Time.now.utc) do
    where("reminder_at IS NOT NULL AND reminder_at <= :before_time", before_time: before_time)
  end

  scope :pending_reminders_for_user, ->(user) do
    pending_reminders.where(user: user)
  end

  def self.count_per_day(opts = nil)
    opts ||= {}
    result = where('bookmarks.created_at >= ?', opts[:start_date] || (opts[:since_days_ago] || 30).days.ago)
    result = result.where('bookmarks.created_at <= ?', opts[:end_date]) if opts[:end_date]
    result = result.joins(:topic).merge(Topic.in_category_and_subcategories(opts[:category_id])) if opts[:category_id]
    result.group('date(bookmarks.created_at)')
      .order('date(bookmarks.created_at)')
      .count
  end

  ##
  # Deletes bookmarks that are attached to posts/topics that were deleted
  # more than X days ago. We don't delete bookmarks instantly when a post/topic
  # is deleted so that there is a grace period to un-delete.
  def self.cleanup!
    grace_time = 3.days.ago
    DB.exec(<<~SQL, grace_time: grace_time)
      DELETE FROM bookmarks b
      USING topics t, posts p
      WHERE (b.topic_id = t.id AND b.post_id = p.id)
        AND (t.deleted_at < :grace_time OR p.deleted_at < :grace_time)
    SQL
  end
end

# == Schema Information
#
# Table name: bookmarks
#
#  id                     :bigint           not null, primary key
#  user_id                :bigint           not null
#  topic_id               :bigint           not null
#  post_id                :bigint           not null
#  name                   :string(100)
#  reminder_type          :integer
#  reminder_at            :datetime
#  created_at             :datetime         not null
#  updated_at             :datetime         not null
#  reminder_last_sent_at  :datetime
#  reminder_set_at        :datetime
#  auto_delete_preference :integer          default(0), not null
#  pinned                 :boolean          default(FALSE)
#
# Indexes
#
#  index_bookmarks_on_post_id              (post_id)
#  index_bookmarks_on_reminder_at          (reminder_at)
#  index_bookmarks_on_reminder_set_at      (reminder_set_at)
#  index_bookmarks_on_reminder_type        (reminder_type)
#  index_bookmarks_on_topic_id             (topic_id)
#  index_bookmarks_on_user_id              (user_id)
#  index_bookmarks_on_user_id_and_post_id  (user_id,post_id) UNIQUE
#