<?php
namespace Newland\Toubiz\Map\Neos\DependencyInjection;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Aop\JoinPointInterface;
use Neos\Flow\ObjectManagement\ObjectManager;
use Neos\Flow\Reflection\ReflectionService;
use Newland\Toubiz\Map\Neos\DependencyInjection\SoftInjectProxy;
use ReflectionObject;

/**
 * @Flow\Aspect
 */
class SoftInjectAspect
{

    /**
     * @Flow\Inject()
     * @var ReflectionService
     */
    protected $reflectionService;

    /**
     * @Flow\Inject()
     * @var ObjectManager
     */
    protected $objectManager;

    /**
     * @Flow\Before(
     *     "classAnnotatedWith(Neos\Flow\Annotations\Scope) && method(Newland\Toubiz\Map\Neos\.*->__construct())"
     * )
     */
    public function scopeClasses(JoinPointInterface $joinPoint): void
    {
        $properties = $this->reflectionService->getPropertyNamesByAnnotation(
            $joinPoint->getClassName(),
            SoftInject::class
        );

        foreach ($properties as $property) {
            $types = $this->reflectionService
                ->getPropertyTagValues($joinPoint->getClassName(), $property, 'var');
            $type = implode('', $types);

            $this->setPropertyOnInstance(
                $joinPoint->getProxy(),
                $property,
                $this->initializeSoftInjectProxy($type)
            );
        }
    }

    /**
     * @param mixed $instance
     * @param mixed $propertyValue
     */
    private function setPropertyOnInstance($instance, string $propertyName, $propertyValue): void
    {
        $reflection = new \ReflectionProperty(\get_class($instance), $propertyName);
        $reflection->setAccessible(true);
        $reflection->setValue($instance, $propertyValue);
    }

    private function initializeSoftInjectProxy(string $type): SoftInjectProxy
    {
        /** @var SoftInjectProxy $proxy */
        $proxy = $this->objectManager->get(SoftInjectProxy::class);
        $proxy->initialize($type);
        return $proxy;
    }
}
