<?php
/**
 * For the full copyright and license information, please view the
 * docs/licenses/LICENSE.txt file that was distributed with this source code.
 */

declare(strict_types=1);

namespace PrestaShopBundle\Twig\Layout;

use PrestaShop\PrestaShop\Adapter\LegacyContext;
use PrestaShopBundle\Entity\Repository\TabRepository;
use PrestaShopBundle\Entity\Tab;
use PrestaShopBundle\Routing\Converter\LegacyParametersConverter;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Tab as LegacyTab;

class MenuBuilder
{
    private ?Tab $currentTab = null;
    /** @var array<int, Tab[]> */
    private array $ancestorsTab = [];

    public function __construct(
        private readonly LegacyContext $context,
        private readonly RequestStack $requestStack,
        private readonly TabRepository $tabRepository,
        private readonly TranslatorInterface $translator,
        private readonly UrlGeneratorInterface $urlGenerator,
        private readonly LegacyParametersConverter $legacyParametersConverter
    ) {
    }

    public function getCurrentTab(): ?Tab
    {
        if ($this->currentTab) {
            return $this->currentTab;
        }

        $tab = null;
        $routeName = $this->getRouteName();
        if (!empty($routeName)) {
            $tab = $this->tabRepository->findOneByRouteName($routeName);
        }

        if (!$tab) {
            $className = $this->getLegacyControllerClassName();
            if (!empty($className)) {
                $tab = $this->tabRepository->findOneByClassName($className);
            }
        }

        $this->currentTab = $tab;

        return $tab;
    }

    public function getCurrentTabLevel(): int
    {
        $currentTab = $this->getCurrentTab();
        if ($currentTab) {
            $ancestorsTab = $this->getAncestorsTab($currentTab->getId());
            if (!empty($ancestorsTab)) {
                return count($ancestorsTab);
            }
        }

        return 0;
    }

    /* @return Tab[] */
    public function getAncestorsTab(int $currentTabId): array
    {
        if (isset($this->ancestorsTab[$currentTabId])) {
            return $this->ancestorsTab[$currentTabId];
        }
        $this->ancestorsTab[$currentTabId] = $this->tabRepository->getAncestors($currentTabId);

        return $this->ancestorsTab[$currentTabId];
    }

    /**
     * @return array<string, MenuLink>
     */
    public function getBreadcrumbLinks(): array
    {
        $currentTab = $this->getCurrentTab();
        if (null === $currentTab) {
            return [];
        }

        $tabAncestors = $this->getAncestorsTab($currentTab->getId());

        return $this->convertTabsToBreadcrumbLinks($currentTab, $tabAncestors);
    }

    /**
     * @return array<string, MenuLink>
     */
    public function convertTabsToBreadcrumbLinks(Tab $currentTab, array $tabAncestors): array
    {
        $breadcrumbLinks = [
            'tab' => $this->convertTabToMenuLink($currentTab),
        ];

        if (!empty($tabAncestors)) {
            $breadcrumbLinks['container'] = $this->convertTabToMenuLink(reset($tabAncestors));
        }

        $actionLink = $this->getActionLink();
        if (null !== $actionLink) {
            $breadcrumbLinks['action'] = $actionLink;
        }

        return $breadcrumbLinks;
    }

    private function convertTabToMenuLink(Tab $tab): MenuLink
    {
        return new MenuLink(
            name: $this->getBreadcrumbLabel($tab),
            href: $this->getLinkFromTab($tab),
            icon: $tab->getIcon() ?? '',
        );
    }

    /**
     * @return array<int, MenuLink>
     */
    public function buildNavigationTabs(Tab $tab): array
    {
        // Get siblings tabs, filter them based on the employee authorizations
        $currentLevelTabs = array_filter($this->tabRepository->findByParentId($tab->getIdParent()), function (Tab $tab) {
            return LegacyTab::checkTabRights($tab->getId())
                && $tab->isEnabled()
                && $tab->getClassName() !== 'AdminCarrierWizard';
        });

        $navigationTabs = [];

        /* @var $currentLevelTab Tab */
        foreach ($currentLevelTabs as $currentLevelTab) {
            $tabLang = $currentLevelTab->getTabLangByLanguageId($this->getContextLanguageId());
            $menuLink = new MenuLink(
                name: $tabLang ? $tabLang->getName() : $currentLevelTab->getWording(),
                href: $this->getLinkFromTab($currentLevelTab),
                attributes: [
                    'id_tab' => $currentLevelTab->getId(),
                    'class_name' => $currentLevelTab->getClassName(),
                    'current' => $currentLevelTab->getId() == $tab->getId(),
                    'active' => $currentLevelTab->getActive(),
                ]
            );
            $navigationTabs[] = $menuLink;
        }

        return $navigationTabs;
    }

