<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonCommon\Classes\Installation;

use Doctrine\ORM\PersistentCollection;
use Shopware\Components\Model\ModelManager;
use Shopware\Models\Config\Element;
use Shopware\Models\Config\Value;
use Shopware\Plugins\ViisonCommon\Classes\Util\Util as ViisonCommonUtil;

class FormElementMigrator
{
    /**
     * @var ModelManager
     */
    private $modelManager;

    /**
     * @param ModelManager $modelManager
     */
    public function __construct(ModelManager $modelManager)
    {
        $this->modelManager = $modelManager;
    }

    /**
     * Checks whether the backend Base controller has the 'getDocTypesAction' method and, if it does, converts the
     * config element $element to a multi select combobox that is backend by the document type store.
     *
     * @param Element $element
     * @param array $additionalOptions ExtJS options for the combobox
     */
    public function convertTextConfigElementToDocTypeComboboxElement(Element $element, $multiSelect = false, array $additionalOptions = [])
    {
        // Make sure the document type combobox is available
        $reflectionClass = new \ReflectionClass('Shopware_Controllers_Backend_Base');
        if (!$reflectionClass->hasMethod('getDocTypesAction')) {
            return;
        }
        if ($element->getType() === 'select') {
            // Form element was migrated already
            return;
        }

        // Persists the form before making any changes
        $form = $element->getForm();
        $this->modelManager->persist($form);

        $element->setType('select');
        // Migrate default and current value

        $element->setValue($this->convertTextValue($element->getValue(), $multiSelect));
        if ($element->getValues()) {
            foreach ($element->getValues() as $value) {
                $value->setValue($this->convertTextValue($value->getValue(), $multiSelect));
                $this->modelManager->flush($value);
            }
        }
        // Add select/store options
        $options = $element->getOptions();
        $options['store'] = 'Shopware.apps.Base.store.DocType';
        $options['displayField'] = 'name';
        $options['multiSelect'] = $multiSelect;
        $options['editable'] = !$multiSelect;
        $options = array_merge($options, $additionalOptions);
        $element->setOptions($options);
        $this->modelManager->flush($element);
    }

    /**
     * Convert a value of a text field to be used with a select field
     *
     * @param $textValue
     * @param bool $multiple True, if the select field allows multiSelect
     * @return array|mixed
     */
    private function convertTextValue($textValue, $multiple)
    {
        $newValue = ViisonCommonUtil::safeExplode(',', $textValue);
        $newValue = array_map('intval', $newValue);
        $newValue = array_values(array_unique($newValue));

        return $multiple ? $newValue : $newValue[0];
    }

    /**
     * Copys all Values from a from the Form-Element $fromElement to the Form-Element $toElement
     *
     * The optional callback $converter can be provided to convert the values while copying
     *
     * @param Element $fromElement
     * @param Element $toElement
     * @param callable|null $converter
     */
    public function copyFormElementValues(Element $fromElement, Element $toElement, callable $converter = null)
    {
        // Persists the form before making any changes
        $form = $toElement->getForm();
        $this->modelManager->persist($form);
        $this->modelManager->persist($toElement);

        /** @var Value $oldValue */
        $newValues = [];
        /** @var PersistentCollection $oldValues */
        $oldValues = $fromElement->getValues();
        foreach ($oldValues as $oldValue) {
            $newValue = new Value();
            $newValue->setElement($toElement);
            $newValue->setShop($oldValue->getShop());
            if (is_callable($converter)) {
                $newValue->setValue($converter($oldValue->getValue()));
            } else {
                $newValue->setValue($oldValue->getValue());
            }
            $this->modelManager->persist($newValue);
            $newValues[] = $newValue;
        }
        if (!empty($newValues)) {
            $this->modelManager->flush($newValues);
        }
    }
}
