<?php
namespace SabaiApps\Framework\Model;

use SabaiApps\Framework\Exception;

/**
 * AbstractTreeEntity class for representing hierarchical entities in a tree structure.
 * Provides methods for managing parent, child, and descendant entities.
 */
abstract class AbstractTreeEntity extends AbstractEntity {

	/**
	 * @var int Number of parent entities.
	 */
	protected $_parentsCount;

	/**
	 * @var int Number of descendant entities.
	 */
	protected $_descendantsCount;

	/**
	 * @var int Number of first-level child entities.
	 */
	protected $_childrenCount;

	/**
	 * Retrieves all parent entities of this entity.
	 *
	 * @return AbstractEntityCollection Collection of parent entities.
	 */
	public function getParents() {
		if ( ! isset( $this->_objects['Parents'] ) ) {
			$this->_objects['Parents'] = $this->_getRepository()->fetchParents( $this->id );
		}

		return $this->_objects['Parents'];
	}

	/**
	 * Fetches all first-level child entities of this entity.
	 *
	 * @return AbstractEntityCollection Collection of child entities.
	 */
	protected function _fetchChildren() {
		if ( ! isset( $this->_objects['Children'] ) ) {
			$this->_objects['Children'] = $this->_getRepository()->fetchByParent( $this->id );
		}

		return $this->_objects['Children'];
	}

	/**
	 * Retrieves all descendant entities of this entity.
	 *
	 * @return AbstractEntityCollection Collection of descendant entities.
	 */
	public function getDescendants() {
		if ( ! isset( $this->_objects['Descendants'] ) ) {
			$this->_objects['Descendants'] = $this->_getRepository()->fetchDescendantsByParent( $this->id );
		}

		return $this->_objects['Descendants'];
	}

	/**
	 * Sets the number of parent entities.
	 *
	 * @param int $count Number of parent entities.
	 */
	public function setParentsCount( $count ) {
		$this->_parentsCount = $count;
	}

	/**
	 * Sets the number of first-level child entities.
	 *
	 * @param int $count Number of child entities.
	 */
	public function setChildrenCount( $count ) {
		$this->_childrenCount = $count;
	}

	/**
	 * Sets the number of descendant entities.
	 *
	 * @param int $count Number of descendant entities.
	 */
	public function setDescendantsCount( $count ) {
		$this->_descendantsCount = $count;
	}

	/**
	 * Gets the number of all first-level child entities.
	 *
	 * @return int Number of child entities.
	 */
	public function getChildrenCount() {
		if ( ! isset( $this->_childrenCount ) ) {
			$this->_childrenCount = $this->_getRepository()->countByParent( $this->id );
		}

		return $this->_childrenCount;
	}

	/**
	 * Gets the number of all descendant entities.
	 *
	 * @return int Number of descendant entities.
	 */
	public function getDescendantsCount() {
		if ( ! isset( $this->_descendantsCount ) ) {
			$this->_descendantsCount = $this->_getRepository()->countDescendantsByParent( $this->id );
		}

		return $this->_descendantsCount;
	}

	/**
	 * Gets the number of all parent entities for this entity.
	 *
	 * @return int Number of parent entities.
	 */
	public function getParentsCount() {
		if ( ! isset( $this->_parentsCount ) ) {
			$this->_parentsCount = $this->_getRepository()->countParents( $this->id );
		}

		return $this->_parentsCount;
	}

	/**
	 * Creates a new child entity.
	 *
	 * @return AbstractTreeEntity New child entity.
	 * @throws Exception If the current entity does not exist.
	 */
	public function createChild() {
		if ( ! $this->id ) {
			throw new Exception( sprintf( 'Cannot create a new child entity for a non-existent %s entity', $this->getName() ) );
		}
		$child         = $this->_model->create( $this->getName() );
		$child->Parent = $this;

		return $child;
	}
}
