# frozen_string_literal: true

module Lineage
  module ActiveRecord
    # ActiveRecord instance methods (for dynamic inclusion).
    module InstanceMethods
      def root?
        ancestors.empty?
      end

      def ancestors depth = 1
        query = self.class.joins :ancestor_branches
        query = query.where(self.class.branch_table => {descendant_id: id})
        query = query.where ["#{self.class.branch_table}.depth >= ?", depth]
        query.order "#{self.class.branch_table}.ancestor_id DESC, " \
                    "#{self.class.branch_table}.descendant_id DESC"
      end

      def ancestor
        ancestors.last
      end

      def ancestor_of? descendant
        descendants.include? descendant
      end

      def add_ancestor ancestor
        # Create branches for all ancestors of self.
        ancestor_branches.each do |branch|
          self.class.branch_class.create ancestor_id: ancestor.id,
                                         descendant_id: branch.descendant_id,
                                         depth: branch.depth + 1
        end

        # Create branches for all ancestors of the ancestor.
        ancestor.descendant_branches.each do |branch|
          self.class.branch_class.create ancestor_id: branch.ancestor_id,
                                         descendant_id: id,
                                         depth: branch.depth + 1
        end
      end

      def remove_ancestor ancestor
        descendant_branches.where(ancestor_id: ancestor.id).each(&:destroy)
      end

      def descendants depth = 1
        query = self.class.joins :descendant_branches
        query = query.where(self.class.branch_table => {ancestor_id: id})
        query = query.where ["#{self.class.branch_table}.depth >= ?", depth]
        query.order "#{self.class.branch_table}.ancestor_id, " \
                    "#{self.class.branch_table}.descendant_id"
      end

      def descendant
        descendants.first
      end

      def descendant_of? ancestor
        ancestors.include? ancestor
      end

      def add_descendant descendant
        # Create branches for all ancestors of self.
        descendant_branches.each do |branch|
          self.class.branch_class.create ancestor_id: branch.ancestor_id,
                                         descendant_id: descendant.id,
                                         depth: branch.depth + 1
        end

        # Create branches for all descendants of the descendant.
        descendant.ancestor_branches.each do |branch|
          self.class.branch_class.create ancestor_id: id,
                                         descendant_id: branch.descendant_id,
                                         depth: branch.depth + 1
        end
      end

      def remove_descendant descendant
        ancestor_branches.where(descendant_id: descendant.id).each(&:destroy)
      end

      # Adds a new branch to the closure table for self.
      def add_branch
        self.class.branch_class.create ancestor_id: id, descendant_id: id, depth: 0
      end
    end
  end
end
