<?php

declare(strict_types=1);

namespace Gls\GlsPoland\PrestaShop\MessageHandler\Tab;

use Doctrine\ORM\EntityRepository;
use Gls\GlsPoland\PrestaShop\Message\CreateAdminTabCommand;
use Gls\GlsPoland\PrestaShop\ObjectModel\Repository\TabRepository;
use PrestaShop\PrestaShop\Adapter\Module\Tab\ModuleTabRegister;
use PrestaShopBundle\Entity\Lang;
use PrestaShopBundle\Entity\Repository\LangRepository;

final class CreateAdminTabHandler implements CreateAdminTabHandlerInterface
{
    private $tabRepository;

    /**
     * @var EntityRepository<Lang>
     */
    private $languageRepository;

    private $languages;

    public function __construct(TabRepository $tabRepository, LangRepository $languageRepository)
    {
        $this->tabRepository = $tabRepository;
        $this->languageRepository = $languageRepository;
    }

    public function handle(CreateAdminTabCommand $command): \Tab
    {
        $className = $command->getClassName();
        if ($tab = $this->tabRepository->findOneByClassName($className)) {
            return $tab;
        }

        $tab = new \Tab();
        $tab->class_name = $className;
        $tab->active = $visible = $command->isVisible();
        $tab->module = $command->getModuleName();
        $tab->name = $this->getTabNames($command->getName());
        $tab->icon = $command->getIcon();
        $tab->id_parent = $this->getParentId($command->getParentClassName(), $visible);
        if (property_exists($tab, 'route_name')) {
            $tab->route_name = $command->getRouteName();
        }

        $this->tabRepository->add($tab);

        return $tab;
    }

    private function getTabNames($names): array
    {
        $translatedNames = [];
        foreach ($this->getLanguages() as $language) {
            $translatedNames[$language->getId()] = $this->getTabName($names, $language);
        }

        return $translatedNames;
    }

    /**
     * @return iterable<Lang>
     */
    private function getLanguages(): iterable
    {
        return $this->languages ?? ($this->languages = $this->languageRepository->findAll());
    }

    private function getTabName($names, Lang $language): ?string
    {
        if (!is_array($names)) {
            return $names;
        }

        if (array_key_exists($locale = $language->getLocale(), $names)) {
            return $names[$locale];
        }

        if (array_key_exists($languageCode = $language->getLanguageCode(), $names)) {
            return $names[$languageCode];
        }

        if (array_key_exists($isoCode = $language->getIsoCode(), $names)) {
            return $names[$isoCode];
        }

        return reset($names);
    }

    private function getParentId(?string $parentClassName, bool $visible): int
    {
        $parentTab = $this->getParentTab($parentClassName, $visible);

        if (null === $parentTab) {
            return 0;
        }

        if ($this->tabRepository->hasChildren((int) $parentTab->id)) {
            return (int) $parentTab->id;
        }

        return (int) $this->duplicateTab($parentTab)->id;
    }

    private function getParentTab(?string $parentClassName, bool $visible): ?\Tab
    {
        if (null !== $parentClassName) {
            $parentTab = $this->tabRepository->findOneByClassName($parentClassName . ModuleTabRegister::SUFFIX);
            if (null === $parentTab) {
                $parentTab = $this->tabRepository->findOneByClassName($parentClassName);
            }

            return $parentTab;
        }

        return $visible ? $this->tabRepository->findOneByClassName('DEFAULT') : null;
    }

    private function duplicateTab(\Tab $tab): \Tab
    {
        $newTab = clone $tab;
        $newTab->id = null;
        $newTab->id_parent = $tab->id_parent;
        $newTab->class_name = $tab->class_name . ModuleTabRegister::SUFFIX;
        $this->tabRepository->add($newTab);

        $newTab->position = $tab->position;
        $this->tabRepository->update($newTab);

        $tab->id_parent = $newTab->id;
        $this->tabRepository->update($tab);

        return $newTab;
    }
}
