<?php

declare(strict_types=1);

/**
 * Container definitions for apexl/io.
 *
 * One file for the whole package rather than one per area: `ContainerFactory` globs
 * `vendor/apexl/**\/services.php` and merges whatever it finds in glob order, so
 * several files bought nothing but an ordering question nobody could answer by
 * reading them.
 */

use Apexl\Config\ConfigurationInterface;
use Apexl\Io\Cache\Interfaces\CacheManagerInterface;
use Apexl\Io\Cache\Interfaces\CacheStreamInterface;
use Apexl\Io\Cache\Services\CacheManager;
use Apexl\Io\Cache\Services\CacheStream;
use function Apexl\Io\env;
use Apexl\Io\Interfaces\GlobalDataInterface;
use Apexl\Io\Interfaces\LazyLoadAllowerInterface;
use Apexl\Io\Interfaces\OutputManagerInterface;
use Apexl\Io\Logger\Dtos\LoggerConfig;
use Apexl\Io\Routing\IoRouteCollector;
use Apexl\Io\Services\GlobalData;
use Apexl\Io\Services\LazyLoadAllowerFromConfigService;
use Apexl\Io\Services\SimpleOutputManager;
use Apexl\Io\System\Factories\ConfigurationFactory;
use Apexl\Io\System\Interfaces\ModuleFinderInterface;
use Apexl\Io\System\Services\ConfigModuleFinder;
use function DI\autowire;
use function DI\factory;
use Psr\Container\ContainerInterface;

use Slim\Factory\AppFactory;
use Slim\Interfaces\CallableResolverInterface;
use Slim\Interfaces\RouteCollectorInterface;

return [
    // ---------------------------------------------------------------- system

    ConfigurationInterface::class => factory([ConfigurationFactory::class, 'make']),

    /**
     * Config files the loader skips, by filename, as a comma-separated `CONFIG__EXCLUDE`.
     *
     * `phinx.php` is the default because it is not really app config: it is a phinx CLI
     * config file that happens to sit in a scanned directory, and including it builds a
     * second container and loads every config file again — per request. Its `vault` block
     * is derived from config the app already has, and nothing reads `phinx.*` at runtime.
     *
     * Set `CONFIG__EXCLUDE=` to load everything, or list other filenames to skip.
     */
    ConfigurationFactory::class => autowire()->constructorParameter(
        'exclude',
        array_values(array_filter(array_map(
            trim(...),
            explode(',', (string) env('CONFIG__EXCLUDE', 'phinx.php')),
        ))),
    ),

    GlobalDataInterface::class => autowire(GlobalData::class),
    LazyLoadAllowerInterface::class => autowire(LazyLoadAllowerFromConfigService::class),
    ModuleFinderInterface::class => autowire(ConfigModuleFinder::class),
    OutputManagerInterface::class => autowire(SimpleOutputManager::class),

    /**
     * Slim builds a plain Route unless told otherwise, and a plain Route cannot be asked
     * what was attached to it — its middleware exists only as a chain of anonymous handlers
     * over private state. IoRoute records what it is given, which is what lets a permission
     * live on the route as the middleware that enforces it and still be readable by the menu
     * and by `routes:dump`.
     *
     * `AppFactory::createFromContainer()` takes whatever is bound here, so this binding is
     * the whole installation.
     */
    RouteCollectorInterface::class => factory(
        static fn (ContainerInterface $container): IoRouteCollector => new IoRouteCollector(
            AppFactory::determineResponseFactory(),
            $container->get(CallableResolverInterface::class),
            $container,
        ),
    ),

    // ----------------------------------------------------------------- cache

    CacheManagerInterface::class => autowire(CacheManager::class),
    CacheStreamInterface::class => autowire(CacheStream::class),
    'cache.default' => factory(fn (CacheManagerInterface $cacheManager) => $cacheManager->default()),

    // ---------------------------------------------------------------- logger

    LoggerConfig::class => factory(
        fn (ConfigurationInterface $config): LoggerConfig => LoggerConfig::fromConfig($config)
    ),
];
