<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

namespace InPost\International\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\GlobFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;

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

final class ContainerFactory
{
    private const REQUIRED_PARAMS = [
        'database_prefix',
        'database_engine',
        'secret',
        'kernel.project_dir',
        'kernel.environment',
        'kernel.debug',
        'kernel.cache_dir',
        'kernel.logs_dir',
    ];

    /**
     * @var Container
     */
    private $container;

    /**
     * @param Container $container
     */
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function buildContainer(iterable $resources): ContainerInterface
    {
        $parameters = $this->getParameters();
        $parameterBag = new EnvPlaceholderParameterBag($parameters);
        $container = new ContainerBuilder($parameterBag);

        $loader = $this->getContainerLoader($container);

        foreach ($resources as $resource) {
            $loader->load($resource);
        }

        $container->register('app_container', ContainerInterface::class)
            ->setSynthetic(true)
            ->setPublic(true);

        $container->compile(true);
        $container->set('app_container', $this->container);

        return $container;
    }

    private function getContainerLoader(ContainerBuilder $container): LoaderInterface
    {
        $locator = new FileLocator();
        $resolver = new LoaderResolver([
            new XmlFileLoader($container, $locator),
            new YamlFileLoader($container, $locator),
            new PhpFileLoader($container, $locator),
            new GlobFileLoader($container, $locator),
            new DirectoryLoader($container, $locator),
            new ClosureLoader($container),
        ]);

        return new DelegatingLoader($resolver);
    }

    /**
     * The `getParameterBag()` method may fail if parameters depend on env variables that are not set. Some modules
     * load default values from .env files, but in some cases the related code may not have been executed yet
     * (e.g. in earlier versions of "ps_edition_basic", the defaults are loaded before module class declaration).
     *
     * @return array<string, mixed>
     */
    private function getParameters(): array
    {
        try {
            return $this->container->getParameterBag()->all();
        } catch (\Exception $e) {
            return array_map(function (string $name) {
                return $this->container->getParameter($name);
            }, array_combine(self::REQUIRED_PARAMS, self::REQUIRED_PARAMS));
        }
    }
}
