<?php

declare(strict_types=1);

namespace FiloBlu\ExtBlackbirdMenuManager\Plugin\Controller\Adminhtml\Menu;

use Blackbird\MenuManager\Api\MenuRepositoryInterface;
use Blackbird\MenuManager\Api\NodeRepositoryInterface;
use Blackbird\MenuManager\Model\Menu\NodeFactory;
use Blackbird\MenuManager\Model\MenuFactory;

use function is_array;
use Magento\Framework\App\Request\Http;
use Magento\Framework\App\RequestInterface;

/**
 *
 */
class Save
{

    /**
     * @var \Blackbird\MenuManager\Api\NodeRepositoryInterface
     */
    private $nodeRepository;
    /**
     * @var \Blackbird\MenuManager\Model\Menu\NodeFactory
     */
    private $nodeFactory;
    /**
     * @var \Blackbird\MenuManager\Api\MenuRepositoryInterface
     */
    private $menuRepository;
    /**
     * @var \Blackbird\MenuManager\Model\MenuFactory
     */
    private $menuFactory;

    /**
     * @param \Blackbird\MenuManager\Api\MenuRepositoryInterface $menuRepository
     * @param \Blackbird\MenuManager\Model\MenuFactory $menuFactory
     * @param \Blackbird\MenuManager\Api\NodeRepositoryInterface $nodeRepository
     * @param \Blackbird\MenuManager\Model\Menu\NodeFactory $nodeFactory
     */
    public function __construct(
        MenuRepositoryInterface $menuRepository,
        MenuFactory $menuFactory,
        NodeRepositoryInterface $nodeRepository,
        NodeFactory $nodeFactory
    ) {
        $this->nodeRepository = $nodeRepository;
        $this->nodeFactory = $nodeFactory;
        $this->menuRepository = $menuRepository;
        $this->menuFactory = $menuFactory;
    }

    /**
     * @param \Blackbird\MenuManager\Controller\Adminhtml\Menu\Save $subject
     * @return null
     */
    public function beforeExecute(\Blackbird\MenuManager\Controller\Adminhtml\Menu\Save $subject)
    {
        $menu = $this->addCustomerGroupToMenu($subject);
        /** @var Http|RequestInterface $request */
        $request = $subject->getRequest();
        $request->setParam('id', $menu->getId());
        $request->setPostValue('serialized_nodes', $this->addCustomerGroupsToNodes($subject, $menu));

        return null;
    }

    /**
     * @param \Blackbird\MenuManager\Controller\Adminhtml\Menu\Save $subject
     * @return \Blackbird\MenuManager\Api\Data\MenuInterface
     */
    public function addCustomerGroupToMenu(\Blackbird\MenuManager\Controller\Adminhtml\Menu\Save $subject)
    {
        $menu = $this->getMenu($subject);
        $customerGroups = $subject->getRequest()->getParam('customer_group');
        $menu->setCustomerGroup($customerGroups ? implode(',', $customerGroups) : null);

        return $this->menuRepository->save($menu);
    }

    /**
     * @param \Blackbird\MenuManager\Controller\Adminhtml\Menu\Save $subject
     * @return \Blackbird\MenuManager\Model\Menu
     */
    public function getMenu(\Blackbird\MenuManager\Controller\Adminhtml\Menu\Save $subject)
    {
        $id = $subject->getRequest()->getParam('id');

        if ($id) {
            $menu = $this->menuRepository->getById($id);
            $menu->setTitle($subject->getRequest()->getParam('title'));
            $menu->setIdentifier($subject->getRequest()->getParam('identifier'));
            $menu->setIsActive($subject->getRequest()->getParam('menu_status'));
            $menu->setStoreViews($subject->getRequest()->getParam('stores'));
            return $menu;
        }

        return $this->menuFactory->create([
            'data' => [
                'title'       => $subject->getRequest()->getParam('title'),
                'identifier'  => $subject->getRequest()->getParam('identifier'),
                'is_active'   => $subject->getRequest()->getParam('menu_status'),
                'store_views' => $subject->getRequest()->getParam('stores')
            ]
        ]);
    }

