<?php
/**
 * API to load, inspect and mutate recurrence/RSET data for Events Pro.
 *
 * @since 6.0.8
 *
 * @package TEC\Events_Pro\Custom_Tables\V1\Events
 */

namespace TEC\Events_Pro\Custom_Tables\V1\Events;

use DateTime;
use Tribe__Events__Date_Utils;


/**
 * Class Recurrence_Rule
 *
 * @since 6.0.8
 *
 * @package TEC\Events_Pro\Custom_Tables\V1\Events
 */
class Recurrence_Rule {

	/**
	 * Contains the _EventRecurrence individual rule.
	 *
	 * @since 6.0.8
	 *
	 * @var array<string,mixed> The Recurrence rule.
	 */
	protected $rule = [];

	/**
	 * Factory to generate an instance of Recurrence_Rule from the rule array.
	 *
	 * @since 6.0.8
	 *
	 * @param array $rule The recurrence rule.
	 *
	 * @return Recurrence_Rule Instance generated by the rule passed.
	 */
	public static function from_recurrence_rule( array $rule ): Recurrence_Rule {
		$instance = new self();

		return $instance->set_rule( $rule );
	}

	/**
	 * Override the rule for this instance.
	 *
	 * @since 6.0.8
	 *
	 * @param array $rule The recurrence rule.
	 *
	 * @return $this This instance.
	 */
	public function set_rule( array $rule ): Recurrence_Rule {
		$this->rule = $rule;

		// No limit? Set to infinite.
		if ( ! $this->has_count_limit() && ! $this->has_until_limit() ) {
			$this->set_infinite();
		}

		return $this;
	}

	/**
	 * Does this rule have a count limit.
	 *
	 * @since 6.0.8
	 *
	 * @return bool
	 */
	public function has_count_limit(): bool {
		return $this->get_count_limit() !== null;
	}

	/**
	 * Does this rule have an until limit.
	 *
	 * @since 6.0.8
	 *
	 * @return bool
	 */
	public function has_until_limit(): bool {
		return $this->get_until_limit() !== null;
	}

	/**
	 * Get the count limit.
	 *
	 * @since 6.0.8
	 *
	 * @return null|int The count limit if one is defined.
	 */
	public function get_count_limit(): ?int {
		if ( isset( $this->rule['end-count'] ) && is_numeric( $this->rule['end-count'] ) ) {

			return (int) $this->rule['end-count'];
		}

		return null;
	}

	/**
	 * Set or remove the count limit.
	 *
	 * @since 6.0.8
	 *
	 * @param int|null $count The count limit.
	 *
	 * @return $this For chaining purposes.
	 */
	public function set_count_limit( ?int $count ): Recurrence_Rule {
		// Unset this limit?
		if ( $count === null ) {
			unset( $this->rule['end-count'] );
			$this->rule['end-type'] = 'Never'; // Assume never.

			return $this;
		}

		$this->rule['end-count'] = $count;
		$this->rule['end-type']  = 'After';
		unset( $this->rule['end'] );

		return $this;
	}


	/**
	 * Set this rule to be infinite.
	 *
	 * @since 6.0.11
	 *
	 * @return $this For chaining purposes.
	 */
	public function set_infinite(): Recurrence_Rule {
		unset( $this->rule['end-count'], $this->rule['end'] );
		$this->rule['end-type'] = 'Never'; // Assume never.

		return $this;
	}


	/**
	 * Get the until limit.
	 *
	 * @since 6.0.8
	 *
	 * @return null|DateTime The count limit if one is defined.
	 */
	public function get_until_limit(): ?DateTime {
		if ( ! empty( $this->rule['end'] ) ) {

			return Tribe__Events__Date_Utils::build_date_object(
				$this->rule['end'],
				$this->rule['EventTimezone'] ?? null
			);
		}

		return null;
	}

	/**
	 * Checks if this rule has any limits defined.
	 *
	 * @since 6.0.8
	 *
	 * @return bool Whether this rule runs infinitely or not (if no limit is defined).
	 */
	public function is_infinite(): bool {
		return ! $this->has_until_limit() && ! $this->has_count_limit();
	}

	/**
	 * Set or remove the UNTIL limit.
	 *
	 * @since 6.0.8
	 *
	 * @param string|null $until The until limit.
	 *
	 * @return $this For chaining purposes.
	 */
	public function set_until_limit( $until ): Recurrence_Rule {
		// Unset this limit?
		if ( $until === null ) {
			unset( $this->rule['end'] );
			$this->rule['end-type'] = 'Never'; // Assume never.

			return $this;
		}

		$this->rule['end']      = $until;
		$this->rule['end-type'] = 'On';
		unset( $this->rule['end-count'] );

		return $this;
	}

	/**
	 * Retrieve this rules _EventRecurrence format.
	 *
	 * @since 6.0.8
	 *
	 * @return array<string,mixed> The rule array.
	 */
	public function to_event_recurrence_rule(): array {
		return $this->rule;
	}
}
