<?php

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

final class SettingsValidator
{
    use ValidatesRequests;

    /**
     * @param array $requestData
     * @throws \InvalidArgumentException
     * @return void
     */
    public static function validateCreateUpdateRequest(array $requestData)
    {
        $validator = Validation::createValidator();
        $constraints = new Assert\Collection(
            fields: [
            'rbacEnabled' => new Assert\Optional([
                new Assert\Type(type: 'bool')
            ]),
            'ldapEnabled' => new Assert\Optional([
                new Assert\Type(type: 'bool')
            ]),
            'enforce2FA' => new Assert\Optional([
                new Assert\Type(type: 'bool')
            ]),
            'allowLlmViewAccessToAttributesNames' => new Assert\Optional([
                new Assert\Type(type: 'bool')
            ]),
            'disableAI' => new Assert\Optional([
                new Assert\Type(type: 'bool')
            ]),
            'ldap_enable_role_syncing' => new Assert\Optional([
                new Assert\Type(type: 'bool')
            ]),
            'ldap_perform_sync_on_login' => new Assert\Optional([
                new Assert\Type(type: 'bool')
            ]),
            'ldap_remove_role_on_syncing' => new Assert\Optional([
                new Assert\Type(type: 'bool')
            ]),
            'hostIdentifier' => new Assert\Optional([
                new Assert\Type(type: 'string'),
                new Assert\NotBlank()
            ]),
            'ldap_roles_list_to_sync' => new Assert\Optional([
                new Assert\Type(type: 'string'),
                    new Assert\Callback(callback: function ($value, $context) {
                        // allow empty string
                        if (empty($value)) {
                            return;
                        }

                        $decoded = json_decode(json: $value, associative: true);

                        if (json_last_error() !== JSON_ERROR_NONE) {
                            $context->buildViolation('ldap_roles_list_to_sync must be a valid JSON array or empty string')
                                ->addViolation();
                            return;
                        }

                        if (!is_array($decoded)) {
                            $context->buildViolation('ldap_roles_list_to_sync must contain a JSON array')
                                ->addViolation();
                            return;
                        }

                        // Validate each item to follow same rule as role does
                        $pattern = '/^[A-Za-z0-9_\-\.\s]*$/';
                        foreach ($decoded as $index => $role) {
                            if (!preg_match($pattern, $role)) {
                                $context->buildViolation("Only letters, spaces, dashes, numbers, dots and underscores are allowed.")
                                    ->addViolation();
                            }
                        }
                    })
            ]),
            'logLevel' => new Assert\Optional([
                new Assert\Type(type: 'string'),
                new Assert\Choice(
                    choices: ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug'],
                    message: 'logLevel must be one of: emergency, alert, critical, error, warning, notice, info, debug'
                )
            ]),
            'blueHostHorizon' => new Assert\Optional([
                new Assert\Type(type: 'integer'),
                new Assert\GreaterThan(value: 0)
            ]),
            'hostsCollisionsThreshold' => new Assert\Optional([
                new Assert\Type(type: 'integer'),
                new Assert\GreaterThan(value: 0)
            ]),
            'passwordComplexity' => new Assert\Optional([
                new Assert\Type(type: 'integer'),
                new Assert\Range(
                    min: 0,
                    max: 4
                )
            ]),
            'minPasswordLength' => new Assert\Optional([
                new Assert\Type(type: 'integer'),
                new Assert\Range(
                    min: 8,
                    max: 20
                )
            ]),
            'passwordExpirationAfterResetHours' => new Assert\Optional([
                new Assert\Type(type: 'integer'),
                new Assert\GreaterThan(value: 0)
            ]),
            'deletedHostsCleanup' => new Assert\Optional([
                new Assert\Collection(
                    fields: [
                        'enabled' => new Assert\Required([
                            new Assert\Type(type: 'bool')
                        ]),
                        'days' => new Assert\Required([
                            new Assert\Type(type: 'integer'),
                            new Assert\GreaterThan(value: 0)
                        ])
                    ]
                )
            ]),
            'duplicateHostnameHostsCleanup' => new Assert\Optional([
                new Assert\Collection(
                    fields: [
                        'enabled' => new Assert\Required([
                            new Assert\Type(type: 'bool')
                        ]),
                        'completelyClear' => new Assert\Required([
                            new Assert\Type(type: 'bool')
                        ])
                    ]
                )
            ]),
            'duplicateIpHostsCleanup' => new Assert\Optional([
                new Assert\Collection(
                    fields: [
                        'enabled' => new Assert\Required([
                            new Assert\Type(type: 'bool')
                        ]),
                        'completelyClear' => new Assert\Required([
                            new Assert\Type(type: 'bool')
                        ])
                    ]
                )
            ]),
            'groupHostsCleanup' => new Assert\Optional([
                new Assert\Collection(
                    fields: [
                        'enabled' => new Assert\Required([
                            new Assert\Type(type: 'bool')
                        ]),
                        'groupId' => new Assert\Optional([
                            new Assert\Type(type: ['integer', 'null'])
                        ]),
                        'completelyClear' => new Assert\Optional([
                            new Assert\Type(type: 'bool')
                        ])
                    ]
                )
            ]),
            'inactiveHostsCleanup' => new Assert\Optional([
                new Assert\Collection(
                    fields: [
                        'enabled' => new Assert\Required([
                            new Assert\Type(type: 'bool')
                        ]),
                        'days' => new Assert\Required([
                            new Assert\Type(type: 'integer'),
                            new Assert\GreaterThan(value: 0)
                        ]),
                        'completelyClear' => new Assert\Required([
                            new Assert\Type(type: 'bool')
                        ])
                    ]
                )
            ])
            ],
            allowExtraFields: true
        );

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