    /**
     * @param \Blackbird\MenuManager\Controller\Adminhtml\Menu\Save $subject
     * @param $menu
     * @return mixed|void
     */
    public function addCustomerGroupsToNodes(\Blackbird\MenuManager\Controller\Adminhtml\Menu\Save $subject, $menu)
    {
        $nodes = $subject->getRequest()->getParam('serialized_nodes');

        if (empty($nodes)) {
            return $nodes;
        }

        $nodes = json_decode($nodes, true);

        $id = $menu->getId();

        $nodeMap = [];
        foreach ($nodes as $node) {
            $nodeId = $node['id'];

            $alreadyExists = false;
            // Already saved node
            if (preg_match('/^node_(\d+)$/', $nodeId)) {
                $alreadyExists = true;
            }

            if (!$this->canProcessNode($node)) {
                continue;
            }

            $customerGroup = $this->getNodeCustomerGroup($node);
            $maxItemColumn = $this->getNodeMaxItemColumn($node);
            $nodeIdentifier = $this->getNodeIdentifier($node);

            if ($alreadyExists) {
                $nodeObject = $this->nodeRepository->getById(str_replace('node_', '', $nodeId));
                $nodeObject->setCustomerGroup($customerGroup);
                $nodeObject->setMaxItemColumn($maxItemColumn);
                $nodeObject->setIdentifier($nodeIdentifier);
                $this->nodeRepository->save($nodeObject);
                continue;
            }

            $nodeObject = $this->nodeFactory->create();
            $nodeObject->setMenuId($id);
            $nodeObject->setCustomerGroup($customerGroup);
            $nodeObject->setMaxItemColumn($maxItemColumn);
            $nodeObject->setIdentifier($nodeIdentifier);
            $storedNode = $this->nodeRepository->save($nodeObject);

            $nodeMap[$nodeId] = $storedNode->getId();
        }

        // Replace ids
        foreach ($nodes as $key => $node) {
            if (isset($nodeMap[$node['id']])) {
                $nodes[$key]['id'] = "node_{$nodeMap[$node['id']]}";
            }

            if (isset($nodeMap[$node['parent']])) {
                $nodes[$key]['parent'] = "node_{$nodeMap[$node['parent']]}";
            }
        }

        return json_encode($nodes);
    }

    /**
     * @param $node
     * @return void
     */
    public function canProcessNode($node): bool
    {
        if (isset($node['data']['customer_group']) && !empty($node['data']['customer_group'])) {
            return true;
        }

        if (isset($node['data']['max_item_column']) && !empty($node['data']['max_item_column'])) {
            return true;
        }

        if (isset($node['data']['node_identifier']) && !empty($node['data']['node_identifier'])) {
            return true;
        }

        return false;
    }

    /**
     * @param $node
     * @return mixed|string|null
     */
    public function getNodeCustomerGroup($node)
    {
        if (!isset($node['data']['customer_group']) || empty($node['data']['customer_group'])) {
            return null;
        }

        $customerGroup = $node['data']['customer_group'] ?? [];

        if (is_array($customerGroup)) {
            return implode(',', $node['data']['customer_group']);
        }

        return $customerGroup;
    }

    /**
     * @param $node
     * @return int|null
     */
    public function getNodeMaxItemColumn($node)
    {
        if (!isset($node['data']['max_item_column']) && empty($node['data']['max_item_column'])) {
            return null;
        }

        return $node['data']['max_item_column'];
    }

    /**
     * @param $node
     * @return void
     */
    public function getNodeIdentifier($node)
    {
        if (!isset($node['data']['node_identifier']) && empty($node['data']['node_identifier'])) {
            return null;
        }

        return $node['data']['node_identifier'];
    }
}
