<?php

namespace AutoAdmin\Bundle\Content\Duplicator;

use AutoAdmin\Bundle\BrickBuilder\Brick\BrickHandlerManagerInterface;
use AutoAdmin\Bundle\BrickBuilder\Brick\ConfigSetting\EntityCollectionSetting;
use AutoAdmin\Bundle\BrickBuilder\Brick\ConfigSetting\EntitySetting;
use AutoAdmin\Bundle\BrickBuilder\Brick\ConfigSetting\ScalarCollectionSetting;
use AutoAdmin\Bundle\BrickBuilder\Brick\ConfigSetting\ScalarSetting;
use AutoAdmin\Bundle\BrickBuilder\Brick\ConfigSetting\SubEntityCollectionSetting;
use AutoAdmin\Bundle\BrickBuilder\Enum\SettingDuplicateBehaviourEnum;
use AutoAdmin\Bundle\BrickBuilder\Model\BrickInterface;
use AutoAdmin\Bundle\BrickBuilder\Model\Setting;
use AutoAdmin\Bundle\BrickBuilder\Storage\ObjectSettingFormat;
use AutoAdmin\Bundle\Media\Model\FileInterface;
use ReflectionClass;

class BrickDuplicator extends AbstractDuplicator
{
    public function __construct(
        protected BrickHandlerManagerInterface $handlerManager,
        protected ObjectSettingFormat $brickFormat,
    ) {
    }

    public function supportInterfaces(): array
    {
        return [
            BrickInterface::class,
        ];
    }


    /**
     * @param BrickInterface $entity
     * @param BrickInterface $duplicate
     *
     * @return BrickInterface
     */
    public function duplicate(object $entity, object $duplicate): object
    {
        $duplicate->setType($entity->getType());
        $duplicate->setEnabled($entity->isEnabled());
        $duplicate->setPosition($entity->getPosition());

        $originalSettings = $entity->getSettings();

        $settings = [];

        $handler = $this->handlerManager->getHandler($entity);
        $config = $handler->getConfiguration();
        foreach ($config->getSettings() as $setting) {
            $name = $setting->getName();

            // only try to duplicate existing fields value in source settings array
            if (!array_key_exists($name, $originalSettings)) {
                continue;
            }

            if ($setting->getType() === EntitySetting::class) {
                $dupBehavior = $setting->getDuplicateBehavior();

                // default is set null
                if ($dupBehavior === SettingDuplicateBehaviourEnum::DEFAULT) {
                    $dupBehavior = SettingDuplicateBehaviourEnum::SET_NULL;
                }

                $settingValue = $originalSettings[$name];
                $sourceObject = null;
                if (is_string($settingValue) && $this->brickFormat->isValidObjectString($settingValue)) {
                    $sourceObject = $this->brickFormat->stringToObject($settingValue);
                }

                // override for known cases if default behaviour is expected
                if ($this->isFileInterfaceSpecificBehavior($setting, $sourceObject)) {
                    $dupBehavior = SettingDuplicateBehaviourEnum::DUPLICATE_OBJECT;
                }

                // handle regular cases
                if ($dupBehavior === SettingDuplicateBehaviourEnum::DUPLICATE_OBJECT) {
                    if (is_object($sourceObject)) {
                        $object = $this->duplicatorManager->duplicate($sourceObject);
                        $settings[$name] = $object;
                    } else {
                        $settings[$name] = null;
                    }
                } elseif ($dupBehavior == SettingDuplicateBehaviourEnum::KEEP_OBJECT) {
                    $object = $originalSettings[$name];
                    $settings[$name] = $object;
                } elseif ($dupBehavior == SettingDuplicateBehaviourEnum::SET_NULL) {
                    $settings[$name] = null;
                }
            } elseif (
                $setting->getType() === ScalarSetting::class
                || $setting->getType() === ScalarCollectionSetting::class
            ) {
                $settings[$name] = $originalSettings[$name] ?? null;
            } elseif (
                $setting->getType() === SubEntityCollectionSetting::class
                || $setting->getType() === EntityCollectionSetting::class
            ) {
                // default is: no dup for complex cases, can be addressed with custom duplicators, enjoy!
            }
        }

        $duplicate->setSettings($settings);

        foreach ($entity->getChildren() as $child) {
            $dupChild = $this->duplicatorManager->duplicate($child);
            $dupChild->setParent($duplicate);
            $duplicate->getChildren()->add($dupChild);
        }

        return $duplicate;
    }

    /**
     * @param Setting $setting
     * @param object|null $sourceObject
     * @return bool
     * @throws \ReflectionException
     */
    public function isFileInterfaceSpecificBehavior(Setting $setting, ?object $sourceObject): bool
    {
        if ($setting->getDuplicateBehavior() !== SettingDuplicateBehaviourEnum::DEFAULT) {
            return false;
        }

        $settingFormTypeClassIsFileInterface =
            array_key_exists('class', $settingFormTypeOptions = $setting->getFormTypeOptions())
            && ($targetClass = $settingFormTypeOptions['class'])
            && (new ReflectionClass($targetClass))->implementsInterface(FileInterface::class);

        if (!$settingFormTypeClassIsFileInterface) {
            return false;
        }

        return $sourceObject instanceof FileInterface;
    }
}
