<?php

namespace AutoAdmin\Bundle\Content\Service;

use AutoAdmin\Bundle\Content\Duplicator\DuplicatorInterface;
use Doctrine\Common\Util\ClassUtils;
use LogicException;

class DuplicatorService
{
    /**
     * @var DuplicatorInterface[]|array
     */
    protected array $duplicators = [];

    public function duplicate($entity, $parent = null): ?object
    {
        if ($entity == null) {
            return null;
        }

        $class = ClassUtils::getRealClass(get_class($entity));
        $duplicate = new $class();

        foreach ($this->duplicators as $duplicator) {
            if (!$duplicator->isSupported($entity)) {
                continue;
            }

            $duplicate = $duplicator->duplicate($entity, $duplicate, $parent);
        }

        $duplicate = $this->dumbDuplicate($entity, $duplicate);

        return $duplicate;
    }

    public function addDuplicator($definition): void
    {
        $class = get_class($definition);

        if (isset($this->duplicators[$class])) {
            throw new LogicException(
                sprintf(
                    'Duplicator "%s" already exists',
                    $class
                )
            );
        }

        $this->duplicators[$class] = $definition;
    }

    /**
     * Dumb data copy for builtin types (example: avoid nullable in a bool prop)
     *
     * @param object $entity
     * @param object $duplicate
     * @return object
     */
    protected function dumbDuplicate(object $entity, object $duplicate): object
    {
        $refl = new \ReflectionClass($entity);

        foreach ($refl->getProperties() as $property) {
            if ($property->getType()->isBuiltin()) {
                $type = $property->getType()->getName();
                switch ($type) {
                    case 'bool':
                        $currentVal = $property->getValue($duplicate);
                        if ($currentVal !== false && $currentVal !== true) {
                            $currentVal = $property->getValue($entity);
                            $property->setValue($duplicate, $currentVal);
                        }
                        break;
                    default:
                        // do nothing
                        break;
                }
            }
        }

        return $duplicate;
    }
}
