<?php

/*
 * This file is part of Sulu.
 *
 * (c) Sulu GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Sulu\Component\Content\Document\Subscriber;

use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector;
use Sulu\Bundle\DocumentManagerBundle\Bridge\PropertyEncoder;
use Sulu\Component\Content\Document\Behavior\ExtensionBehavior;
use Sulu\Component\Content\Document\Extension\ManagedExtensionContainer;
use Sulu\Component\Content\Extension\ExtensionManagerInterface;
use Sulu\Component\DocumentManager\Event\AbstractMappingEvent;
use Sulu\Component\DocumentManager\Events;
use Sulu\Component\DocumentManager\NamespaceRegistry;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ExtensionSubscriber implements EventSubscriberInterface
{
    /**
     * @var ExtensionManagerInterface
     */
    private $extensionManager;

    /**
     * @var DocumentInspector
     */
    private $inspector;

    /**
     * @var NamespaceRegistry
     */
    private $namespaceRegistry;

    /**
     * @var PropertyEncoder
     */
    private $encoder;

    /**
     * TODO: Remove this: Use a dedicated namespace instead.
     *
     * @var string
     */
    private $internalPrefix = '';

    public function __construct(
        PropertyEncoder $encoder,
        ExtensionManagerInterface $extensionManager,
        DocumentInspector $inspector,
        // these two dependencies should absolutely not be necessary
        NamespaceRegistry $namespaceRegistry
    ) {
        $this->encoder = $encoder;
        $this->extensionManager = $extensionManager;
        $this->inspector = $inspector;
        $this->namespaceRegistry = $namespaceRegistry;
    }

    public static function getSubscribedEvents()
    {
        return [
            // persist should happen before content is mapped
            Events::PERSIST => ['saveExtensionData', 10],
            Events::PUBLISH => ['saveExtensionData', 10],
            // hydrate should happen afterwards
            Events::HYDRATE => ['handleHydrate', -10],
        ];
    }

    public function handleHydrate(AbstractMappingEvent $event)
    {
        if (!$event->getDocument() instanceof ExtensionBehavior) {
            return;
        }

        $this->hydrate($event);
    }

    public function saveExtensionData(AbstractMappingEvent $event)
    {
        $locale = $event->getLocale();

        if (!$locale) {
            return;
        }

        $document = $event->getDocument();

        if (!$document instanceof ExtensionBehavior) {
            return;
        }

        $structureType = $document->getStructureType();
        $node = $event->getNode();
        $extensionsData = $document->getExtensionsData();

        $webspaceName = $this->inspector->getWebspace($document);
        $prefix = $this->namespaceRegistry->getPrefix('extension_localized');

        $extensions = $this->extensionManager->getExtensions($structureType);

        foreach ($extensions as $extension) {
            $extensionData = null;

            if (!isset($extensionsData[$extension->getName()])) {
                continue;
            }

            $extensionData = $extensionsData[$extension->getName()];

            $extension->setLanguageCode($locale, $prefix, $this->internalPrefix);
            $extension->save(
                $node,
                $extensionData,
                $webspaceName,
                $locale
            );
        }

        $this->hydrate($event);
    }

    private function hydrate(AbstractMappingEvent $event)
    {
        $document = $event->getDocument();
        $node = $event->getNode();
        $locale = $this->inspector->getLocale($document);
        $webspaceName = $this->inspector->getWebspace($document);
        $structureType = $document->getStructureType();

        if (null === $structureType) {
            return;
        }

        $prefix = $this->namespaceRegistry->getPrefix('extension_localized');
        $extensionContainer = new ManagedExtensionContainer(
            $structureType,
            $this->extensionManager,
            $node,
            $locale,
            $prefix,
            $this->internalPrefix,
            $webspaceName
        );

        $document->setExtensionsData($extensionContainer);
    }
}
