<?php

use Gls\GlsPoland\Controller\Admin\ConfigurationController;
use Gls\GlsPoland\Controller\Admin\ConsignmentController;
use Gls\GlsPoland\PrestaShop\Checkout\DeliveryOptionAvailabilityCheckerInterface;
use Gls\GlsPoland\PrestaShop\Checkout\Exception\AbortCheckoutException;
use Gls\GlsPoland\PrestaShop\Hook\HookExecutorInterface;
use Gls\GlsPoland\PrestaShop\Installer\InstallerException;
use Gls\GlsPoland\PrestaShop\Installer\InstallerFactory;
use Gls\GlsPoland\PrestaShop\Installer\InstallerInterface;
use Gls\GlsPoland\PrestaShop\Installer\UninstallerInterface;
use Gls\GlsPoland\Translation\TranslationLoaderTrait;
use PrestaShop\PrestaShop\Adapter\ContainerBuilder as PrestaShopContainerBuilder;
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use PrestaShop\PrestaShop\Core\Exception\ContainerNotFoundException;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

if (!defined('_PS_VERSION_')) {
    exit;
}

require_once __DIR__ . '/src/Compat/php80.php';

if (file_exists(__DIR__ . '/vendor/autoload.php')) {
    require_once __DIR__ . '/vendor/autoload.php';
}

if (Tools::version_compare(_PS_VERSION_, '9.0.0', '>=')) {
    trait GlsPolandGetCompatTrait
    {
        /**
         * @param string $serviceName
         *
         * @return object|null
         */
        public function get(string $serviceName): ?object
        {
            return $this->doGetContainer()->get($serviceName);
        }
    }
} else {
    trait GlsPolandGetCompatTrait
    {
        /**
         * @param string $serviceName
         *
         * @return object|null
         */
        public function get($serviceName)
        {
            return $this->doGetContainer()->get($serviceName);
        }
    }
}

/**
 * @method void hookActionDispatcherBefore(array $parameters)
 */
class GlsPoland extends CarrierModule
{
    use TranslationLoaderTrait;
    use GlsPolandGetCompatTrait;
    private const FALLBACK_LOG_FILE = 'glspoland_fallback.log';

    /**
     * @var int|numeric-string identifier of the module's carrier for which PrestaShop is currently performing shipping cost calculation
     *
     * @see \Cart::getPackageShippingCostFromModule
     */
    public $id_carrier;

    /**
     * @var RequestStack
     */
    private $requestStack;
    /**
     * @var bool
     */
    private $checkoutHooksEnsured = false;
    /**
     * @var bool
     */
    private $adminHooksEnsured = false;

    public function __construct()
    {
        $this->name = 'glspoland';
        $this->version = '1.2.0';
        $this->author = 'GLS Poland Sp. z o.o.';
        $this->tab = 'shipping_logistics';
        $this->need_instance = 0;
        $this->ps_versions_compliancy = ['min' => '1.7.8.0', 'max' => '1.7.8.99'];

        parent::__construct();

        $this->initTranslations();
        $this->displayName = $this->trans('GLS Poland', [], 'Modules.Glspoland.Admin');
        $this->description = $this->trans('Official GLS Poland integration module for PrestaShop', [], 'Modules.Glspoland.Admin');

        if (!extension_loaded('soap')) {
            $this->warning = $this->trans('Communication with the API requires the PHP SOAP extension. Please enable it on your server.', [], 'Modules.Glspoland.Installer');
        }
    }

    /**
     * @return true
     */
    public function isUsingNewTranslationSystem()
    {
        return true;
    }

    /**
     * @return bool
     */
    public function install()
    {
        if (70200 > PHP_VERSION_ID) {
            $this->_errors[] = $this->trans('This module requires PHP 7.2 or later.', [], 'Modules.Glspoland.Installer');

            return false;
        }

        $this->initializeDoctrineMappings();
        $this->setUpRoutingLoaderResolver();

        if (!parent::install()) {
            return false;
        }

        $this->clearStaticCircularReferences();

        try {
            $this->getInstaller()->install($this);
        } catch (InstallerException $exception) {
            try {
                parent::uninstall();
            } catch (\Throwable $e) {
                $this->logWithFallback('error', 'Could not rollback module install after installer failure.', [
                    'error' => $e,
                ]);
            }

            $this->_errors[] = $exception->getMessage();

            return false;
        }

        if (!$this->ensureModuleIsEnabledAfterInstall()) {
            $this->_errors[] = $this->trans('Module has been installed, but could not be enabled automatically.', [], 'Modules.Glspoland.Installer');

            return false;
        }

        if (!$this->ensureModuleCarriersExistAfterInstall()) {
            $this->_errors[] = $this->trans('Module has been installed, but default carriers could not be created automatically.', [], 'Modules.Glspoland.Installer');

            return false;
        }

        return true;
    }

