<?php

namespace Velis\Dto\Traits;

use ReflectionAttribute;
use ReflectionProperty;
use Velis\Dto\Exceptions\ValidationException;
use Velis\Dto\Validators\BaseValidator;
use Velis\Dto\Validators\Validatable;

/**
 * This trait is responsible for validating DTOs.
 * @author Szymon Janaczek <szymon.janaczek@velistech.com>
 */
trait ValidationTrait
{
    /**
     * @throws ValidationException
     */
    protected function validateField(ReflectionProperty $property, $value, array $data): bool
    {
        // If value is null and property allows null, skip validation.
        if (is_null($value) && $property->getType()->allowsNull()) {
            return true;
        }

        $validators = $property->getAttributes(Validatable::class, ReflectionAttribute::IS_INSTANCEOF);
        $errors = [];
        // Iterate through validators and validate value using its logic.
        foreach ($validators as $attribute) {
            /** @var BaseValidator $instance */
            $instance = $attribute->newInstance();
            $instance->setDtoData($data);
            $isSuccess = $instance->validate($value);

            // If validation failed, add error to the list.
            if (!$isSuccess) {
                $errors[] = [
                    'property' => $property->getName(),
                    'message' => $instance->getMessage()
                ];
            }
        }

        if (count($errors)) {
            throw new ValidationException('Validation Failed', $errors);
        }

        return true;
    }
}
