<?php

/**
 * Конфигурация сервисов для контейнера
 *
 * @package Flowaxy\Core\System
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Core\System;

use Flowaxy\Core\Contracts\ContainerInterface;
use Flowaxy\Support\Helpers\IniHelper;
use Flowaxy\Support\Helpers\JsonHelper;
use InvalidArgumentException;

use function array_replace_recursive;
use function is_array;
use function is_file;
use function is_string;
use function pathinfo;
use function sprintf;
use function strtolower;

final class ServiceConfig
{
    public static function load(?string $baseFile = null, ?string $overrideFile = null): array
    {
        $base = self::loadFile($baseFile);
        $override = self::loadFile($overrideFile);

        return array_replace_recursive($base, $override);
    }

    public static function register(ContainerInterface $container, array $config): void
    {
        self::registerGroup($container, $config['singletons'] ?? [], true);
        self::registerGroup($container, $config['bindings'] ?? [], false);
    }

    private static function loadFile(?string $file): array
    {
        if ($file === null || ! is_file($file)) {
            return [];
        }

        $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));

        // Поддержка разных форматов
        if ($extension === 'php') {
            $config = require $file;
            return is_array($config) ? $config : [];
        } elseif ($extension === 'json') {
            $config = JsonHelper::readFile($file, true);
            return is_array($config) ? $config : [];
        } elseif ($extension === 'ini') {
            $config = IniHelper::readFile($file, true);
            return is_array($config) ? $config : [];
        }

        // Fallback для PHP
        $config = require $file;
        return is_array($config) ? $config : [];
    }

    private static function registerGroup(ContainerInterface $container, array $definitions, bool $shared): void
    {
        foreach ($definitions as $abstract => $definition) {
            $normalized = self::normalizeDefinition($definition, (string)$abstract);
            $class = $normalized['class'];
            $arguments = $normalized['arguments'] ?? [];
            $method = $shared ? 'singleton' : 'bind';

            $shouldBindDirectly = $arguments === [] || (string)$abstract === $class;

            if ($shouldBindDirectly) {
                $container->$method($abstract, $class);

                continue;
            }

            $container->$method(
                $abstract,
                static function (ContainerInterface $container) use ($class, $arguments) {
                    return $container->make($class, $arguments);
                }
            );
        }
    }

    private static function normalizeDefinition(mixed $definition, string $abstract): array
    {
        if (is_string($definition)) {
            return ['class' => $definition];
        }

        if (is_array($definition) && isset($definition['class']) && is_string($definition['class'])) {
            $normalized = ['class' => $definition['class']];
            if (isset($definition['arguments']) && is_array($definition['arguments'])) {
                $normalized['arguments'] = $definition['arguments'];
            }

            return $normalized;
        }

        throw new InvalidArgumentException(sprintf(
            'Service definition for "%s" має бути рядком або масивом з ключем class',
            $abstract
        ));
    }
}
