<?php

namespace Planaday\Utility\FormBuilder\Structures;

use Planaday\Utility\FormBuilder\Condition;
use Planaday\Utility\FormBuilder\InvalidFormDataException;
use Planaday\Utility\FormBuilder\SettableData;
use Planaday\Utility\FormBuilder\Structure;

class RepeatingGroup implements Structure{
	use BasicStructure{
		setValue as protected standardSetValue;
	}

	/** @var callable(string|int $index):Structure */
	protected $repeatingContents;

	/** @var Condition[] */
	protected array $repeatingConditions = [];
	protected ?string $index = null;
	protected ?int $maxCount = null;
	protected bool $atLeastOne = false;
	protected bool $withRemoveButton = false;
	protected string $addButtonText = '';
	protected string $removeButtonText = '';

	public function withRemoveButton(): self {
		$this->withRemoveButton = true;
		return $this;
	}

	public function setAddButtonText( string $addButtonText ): self {
		$this->addButtonText = $addButtonText;

		return $this;
	}

	public function setRemoveButtonText( string $removeButtonText ): self {
		$this->removeButtonText = $removeButtonText;

		return $this;
	}

	public function setIndex( ?string $index ): self {
		$this->index = $index;
		return $this;
	}

	/**
	 * @param callable(string|int $index):Structure $repeatingContents
	 *
	 * @return $this
	 */
	public function setRepeatingContents( callable $repeatingContents ): self {
		$this->repeatingContents = $repeatingContents;
		return $this;
	}

	public function addRepeatingConditions( Condition ...$condition ): self {
		foreach ($condition as $c) {
			$this->repeatingConditions[] = $c;
		}
		return $this;
	}

	public function setMaxCount( ?int $maxCount ): self {
		$this->maxCount = $maxCount;
		return $this;
	}

	public function atLeastOne(): self {
		$this->atLeastOne = true;
		return $this;
	}

	public function setValue( mixed $value, bool $callSetCallback = true): void {
		if ( !is_array($value) ) {
			throw (new InvalidFormDataException())->addError('DataError', 'Invalid data type. Expected array.');
		}

		$contents = [];
		foreach ($value as $key => $item) {
			$group = (new BasicGroup())
				->setIndex($key)
				->setParent($this)
				->addContents(($this->repeatingContents)($key));
			$contents[] = $group;
		}

		$this->contents = $contents;

		$this->standardSetValue($value, $callSetCallback);
	}

	public function getIndex(): ?string {
		return $this->index;
	}

	public function getImplJson(): array {
		return [
			"type" => 'repeating',
			"maxCount" => $this->maxCount,
			"atLeastOne" => $this->atLeastOne,
			"repeatingContents" => $this->getRepeatedSegmentForJson(),
			"repeatingConditions" => $this->repeatingConditions,
			"withRemoveButton" => $this->withRemoveButton,
			"addButtonText" => $this->addButtonText,
			"removeButtonText" => $this->removeButtonText,
		];
	}

	public function getIndexTree(): array {
		$innerIndexes = [];

		foreach ($this->contents as $content) {
			if ($content instanceof SettableData) {
				$innerIndexes = array_merge($innerIndexes, $content->getIndexTree());
			}
		}

		$repeatingContents = ($this->repeatingContents)(0);

		foreach ($repeatingContents as $content) {
			if ($content instanceof SettableData) {
				$innerIndexes["repeating"] = array_merge($innerIndexes["repeating"], $content->getIndexTree());
			}
		}

		if ($this->index === null) return $innerIndexes;
		return [
			$this->index => $innerIndexes
		];
	}

	protected function getRepeatedSegmentForJson(): BasicGroup {
		return (new BasicGroup())
			->setIndex("templateIndex")
			->setParent($this)
			->addContents(($this->repeatingContents)("templateIndex"));
	}
}