<?php

namespace CMDB\v2\Validators;

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

final class CmdbRequestValidator
{
    use \ValidatesRequests;

    /**
     * @param array $requestData
     * @throws \InvalidArgumentException
     * @return void
     */
    public static function validateCreateUpdateRequest(array $requestData)
    {

        $validator = Validation::createValidator();
        $constraints = new Assert\Collection(
            fields: [
                'name' => [
                    new Assert\Required(),
                    new Assert\Length(max: MAX_CMDB_NAME_LENGTH)
                ],
                'description' => new Assert\Optional([
                    new Assert\Type('string'),
                    new Assert\Length(max: MAX_VALUE_LENGTH)
                ]),
                'type' => [
                    new Assert\NotBlank(),
                    new Assert\Choice(
                        choices: ['inventory', 'class', 'variable', 'policy_configuration'],
                        message: "Only inventory, class, variable or policy_configuration are allowed types."
                    )
                ],
                'tags' => new Assert\Optional(new Assert\All([
                    new Assert\Type('string'),
                    new Assert\Length(max: MAX_CMDB_NAME_LENGTH)
                ])),
                'meta' => new Assert\Optional(new Assert\Type('array')),
                'entries' => new Assert\Optional(new Assert\All(
                    new Assert\Collection(
                        fields: [
                            'item_name' => [
                                new Assert\NotBlank(),
                                new Assert\Length(max: MAX_CMDB_NAME_LENGTH),
                                new Assert\Regex(
                                    pattern: '/^[a-zA-Z0-9_\.:]+$/',
                                    message: 'This field should contain only letters, numbers, dots, colons or underscores.'
                                )
                            ],
                            'item_type' => [
                                new Assert\NotBlank(),
                                new Assert\Choice(choices: ['class', 'variable'], message: "Only class or variable are allowed types.")
                            ],
                            'item_value' => new Assert\Optional(new Assert\Callback(function ($value, ExecutionContextInterface $context) {
                                $stringValue = json_encode($value);
                                if (mb_strlen($stringValue) > MAX_VALUE_LENGTH) {
                                    $context->buildViolation('item_value is too long. It should have {{ limit }} characters or less.')
                                        ->setParameter('{{ limit }}', MAX_VALUE_LENGTH)
                                        ->addViolation();
                                }
                            })),
                        ],
                        allowExtraFields: true
                    )
                )),
            ],
            allowExtraFields: true
        );

        self::throwOnIssues($validator->validate($requestData, $constraints));
    }
}
