<?php
namespace SabaiApps\Framework\Model\EntityCollection;

use SabaiApps\Framework\Model\Model;

/**
 * AbstractEntityCollection class for managing collections of entities.
 * Provides methods for iterating, updating, deleting, and accessing entities within the collection.
 */
abstract class AbstractEntityCollection implements \Iterator, \Countable {

	protected $_name, $_model, $_key = 0;

	/**
	 * Constructor.
	 *
	 * @param Model  $model Model instance.
	 * @param string $name Name of the entity.
	 */
	protected function __construct( Model $model, $name ) {
		$this->_name  = $name;
		$this->_model = $model;
	}

	/**
	 * Gets the name of the entity.
	 *
	 * @return string
	 */
	public function getName() {
		return $this->_name;
	}

	/**
	 * Gets the model instance.
	 *
	 * @return Model
	 */
	public function getModel() {
		return $this->_model;
	}

	/**
	 * Decorates the collection with additional functionality.
	 *
	 * @return mixed
	 */
	public function with() {
		$args = func_get_args();
		return $this->_model->decorate( $this, $args );
	}

	/**
	 * Gets all entity IDs in the collection.
	 *
	 * @return array
	 */
	public function getAllIds() {
		return $this->getArray( 'id' );
	}

	/**
	 * Converts the collection into an associative array.
	 *
	 * @param string|null $var Variable name to use as values.
	 * @param string|null $key Variable name to use as keys.
	 * @return array
	 */
	public function getArray( $var = null, $key = null ) {
		$ret = array();
		$this->rewind();
		$key = isset( $key ) ? $key : 'id';
		while ( $this->valid() ) {
			$entity               = $this->current();
			$ret[ $entity->$key ] = isset( $var ) ? $entity->$var : $entity;
			$this->next();
		}

		return $ret;
	}

	/**
	 * Updates values of all entities in the collection.
	 *
	 * @param array $values Associative array of values to update.
	 * @param bool  $commit Whether to commit changes to the database.
	 */
	public function update( array $values, $commit = false ) {
		$this->rewind();
		while ( $this->valid() ) {
			foreach ( $values as $key => $value ) {
				$this->current()->set( $key, $value );
			}
			$this->next();
		}
		if ( $commit ) {
			$this->_model->commit();
		}
	}

	/**
	 * Marks all entities in the collection as removed.
	 *
	 * @param bool $commit Whether to commit changes to the database.
	 */
	public function delete( $commit = false ) {
		$this->rewind();
		while ( $this->valid() ) {
			$this->current()->markRemoved();
			$this->next();
		}
		if ( $commit ) {
			$this->_model->commit();
		}
	}

	/**
	 * Gets the next entity in the collection.
	 *
	 * @return mixed
	 */
	public function getNext() {
		$ret = false;
		if ( $this->valid() ) {
			$ret = $this->current();
			$this->next();
		}
		return $ret;
	}

	/**
	 * Gets the first entity in the collection.
	 *
	 * @return mixed
	 */
	public function getFirst() {
		$this->rewind();
		return $this->valid() ? $this->current() : false;
	}

	#[\ReturnTypeWillChange]
	public function rewind() {
		$this->_key = 0;
	}

	#[\ReturnTypeWillChange]
	public function next() {
		++$this->_key;
	}

	/**
	 * Gets the current entity in the collection.
	 *
	 * @return SabaiApps\Framework\Model\AbstractEntity
	 */
	#[\ReturnTypeWillChange]
	public function current() {
		$ret = $this->getCurrent( $this->_key );
		$this->_model->cacheEntity( $ret );

		return $ret;
	}

	/**
	 * Abstract method for retrieving the current entity by index.
	 *
	 * @param int $index Index of the entity.
	 * @return mixed
	 */
	abstract public function getCurrent( $index );

	/**
	 * Gets the current key in the collection.
	 *
	 * @return int
	 */
	#[\ReturnTypeWillChange]
	public function key() {
		return $this->_key;
	}
}
