class TagImplication < ApplicationRecord
  belongs_to :tag, class_name: 'Tag'
  belongs_to :parent_tag, class_name: 'Tag'

  validates :tag_id, presence: true, uniqueness: { scope: :parent_tag_id }
  validates :parent_tag_id, presence: true

  validate :parent_tag_mustnt_be_itself
  validate :parent_tag_mustnt_create_cycle

  private

  def parent_tag_mustnt_be_itself
    if parent_tag == tag
      errors.add :parent_tag_id, '親タグは子タグと同一であってはなりません．'
    end
  end

  def parent_tag_mustnt_create_cycle
    return if tag_id.blank? || parent_tag_id.blank?
    return if errors[:parent_tag_id].present?

    seen = { }
    stack = [parent_tag_id]

    until stack.empty?
      current_id = stack.pop
      next if seen[current_id]

      seen[current_id] = true

      if current_id == tag_id
        errors.add :parent_tag_id, '親タグに子孫タグを指定すると循環します．'
        errors.add :base, 'タグの親子関係が循環します．'
        return
      end

      stack.concat(TagImplication.where(tag_id: current_id).pluck(:parent_tag_id))
    end
  end
end