    /**
     * @return bool
     */
    public function uninstall()
    {
        $this->initializeDoctrineMappings();
        $this->setUpRoutingLoaderResolver();

        try {
            $this->getUninstaller()->uninstall($this, $this->isResetModuleAction());
        } catch (InstallerException $exception) {
            $this->_errors[] = $exception->getMessage();

            return false;
        }

        return parent::uninstall();
    }

    /**
     * @param bool $force_all
     *
     * @return bool
     */
    public function disable($force_all = false)
    {
        try {
            $this->deactivateModuleCarriers();
        } catch (Throwable $e) {
            $this->logWithFallback('error', 'Could not deactivate GLS carriers while disabling the module.', [
                'error' => $e,
            ]);
        }

        try {
            $result = parent::disable($force_all);
        } catch (Throwable $e) {
            $this->logWithFallback('critical', 'Fatal error while disabling module.', [
                'error' => $e,
                'force_all' => $force_all,
            ]);
            $this->_errors[] = $this->trans('Could not disable GLS Poland module. Check module fallback log for details.', [], 'Modules.Glspoland.Admin');

            return false;
        }

        if (!$result) {
            $this->logWithFallback('error', 'Module disable returned false.');
        }

        return $result;
    }

    /**
     * @return array
     */
    public function runUpgradeModule()
    {
        try {
            $result = parent::runUpgradeModule();

            if (!$result['available_upgrade']) {
                return $result;
            }

            if ($result['success']) {
                $this->getLogger()->info('Upgraded module to version {version}.', [
                    'version' => $result['upgraded_to'],
                ]);
            } else {
                $this->getLogger()->error('Could not upgrade module.', [
                    'details' => $result,
                ]);
            }

            return $result;
        } catch (Throwable $e) {
            $this->logWithFallback('critical', 'Upgrade error: {exception}.', [
                'exception' => $e,
            ]);

            if ($e instanceof InstallerException) {
                $this->_errors[] = $e->getMessage();
            }

            throw $e;
        }
    }

    /**
     * @return never-returns
     */
    public function getContent()
    {
        try {
            /** @var UrlGeneratorInterface $router */
            $router = $this->get('router');

            Tools::redirectAdmin($router->generate('admin_gls_poland_config_api'));
        } catch (RouteNotFoundException $e) {
            if (!$this->active && Tools::version_compare(_PS_VERSION_, '8.0.0', '>=') && !$this->hasShopAssociations()) {
                /** @var Session $session */
                $session = $this->get('session');
                $session->getFlashBag()->add('error', $this->trans('To access the configuration page, the module must be enabled.', [], 'Modules.Glspoland.Admin'));

                Tools::redirectAdmin($router->generate('admin_module_manage'));
            }

            if (Tools::getValue('cache_cleared')) {
                $this->getLogger()->error('GLS configuration route is unavailable after cache clear.', [
                    'error' => $e,
                ]);

                Tools::redirectAdmin($this->context->link->getAdminLink('AdminModules'));
            }

            Tools::clearSf2Cache();
            Tools::redirectAdmin($this->context->link->getAdminLink('AdminModules', true, null, [
                'configure' => $this->name,
                'cache_cleared' => true,
            ]));
        }
    }

    /**
     * @param Cart $params
     * @param float $shipping_cost
     *
     * @return float|false
     */
    public function getOrderShippingCost($params, $shipping_cost)
    {
        if (!$params instanceof Cart) {
            throw new InvalidArgumentException(sprintf('Argument 1 passed to %s() must be an instance of "%s", "%s" given.', __METHOD__, Cart::class, get_debug_type($params)));
        }

        if (!$this->active || 0 >= $carrierId = (int) $this->id_carrier) {
            return false;
        }

        if (!$this->get(DeliveryOptionAvailabilityCheckerInterface::class)->isAvailable($carrierId, $params)) {
            return false;
        }

        return (float) $shipping_cost;
    }