    private function getBreadcrumbLabel(Tab $tab): string
    {
        if (null !== $tab->getWording() && null !== $tab->getWordingDomain()) {
            return $this->translator->trans($tab->getWording(), [], $tab->getWordingDomain());
        }

        $tabLang = $tab->getTabLangByLanguageId($this->getContextLanguageId());
        if (null !== $tabLang) {
            return $tabLang->getName();
        }

        if (!$tab->getTabLangs()->isEmpty()) {
            return $tab->getTabLangs()->first()->getName();
        }

        return $tab->getClassName();
    }

    private function getContextLanguageId(): int
    {
        return $this->context->getLanguage()->getId();
    }

    private function getActionLink(): ?MenuLink
    {
        $action = $this->getLegacyAction();

        switch (true) {
            // In legacy no action is always equivalent to list action, but maybe we should change this into null and consider no action can be identified
            case null === $action:
            case $action === '':
            case str_starts_with($action, 'list'):
                return new MenuLink(
                    name: $this->translator->trans('List', [], 'Admin.Actions'),
                    icon: 'icon-th-list'
                );

            case str_starts_with($action, 'add'):
            case str_starts_with($action, 'new'):
                return new MenuLink(
                    name: $this->translator->trans('Add', [], 'Admin.Actions'),
                    icon: 'icon-plus'
                );
            case str_starts_with($action, 'edit'):
            case str_starts_with($action, 'update'):
                return new MenuLink(
                    name: $this->translator->trans('Edit', [], 'Admin.Actions'),
                    icon: 'icon-pencil'
                );

            case str_starts_with($action, 'details'):
            case str_starts_with($action, 'view'):
                return new MenuLink(
                    name: $this->translator->trans('View details', [], 'Admin.Actions'),
                    icon: 'icon-zoom-in'
                );
            case str_starts_with($action, 'options'):
                return new MenuLink(
                    name: $this->translator->trans('Options', [], 'Admin.Actions'),
                    icon: 'icon-cogs'
                );
            case str_starts_with($action, 'generator'):
                return new MenuLink(
                    name: $this->translator->trans('Generator', [], 'Admin.Actions'),
                    icon: 'icon-flask'
                );
            default:
                return null;
        }
    }

    private function getLegacyAction(): ?string
    {
        // Get action from legacy parameters set on the route when we are in a Symfony page
        $legacyParameters = $this->legacyParametersConverter->getParameters(
            $this->requestStack->getMainRequest()->attributes->all(),
            $this->requestStack->getMainRequest()->query->all()
        );

        return $legacyParameters['action'] ?? $this->getLegacyActionFromQuery();
    }

    private function getLegacyActionFromQuery(): ?string
    {
        $request = $this->requestStack->getMainRequest();
        $controllerTable = $this->context->getContext()->controller->table;
        $objectIdentifier = 'id_' . $controllerTable;
        $objectId = $request->query->get($objectIdentifier);

        if ($request->query->has('deleteImage')) {
            return 'delete_image';
        } elseif ($request->query->has('delete' . $controllerTable)) {
            return 'delete';
        } elseif ($request->query->has('status' . $controllerTable) || $request->query->has('status')) {
            return 'status';
        } elseif ($request->query->has('position')) {
            return 'position';
        } elseif ($request->query->has('add' . $controllerTable)) {
            return 'new';
        } elseif ($request->query->has('update' . $controllerTable) && $objectId) {
            return 'edit';
        } elseif ($request->query->has('view' . $controllerTable)) {
            return 'view';
        } elseif ($request->query->has('details' . $controllerTable)) {
            return 'details';
        } elseif ($request->query->has('export' . $controllerTable)) {
            return 'export';
        } elseif ($request->query->has('action') && !empty($request->query->get('action'))) {
            return $request->query->get('action');
        }

        return null;
    }

    public function getLegacyControllerClassName(): ?string
    {
        $request = $this->requestStack->getMainRequest();
        if ($request->attributes->has('_legacy_controller')) {
            return $request->attributes->get('_legacy_controller');
        } elseif ($request->query->has('controller')) {
            return $request->query->get('controller');
        }

        return null;
    }

    private function getRouteName(): ?string
    {
        $request = $this->requestStack->getMainRequest();
        if ($request->attributes->has('_route')) {
            return $request->attributes->get('_route');
        }

        return null;
    }

    private function getLinkFromTab(Tab $tab): string
    {
        if (!empty($tab->getRouteName())) {
            $href = $this->urlGenerator->generate($tab->getRouteName());
        } else {
            $href = $this->context->getAdminLink($tab->getClassName());
        }

        return $href;
    }
}
