<?php

/**
 * Matomo - free/libre analytics platform
 *
 * @link    https://matomo.org
 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */
namespace Piwik\Container;

use Matomo\Dependencies\DI\ContainerBuilder;
use Piwik\Application\Kernel\GlobalSettingsProvider;
use Piwik\Application\Kernel\PluginList;
use Piwik\Plugin\Manager;
/**
 * Creates a configured DI container.
 */
class ContainerFactory
{
    /**
     * @var PluginList
     */
    private $pluginList;
    /**
     * @var GlobalSettingsProvider
     */
    private $settings;
    /**
     * Optional environment configs to load.
     *
     * @var string[]
     */
    private $environments;
    /**
     * @var array[]
     */
    private $definitions;
    /**
     * @param string[] $environments Optional environment configs to load.
     * @param array[] $definitions
     */
    public function __construct(PluginList $pluginList, GlobalSettingsProvider $settings, array $environments = array(), array $definitions = array())
    {
        $this->pluginList = $pluginList;
        $this->settings = $settings;
        $this->environments = $environments;
        $this->definitions = $definitions;
    }
    /**
     * @link https://php-di.org/doc/container-configuration.html
     * @throws \Exception
     */
    public function create() : \Piwik\Container\Container
    {
        $builder = new ContainerBuilder(\Piwik\Container\Container::class);
        $builder->useAnnotations(\false);
        // INI config
        $builder->addDefinitions(new \Piwik\Container\IniConfigDefinitionSource($this->settings));
        // Global config
        $builder->addDefinitions(PIWIK_DOCUMENT_ROOT . '/config/global.php');
        // Plugin configs
        $this->addPluginConfigs($builder);
        // Development config
        if ($this->isDevelopmentModeEnabled()) {
            $this->addEnvironmentConfig($builder, 'dev');
        }
        // Environment config
        foreach ($this->environments as $environment) {
            $this->addEnvironmentConfig($builder, $environment);
        }
        // User config
        if (file_exists(PIWIK_USER_PATH . '/config/config.php') && !in_array('test', $this->environments, \true)) {
            $builder->addDefinitions(PIWIK_USER_PATH . '/config/config.php');
        }
        if (!empty($this->definitions)) {
            foreach ($this->definitions as $definitionArray) {
                $builder->addDefinitions($definitionArray);
            }
        }
        /** @var Container $container */
        $container = $builder->build();
        $container->set('Piwik\\Application\\Kernel\\PluginList', $this->pluginList);
        $container->set('Piwik\\Application\\Kernel\\GlobalSettingsProvider', $this->settings);
        return $container;
    }
    private function addEnvironmentConfig(ContainerBuilder $builder, $environment)
    {
        if (!$environment) {
            return;
        }
        $file = sprintf('%s/config/environment/%s.php', PIWIK_USER_PATH, $environment);
        if (file_exists($file)) {
            $builder->addDefinitions($file);
        }
        $file = sprintf('%s/config/environment/%s-%s.php', PIWIK_USER_PATH, $environment, strtolower($this->getDatabaseSchema()));
        if (file_exists($file)) {
            $builder->addDefinitions($file);
        }
        // add plugin environment configs
        $plugins = $this->pluginList->getActivatedPlugins();
        $plugins = array_unique(array_merge($plugins, Manager::getAlwaysActivatedPlugins()));
        if ($this->shouldSortPlugins()) {
            $plugins = $this->sortPlugins($plugins);
        }
        foreach ($plugins as $plugin) {
            $baseDir = Manager::getPluginDirectory($plugin);
            $environmentFile = $baseDir . '/config/' . $environment . '.php';
            if (file_exists($environmentFile)) {
                $builder->addDefinitions($environmentFile);
            }
        }
    }
    private function addPluginConfigs(ContainerBuilder $builder)
    {
        $plugins = $this->pluginList->getActivatedPlugins();
        $plugins = array_unique(array_merge($plugins, Manager::getAlwaysActivatedPlugins()));
        if ($this->shouldSortPlugins()) {
            $plugins = $this->sortPlugins($plugins);
        }
        foreach ($plugins as $plugin) {
            $baseDir = Manager::getPluginDirectory($plugin);
            $file = $baseDir . '/config/config.php';
            if (file_exists($file)) {
                $builder->addDefinitions($file);
            }
        }
    }
    /**
     * This method is required for Matomo Cloud to allow for custom sorting of plugin order
     *
     */
    private function shouldSortPlugins() : bool
    {
        return isset($GLOBALS['MATOMO_SORT_PLUGINS']) && is_callable($GLOBALS['MATOMO_SORT_PLUGINS']);
    }
    /**
     * @param array $plugins
     * @return array
     */
    private function sortPlugins(array $plugins)
    {
        return call_user_func($GLOBALS['MATOMO_SORT_PLUGINS'], $plugins);
    }
    private function isDevelopmentModeEnabled() : bool
    {
        $section = $this->settings->getSection('Development');
        return (bool) @$section['enabled'];
        // TODO: code redundancy w/ Development. hopefully ok for now.
    }
    private function getDatabaseSchema() : string
    {
        $section = $this->settings->getSection('database');
        return $section['schema'] ?? 'Mysql';
    }
}
