<?php

namespace Planaday\Utility\FormBuilder\DataTypes;

use Planaday\Utility\FormBuilder\DataType;
use Planaday\Utility\FormBuilder\InvalidFormDataException;

/**
 * @template T
 * @implements DataType<array<T>>
 */
class ArrayType implements DataType{
	use TypeValidation{
		validate as traitValidate;
	}

	/**
	 * @param DataType<T> $arrayType
	 */
	public function __construct(
		protected DataType $arrayType
	) {}

	/**
	 * @param mixed $value
	 *
	 * @return T[]|null
	 * @throws InvalidFormDataException
	 */
	public function sanitize( mixed $value ): ?array {
		if ( $value === null){
			return null;
		}

		$exception = new InvalidFormDataException();
		$shouldThrow = false;

		if ( !is_array($value) ) {
			throw (new InvalidFormDataException)->addError('DataError', 'Invalid data type. Expected array.');
		}

		$cleanData = [];
		foreach ($value as $key => $arrayValue) {
			try {
				$cleanData[$key] = $this->arrayType->sanitize($arrayValue);
			} catch (InvalidFormDataException $e) {
				$exception->addError($key, $e->getErrors());
				$shouldThrow = true;
			}
		}

		if ($shouldThrow) {
			throw $exception;
		}

		return $cleanData;
	}

	/**
	 * @param array<T>|null $value
	 *
	 * @return true|InvalidFormDataException
	 */
	public function validate($value): bool|InvalidFormDataException {
		$exception = new InvalidFormDataException();
		$shouldThrow = false;

		foreach (($value ?? []) as $key => $arrayValue) {
			if (($e = $this->arrayType->validate($arrayValue)) !== true) {
				$exception->addError($key, $e->getErrors());
				$shouldThrow = true;
			}
		}

		if ($shouldThrow) return $exception;

		return $this->traitValidate($value);
	}

	public function jsonSerialize(): array {
		return [
			"type" => 'array',
			"subType" => $this->arrayType,
			"validators" => $this->validators
		];
	}
}