<?php

namespace Planaday\Utility\FormBuilder\InputFields;

use Planaday\Utility\FormBuilder\DataType;
use Planaday\Utility\FormBuilder\InvalidFormDataException;
use Planaday\Utility\FormBuilder\LayoutItems\BasicLayoutItem;
use Planaday\Utility\FormBuilder\Validator;

/**
 * @template T
 */
trait BasicInputField {
	use BasicLayoutItem;

	protected string $name = '';

	protected string $description = '';
	protected string $autocomplete = '';

	protected ?string $index = null;

	/** @var T|null */
	protected mixed $value = null;

	/** @var T|null */
	protected mixed $defaultValue = null;
	/**
	 * @var callable(T|null $value):void|null gets called at setValue after sanitization with the new value
	 */
	protected $setCallback = null;

	/**
	 * @var DataType<T>
	 */
	protected DataType $dataType;

	/**
	 * @param callable(T|null $value):void $callback gets called at setValue after sanitization with the new value
	 *
	 * @return $this
	 */
	public function addSetCallback(callable $callback): static {
		$this->setCallback = $callback;
		return $this;
	}

	/**
	 * @param T $defaultValue
	 *
	 * @return $this
	 */
	public function setDefaultValue($defaultValue): static {
		$this->defaultValue = $defaultValue;
		return $this;
	}

	public function setName(string $name): static{
		$this->name = $name;
		return $this;
	}

	public function setDescription(string $description): static{
		$this->description = $description;
		return $this;
	}

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

	public function setAutocomplete(string $autocomplete): static{
		$this->autocomplete = $autocomplete;
		return $this;
	}

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

	public function getName(): string{
		return $this->name;
	}

	public function getDescription(): string{
		return $this->description;
	}

	public function getAutocomplete(): string{
		return $this->autocomplete;
	}

	/**
	 * @return T|null
	 */
	public function getValue(): mixed{
		return $this->value;
	}

	/**
	 * @return T|null
	 */
	public function getDefaultValue(): mixed{
		return $this->defaultValue;
	}

	public function isSet(): bool{
		return $this->value !== null;
	}

	public function addValidator(Validator ...$validators): static{
		$this->dataType->addValidator(...$validators);
		return $this;
	}

	public function validate(): bool|InvalidFormDataException {
		if ($this->validateConditions()) return $this->dataType->validate($this->value);
		return true;
	}

	/**
	 * @param mixed $value
	 * @param bool $callSetCallback
	 *
	 * @return void
	 * @throws InvalidFormDataException
	 */
	public function setValue( mixed $value, bool $callSetCallback = true): void {
		$this->value = $this->dataType->sanitize($value);
		if ($this->setCallback !== null && $callSetCallback) ($this->setCallback)($this->value);
	}

	protected bool $isActive = true;
	public function applyFilter(array $filters): ?bool {
		$this->isActive = in_array($this->index, $filters);
		return $this->isActive;
	}

	public function isPastFilter():bool{
		return $this->isActive;
	}

	public function jsonSerialize(): array {
		return [
			"id" => $this->getItemId(),
			"type" => 'input',
			"impl" => $this->getImplJson(),
			"conditions" => $this->conditions,
			"value" => $this->value,
			"defaultValue" => $this->defaultValue,
			"name" => $this->name,
			"description" => $this->description,
			"index" => $this->index,
			"dataType" => $this->dataType,
			"autocomplete" => $this->autocomplete,
		];
	}

	public function getIndexTree(): array {
		return [$this->index => true];
	}

	protected abstract function getImplJson(): mixed;
}