    /**
     * @param Cart $params
     *
     * @return false
     */
    public function getOrderShippingCostExternal($params)
    {
        if (!$params instanceof Cart) {
            throw new InvalidArgumentException(sprintf('Argument 1 passed to %s() must be an instance of "%s", "%s" given.', __METHOD__, Cart::class, get_debug_type($params)));
        }

        return false;
    }

    /**
     * @param string $methodName
     */
    public function __call($methodName, array $arguments)
    {
        $hookName = str_starts_with($methodName, 'hook')
            ? lcfirst(Tools::substr($methodName, 4))
            : $methodName;

        if ('actionDispatcherBefore' === $hookName) {
            $this->ensureFrontCheckoutHooksRegistered();
            $this->ensureAdminOrderHooksRegistered();
        }

        $parameters = isset($arguments[0]) ? $arguments[0] : [];
        if (!isset($parameters['request'])) {
            $parameters['request'] = $this->getCurrentRequest();
        }

        try {
            return $this
                ->get(HookExecutorInterface::class)
                ->execute($hookName, $parameters);
        } catch (ServiceNotFoundException $e) {
            $this->getLogger()->error('Hook executor service is not available for hook "{hookName}".', [
                'hookName' => $hookName,
                'error' => $e,
            ]);

            return null;
        } catch (AbortCheckoutException $e) {
            // might be a bit brutal, but some checkout modules do not give any better options
            $this->terminate($e->getResponse());
        } catch (Throwable $e) {
            $this->logWithFallback('critical', 'Error executing hook "{hookName}"', [
                'hookName' => $hookName,
                'error' => $e,
            ]);

            if (!defined('_PS_MODE_DEV_') || _PS_MODE_DEV_) {
                throw $e;
            }

            return null;
        }
    }

    /**
     * @return array
     */
    public function getTabs()
    {
        if ([] !== $this->tabs) {
            return $this->tabs;
        }

        return $this->tabs = [
            [
                'class_name' => ConfigurationController::TAB_NAME,
                'route_name' => 'admin_gls_poland_config_api',
                'visible' => false,
                'name' => [
                    'en' => 'GLS Poland',
                    'pl' => 'GLS Polska',
                ],
            ],
            [
                'class_name' => ConsignmentController::TAB_NAME,
                'parent_class_name' => ConsignmentController::PARENT_TAB_NAME,
                'route_name' => 'admin_gls_poland_consignments_index',
                'name' => [
                    'en' => 'GLS consignments',
                    'pl' => 'Przesylki GLS',
                ],
            ],
        ];
    }

    /**
     * @return Request
     *
     * @internal
     */
    public function getCurrentRequest()
    {
        return $this->getRequestStack()->getCurrentRequest() ?: Request::createFromGlobals();
    }

    /**
     * @return RequestStack
     *
     * @internal
     */
    public function getRequestStack()
    {
        if (isset($this->requestStack)) {
            return $this->requestStack;
        }

        try {
            /** @var RequestStack $requestStack */
            $requestStack = $this->get('request_stack');
            if (null !== $requestStack->getCurrentRequest()) {
                return $this->requestStack = $requestStack;
            }
        } catch (ServiceNotFoundException $e) {
        }

        $this->requestStack = new RequestStack();
        $this->requestStack->push(Request::createFromGlobals());

        return $this->requestStack;
    }

    /**
     * @internal
     *
     * @return LoggerInterface
     */
    public function getLogger()
    {
        try {
            return $this->get('gls_poland.general_logger');
        } catch (Exception $e) {
            return new NullLogger();
        }
    }

    /**
     * @return InstallerInterface
     */
    private function getInstaller()
    {
        if (null !== $installer = $this->createInstaller()) {
            return $installer;
        }

        return $this->get(InstallerInterface::class);
    }

    /**
     * @return UninstallerInterface
     */
    private function getUninstaller()
    {
        if (null !== $installer = $this->createInstaller()) {
            return $installer;
        }

        return $this->get(UninstallerInterface::class);
    }

