<?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\Menu;

use Doctrine\ORM\EntityManager;
use Shopware\Models\Menu\Menu;
use Shopware\Models\Plugin\Plugin;

class InstallationHelper
{

    /**
     * @var EntityManager
     */
    private $entityManager;

    /**
     * @var Plugin
     */
    private $plugin;

    /**
     * @var callable A callable identical to the signature of Shopware_Components_Plugin_Bootstrap::createMenuItem
     */
    private $createMenuItem;

    /**
     * InstallationHelper constructor.
     *
     * @param EntityManager $entityManager
     * @param Plugin|null $plugin
     * @param callable $createMenuItem
     */
    public function __construct(EntityManager $entityManager, $plugin, callable $createMenuItem)
    {
        $this->entityManager = $entityManager;
        $this->plugin = $plugin;
        $this->createMenuItem = $createMenuItem;
    }

    private function menuItemEqualsSpec(Menu $existingItem, array $menuItemSpec)
    {
        // 0 is the default value, which need to use in comparisons.
        $newPosition = array_key_exists('position', $menuItemSpec) ? $menuItemSpec['position'] : 0;

        $bothActive = $existingItem->getActive() == $menuItemSpec['active']; // Lax check for true == 1.
        $sameController = $existingItem->getController() === $menuItemSpec['controller'];
        $sameClass = $existingItem->getClass() === $menuItemSpec['class'];
        $samePosition = $existingItem->getPosition() === $newPosition;
        $sameAction = $existingItem->getAction() === $menuItemSpec['action'];
        // Allow overwriting menu items, even if no plugin is provided
        $samePluginId = !$this->plugin || $existingItem->getPluginId() === $this->plugin->getId();
        // FIXME: What about style?
        // FIXME: What about onclick?
        // FIXME: What about hyperlink?
        // FIXME: What about resourceID?
        // FIXME: What about shortcut?

        /* @var $newParent Menu */
        $newParent = $menuItemSpec['parent'];
        $oldParent = $existingItem->getParent();
        $sameParent = $newParent === $oldParent
            || ($oldParent && $newParent && $oldParent->getId() === $newParent->getId());

        $isSame = $bothActive && $sameController && $sameClass && $samePosition && $sameAction && $samePluginId && $sameParent;

        return $isSame;
    }

    /**
     * Tries to find an existing menu item that is conflicting with the given
     * spec by fetching all menu items from the database, whose 'label' and
     * 'parentID' match the spec (label-parentID is a unique constraint on the
     * Shopware\Models\Menu\Menu model).
     *
     * @return Menu|null
     */
    private function findConflictingMenuItemOrNull(array $menuItemSpec)
    {
        // Check for conflicting item without considering only items of a
        // specific plugin
        $conflictingItems = $this->entityManager->getRepository('Shopware\\Models\\Menu\\Menu')->findBy([
            'label' => $menuItemSpec['label'],
            'parent' => $menuItemSpec['parent'],
        ]);

        return array_shift($conflictingItems);
    }

    /**
     * If the entity defined by $menuItemSpec does not equal an existing Menu in the database, a Menu entity will be
     * inserted and existing conflicting entities will be removed.
     *
     * @return Menu the Menu corresponding to $menuItemSpec (may be an existing entity)
     */
    public function ensureMenuItemInDatabaseIs(array $menuItemSpec)
    {
        $removeItem = null;

        $conflictingExistingItem = $this->findConflictingMenuItemOrNull($menuItemSpec);
        if ($conflictingExistingItem !== null) {
            if ($this->menuItemEqualsSpec($conflictingExistingItem, $menuItemSpec)) {
                return $conflictingExistingItem; // No need to recreate it. Exit early.
            }

            // The item differs in details. Remove old one and create new one;
            $removeItem = $conflictingExistingItem;
        }

        if ($removeItem !== null) {
            $this->entityManager->remove($removeItem);
            $this->entityManager->flush();
        }
        $createMenuItem = $this->createMenuItem;
        $newItem = $createMenuItem($menuItemSpec);
        if ($newItem !== null) { // createMenuItem may return null if $menuItemSpec['label'] is not set
            $this->entityManager->persist($newItem);
            $this->entityManager->flush();
        }

        return $newItem;
    }

    /**
     * Delete menu item from Database if it exist
     *
     * @param array $menuItemSpec
     * @return bool Return true if the delete process is success
     */
    public function deleteMenuItemFromDatabase(array $menuItemSpec)
    {
        $removeItem = null;

        $removeItem = $this->findConflictingMenuItemOrNull($menuItemSpec);
        if ($removeItem !== null) {
            $this->entityManager->remove($removeItem);
            $this->entityManager->flush();

            return true;
        }

        return false;
    }
}
