<?php

declare(strict_types=1);

namespace NoahVet\Reef\Domain\Validation\V2;

final class OpenApiPropertyValidator implements OpenApiPropertyValidatorInterface
{
    /**
     * @param array<string, mixed> $rule
     */
    public function validate(string $propertyName, mixed $value, array $rule): bool
    {
        if ([] === $rule) {
            return true;
        }

        $nullable = \array_key_exists('nullable', $rule) && $rule['nullable'];

        if (null === $value) {
            return $nullable;
        }

        if (isset($rule['enum']) && \is_array($rule['enum'])) {
            return \in_array($value, $rule['enum'], true);
        }

        $type = $rule['type'] ?? null;

        if ('array' === $type) {
            if (!\is_array($value)) {
                return false;
            }

            if (isset($rule['items']) && \is_array($rule['items']) && !isset($rule['items']['properties'])) {
                foreach ($value as $item) {
                    if (!$this->validateValueAgainstRule($item, $rule['items'])) {
                        return false;
                    }
                }
            }

            return true;
        }

        if ('object' === $type) {
            return \is_array($value);
        }

        return $this->validatePrimitive($value, $type);
    }

    /**
     * @param array<string, mixed> $rule
     */
    private function validateValueAgainstRule(mixed $value, array $rule): bool
    {
        $nullable = \array_key_exists('nullable', $rule) && $rule['nullable'];

        if (null === $value) {
            return $nullable;
        }

        if (isset($rule['enum']) && \is_array($rule['enum'])) {
            return \in_array($value, $rule['enum'], true);
        }

        return $this->validatePrimitive($value, $rule['type'] ?? null);
    }

    private function validatePrimitive(mixed $value, ?string $type): bool
    {
        return match ($type) {
            'string' => \is_string($value),
            'integer' => \is_int($value),
            'number' => \is_int($value) || \is_float($value),
            'boolean' => \is_bool($value),
            'array', 'object' => \is_array($value),
            default => true,
        };
    }
}