    /**
     * @return (InstallerInterface&UninstallerInterface)|null
     */
    private function createInstaller()
    {
        $container = SymfonyContainer::getInstance();
        if (null === $container) {
            try {
                $container = $this->doGetContainer();
            } catch (\Throwable $e) {
                return null;
            }
        }

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

        try {
            $installer = (new InstallerFactory($this, $container))->createInstaller();
        } catch (\Throwable $e) {
            return null;
        }

        if (method_exists($container, 'set')) {
            $container->set(InstallerInterface::class, $installer);
            $container->set(UninstallerInterface::class, $installer);
        }

        return $installer;
    }

    private function initTranslations()
    {
        if (null !== $this->id) {
            return;
        }

        if (Tools::version_compare(_PS_VERSION_, '1.7.6')) {
            return;
        }

        if (null === $translator = $this->getTranslator()) {
            return;
        }

        try {
            $catalogue = $this->loadTranslations($this->getLocalPath() . 'translations/', $translator->getLocale());
        } catch (Exception $e) {
            return;
        }

        if (null !== $catalogue) {
            $translator->getCatalogue()->addCatalogue($catalogue);
        }
    }

    /**
     * Accesses the public "routing.loader" service in order to provide a @see \Symfony\Component\Config\Loader\LoaderResolverInterface
     * to the routing configuration loader used by @see \PrestaShop\PrestaShop\Adapter\Module\Tab\ModuleTabRegister
     */
    /**
     * Ensures Doctrine entity mapping for module entities points to the current module path
     * before installer services are instantiated.
     */
    private function initializeDoctrineMappings()
    {
        try {
            $container = $this->doGetContainer();
            if (!$container->has('doctrine')) {
                return;
            }

            $doctrine = $container->get('doctrine');
            $entityManager = $doctrine->getManager();
            $moduleDriver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver(
                new \Doctrine\Common\Annotations\AnnotationReader(),
                [$this->getLocalPath() . 'src/Entity/']
            );

            $namespace = 'Gls\\GlsPoland\\Entity';
            $mappingDriver = $entityManager->getConfiguration()->getMetadataDriverImpl();

            if (method_exists($mappingDriver, 'addDriver')) {
                $mappingDriver->addDriver($moduleDriver, $namespace);
            } else {
                $chainClass = class_exists(\Doctrine\Persistence\Mapping\Driver\MappingDriverChain::class)
                    ? \Doctrine\Persistence\Mapping\Driver\MappingDriverChain::class
                    : \Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain::class;

                $chain = new $chainClass();
                if (method_exists($chain, 'setDefaultDriver') && null !== $mappingDriver) {
                    $chain->setDefaultDriver($mappingDriver);
                }

                $chain->addDriver($moduleDriver, $namespace);
                $entityManager->getConfiguration()->setMetadataDriverImpl($chain);
            }

            // Avoid resetManager(): in upload/install flow it may recreate EntityManager
            // without runtime-added module mapping.
            $entityManager->clear();
        } catch (\Throwable $e) {
            // ignore silently: installer will still report detailed failure if mapping is broken
        }
    }
    private function setUpRoutingLoaderResolver()
    {
        if (Tools::version_compare(_PS_VERSION_, '1.7.7')) {
            return;
        }

        try {
            $this->get('routing.loader');
        } catch (Exception $e) {
            // ignore silently
        }
    }

    private function ensureModuleIsEnabledAfterInstall(): bool
    {
        if (method_exists($this, 'associateTo') && class_exists('Shop')) {
            $shopIds = \Shop::getShops(true, null, true);
            if (is_array($shopIds) && [] !== $shopIds) {
                $this->associateTo(array_map('intval', $shopIds));
            }
        }

        if ($this->active) {
            return true;
        }

        if ($this->enable(true)) {
            return true;
        }

        $updated = Db::getInstance()->update('module', ['active' => 1], 'id_module = ' . (int) $this->id);
        if (false === $updated) {
            return false;
        }

        $this->active = true;

        return true;
    }

