<?php
/**
 * @author      Aurélien BERNARD - TRYDEA - 2021
 */

declare(strict_types=1);

namespace Shared\Enumeration\EventSubscriber;

use Doctrine\Common\Annotations\Reader;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Events;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use ReflectionClass;
use Shared\Enumeration\Annotation\EnumType;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;

/**
 * Class EnumValidValueSubscriber
 * @package Shared\Enumeration\EventSubscriber
 */
class EnumValidValueSubscriber implements EventSubscriber
{
    private $annotationReader;
    private $propertyAccessor;

    public function __construct(
        Reader $annotationReader,
        PropertyAccessorInterface $propertyAccessor
    )
    {
        $this->annotationReader = $annotationReader;
        $this->propertyAccessor = $propertyAccessor;
    }

    public
    function getSubscribedEvents(): array
    {
        return [
            Events::prePersist,
            Events::preUpdate,
        ];
    }

    public
    function prePersist(LifecycleEventArgs $event): void
    {
        $this->checkEnumTypeAnnotation($event->getObject());
    }

    public
    function preUpdate(LifecycleEventArgs $event): void
    {
        $this->checkEnumTypeAnnotation($event->getObject());
    }

    private
    function checkEnumTypeAnnotation(object $object): void
    {
        $reflectionClass = new ReflectionClass($object);
        $errors = [];
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
            $annotation = $this->annotationReader->getPropertyAnnotation($reflectionProperty, EnumType::class);
            if ($annotation) {
                $value = $this->propertyAccessor->getValue($object, $reflectionProperty->getName());
                if (!$annotation->class::isValidValue($value, $annotation->nullable)) {
                    $classNameArr = explode('\\', $annotation->class);
                    $errors[] = sprintf('Property "%s" with value "%s" does not match any value in "%s"',
                        $reflectionProperty->getName(),
                        $value,
                        array_pop($classNameArr),
                    );
                }
            }
        }
        if (0 < sizeof($errors)) {
            throw new BadRequestHttpException(join('; ', $errors));
        }
    }
}