    private function ensureModuleCarriersExistAfterInstall(): bool
    {
        if ($this->hasInstalledModuleCarriers()) {
            return true;
        }

        try {
            // Retry full installer once after module is active/associated with shops.
            $this->getInstaller()->install($this);
        } catch (\Throwable $e) {
            $this->logWithFallback('error', 'Carrier creation retry after install failed.', [
                'error' => $e,
            ]);
        }

        return $this->hasInstalledModuleCarriers();
    }

    private function hasInstalledModuleCarriers(): bool
    {
        $count = Db::getInstance()->getValue(sprintf(
            'SELECT COUNT(*) FROM `%scarrier` WHERE external_module_name = "%s" AND deleted = 0',
            _DB_PREFIX_,
            pSQL($this->name)
        ));

        return (int) $count > 0;
    }

    private function ensureFrontCheckoutHooksRegistered(): void
    {
        if ($this->checkoutHooksEnsured || !$this->active) {
            return;
        }

        $this->checkoutHooksEnsured = true;
        $requiredHooks = [
            'displayCarrierExtraContent',
            'displayAfterCarrier',
            'displayBeforeBodyClosingTag',
            'actionCarrierProcess',
            'actionValidateStepComplete',
        ];

        foreach ($requiredHooks as $hookName) {
            try {
                $isRegistered = method_exists($this, 'isRegisteredInHook')
                    ? (bool) $this->isRegisteredInHook($hookName)
                    : false;

                if (!$isRegistered) {
                    $this->registerHook($hookName);
                }
            } catch (\Throwable $e) {
                $this->logWithFallback('warning', 'Could not ensure checkout hook registration.', [
                    'hook' => $hookName,
                    'error' => $e,
                ]);
            }
        }
    }

    private function ensureAdminOrderHooksRegistered(): void
    {
        if ($this->adminHooksEnsured || !$this->active) {
            return;
        }

        $this->adminHooksEnsured = true;
        $requiredHooks = [
            'displayAdminOrderMain',
            'displayAdminOrderTabLink',
            'displayAdminOrderTabContent',
            'displayAdminOrderContentShip',
            'displayAdminOrderTabShip',
            'displayAdminOrdersListBefore',
            'actionOrderGridDefinitionModifier',
        ];

        foreach ($requiredHooks as $hookName) {
            try {
                $isRegistered = method_exists($this, 'isRegisteredInHook')
                    ? (bool) $this->isRegisteredInHook($hookName)
                    : false;

                if (!$isRegistered) {
                    $this->registerHook($hookName);
                }
            } catch (\Throwable $e) {
                $this->logWithFallback('warning', 'Could not ensure admin hook registration.', [
                    'hook' => $hookName,
                    'error' => $e,
                ]);
            }
        }
    }

    private function deactivateModuleCarriers()
    {
        $db = Db::getInstance();
        $moduleName = pSQL($this->name);
        $prefix = _DB_PREFIX_;

        $carrierIds = $db->executeS(sprintf(
            'SELECT id_carrier FROM `%scarrier` WHERE external_module_name = "%s"',
            $prefix,
            $moduleName
        ));

        if (!is_array($carrierIds) || [] === $carrierIds) {
            return;
        }

        $ids = array_map(static function (array $row): int {
            return (int) ($row['id_carrier'] ?? 0);
        }, $carrierIds);
        $ids = array_filter($ids);

        if ([] === $ids) {
            return;
        }

        $idList = implode(',', $ids);

        $result = $db->execute(sprintf(
            'UPDATE `%scarrier` SET active = 0 WHERE id_carrier IN (%s)',
            $prefix,
            $idList
        ));
        if (false === $result) {
            throw new \RuntimeException('Failed to deactivate GLS carriers.');
        }

        $result = $db->execute(sprintf(
            'UPDATE `%scart` SET id_carrier = 0, delivery_option = "{}" WHERE id_carrier IN (%s)',
            $prefix,
            $idList
        ));
        if (false === $result) {
            throw new \RuntimeException('Failed to clear GLS delivery options from carts.');
        }
    }

    private function logWithFallback(string $level, string $message, array $context = []): void
    {
        try {
            $logger = $this->getLogger();
            if (method_exists($logger, $level)) {
                $logger->$level($message, $context);
            } else {
                $logger->error($message, $context);
            }
        } catch (Throwable $e) {
            $context['logger_error'] = $e->getMessage();
        }

        $this->writeFallbackLog($level, $message, $context);
    }

    private function writeFallbackLog(string $level, string $message, array $context = []): void
    {
        $baseDir = defined('_PS_CACHE_DIR_') ? rtrim(_PS_CACHE_DIR_, '/\\') : rtrim(_PS_ROOT_DIR_, '/\\') . DIRECTORY_SEPARATOR . 'var';
        $logDir = $baseDir . DIRECTORY_SEPARATOR . 'gls_poland_logs';
        if (!is_dir($logDir)) {
            @mkdir($logDir, 0775, true);
        }

        $normalizedContext = array_map(static function ($value) {
            if ($value instanceof Throwable) {
                return [
                    'class' => get_class($value),
                    'message' => $value->getMessage(),
                    'file' => $value->getFile(),
                    'line' => $value->getLine(),
                    'trace' => $value->getTraceAsString(),
                ];
            }

            return $value;
        }, $context);

        $line = sprintf(
            "[%s] %s %s %s\n",
            date('Y-m-d H:i:s'),
            strtoupper($level),
            $message,
            json_encode($normalizedContext, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
        );

        @file_put_contents($logDir . DIRECTORY_SEPARATOR . self::FALLBACK_LOG_FILE, $line, FILE_APPEND);
    }

    /**
     * @return bool
     */
    private function isResetModuleAction()
    {
        $request = $this->getCurrentRequest();

        return 'admin_module_manage_action' === $request->attributes->get('_route')
            && 'reset' === $request->attributes->get('action');
    }

    /**
     * Dumping container metadata on PHP versions before 8.1 might cause the script to exceed memory limit
     * in @see \Symfony\Component\Config\Resource\ReflectionClassResource::generateSignature()
     * due to https://bugs.php.net/bug.php?id=80821 if @see \Module::$_INSTANCE is not empty.
     */
    private function clearStaticCircularReferences()
    {
        if (80100 <= PHP_VERSION_ID || Tools::version_compare(_PS_VERSION_, '1.7.8')) {
            return;
        }

        try {
            /** @var EventDispatcher $eventDispatcher */
            $eventDispatcher = $this->get('event_dispatcher');
            $eventDispatcher->addListener(KernelEvents::TERMINATE, static function () {
                if (is_callable([Module::class, 'resetStaticCache'])) {
                    Module::resetStaticCache();
                } else {
                    Module::$_INSTANCE = [];
                }
            }, -10000);
        } catch (Exception $e) {
            // ignore silently
        }
    }

    /**
     * @param Response $response
     *
     * @return never-returns
     */
    private function terminate($response)
    {
        $this->context->cookie->write();
        $response->send();

        exit;
    }

    /**
     * @return ContainerInterface
     */
    private function doGetContainer()
    {
        if (Tools::version_compare(_PS_VERSION_, '1.7.8') && null !== $container = SymfonyContainer::getInstance()) {
            return $container;
        }

        if (Tools::version_compare(_PS_VERSION_, '1.7.7')) {
            return $this->getPS176LegacyContainer();
        }

        try {
            return $this->getContainer();
        } catch (ContainerNotFoundException $e) {
            return $this->getFrontOfficeLegacyContainer();
        }
    }

    /**
     * {@see \Module::get()} does not check if {@see \Controller::$container} is set on PS 1.7.6.
     *
     * @return ContainerInterface
     */
    private function getPS176LegacyContainer()
    {
        if (isset($this->context->container)) {
            return $this->context->container;
        }

        if ($this->context->controller instanceof Controller && null !== $container = $this->context->controller->getContainer()) {
            return $container;
        }

        return $this->getFrontOfficeLegacyContainer();
    }

    /**
     * Access container before {@see \FrontController::$container} is set in {@see \Controller::init()}.
     *
     * @return ContainerInterface
     */
    private function getFrontOfficeLegacyContainer()
    {
        if (!class_exists(PrestaShopContainerBuilder::class)) {
            throw new \RuntimeException('DI container was not found.');
        }

        try {
            return PrestaShopContainerBuilder::getContainer('front', _PS_MODE_DEV_);
        } catch (Exception $e) {
            throw new \RuntimeException('DI container was not found.', 0, $e);
        }
    }
}

