<?php

/**
 * Стандартизований завантажувач environment конфігурацій
 * Підтримка INI/JSON форматів та layered overrides
 *
 * @package Flowaxy\Core\System\Environment
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Core\System\Environment;

use Flowaxy\Core\System\PathResolver;
use Flowaxy\Support\Facades\Log;
use Flowaxy\Support\Helpers\IniHelper;
use Flowaxy\Support\Helpers\JsonHelper;
use Exception;

use function file_exists;
use function is_readable;
use function pathinfo;
use function strtolower;

/**
 * @method string getEnvironment()
 */
class EnvironmentLoader
{
    private string $rootDir;
    private string $configDir;
    private string $environment;
    private array $loadedConfigs = [];

    /**
     * @param string $rootDir Корінь проекту
     * @param string|null $configDir Директорія для конфігурацій
     * @param string|null $environment Середовище (development, staging, production)
     */
    public function __construct(string $rootDir, ?string $configDir = null, ?string $environment = null)
    {
        if (empty($rootDir)) {
            $this->rootDir = PathResolver::root();
        } else {
            $this->rootDir = rtrim($rootDir, '/\\');
        }

        if ($configDir === null) {
            $this->configDir = PathResolver::storageConfig() . DS;
        } else {
            $this->configDir = rtrim($configDir, '/\\') . DS;
        }
        $this->environment = $environment ?? $this->detectEnvironment();
    }

    /**
     * Автоматичне визначення середовища
     */
    private function detectEnvironment(): string
    {
        // Перевіряємо змінну оточення
        $env = $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? getenv('APP_ENV');
        if ($env !== false && ! empty($env)) {
            return strtolower((string)$env);
        }

        // Перевіряємо домен
        $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
        if (str_contains($host, 'localhost') || str_contains($host, '127.0.0.1') || str_contains($host, '.local')) {
            return 'development';
        }

        if (str_contains($host, 'staging') || str_contains($host, 'stage')) {
            return 'staging';
        }

        // За замовчуванням - production
        return 'production';
    }

    /**
     * Завантаження всіх environment конфігурацій
     *
     * @return array<string, mixed> Об'єднана конфігурація з урахуванням overrides
     */
    public function load(): array
    {
        // Завантажуємо конфігурації з пріоритетом:
        // 1. Базова конфігурація (загальна)
        // 2. Environment-специфічна конфігурація
        // 3. Local конфігурація (найвищий пріоритет)

        $config = [];

        // 1. Базова конфігурація
        $config = $this->mergeConfig($config, $this->loadConfigFile('environment.json'));
        $config = $this->mergeConfig($config, $this->loadConfigFile('environment.ini'));

        // 2. Environment-специфічна конфігурація
        $config = $this->mergeConfig($config, $this->loadConfigFile("environment.{$this->environment}.json"));
        $config = $this->mergeConfig($config, $this->loadConfigFile("environment.{$this->environment}.ini"));

        // 3. Local конфігурація (найвищий пріоритет)
        $config = $this->mergeConfig($config, $this->loadConfigFile('environment.local.json'));
        $config = $this->mergeConfig($config, $this->loadConfigFile('environment.local.ini'));

        $this->loadedConfigs = $config;

        return $config;
    }

    /**
     * Об'єднання конфігурацій з пріоритетом (другий масив перезаписує перший)
     */
    private function mergeConfig(array $base, array $override): array
    {
        return array_replace_recursive($base, $override);
    }

    /**
     * Завантаження конфігураційного файлу
     *
     * @param string $filename Ім'я файлу
     * @return array<string, mixed>
     */
    private function loadConfigFile(string $filename): array
    {
        $filePath = $this->configDir . $filename;

        if (! file_exists($filePath) || ! is_readable($filePath)) {
            return [];
        }

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

        try {
            if ($extension === 'json') {
                return JsonHelper::readFile($filePath, true) ?? [];
            } elseif ($extension === 'ini') {
                return IniHelper::readFile($filePath, true) ?? [];
            }
        } catch (Exception $e) {
            Log::Error("EnvironmentLoader: помилка завантаження {$filename}: " . $e->getMessage(), [
                    'exception' => $e,
                    'filename' => $filename
                ]);
        }

        return [];
    }



    /**
     * Отримання значення конфігурації
     *
     * @param string $key Ключ (може бути з точкою для вкладених значень)
     * @param mixed $default Значення за замовчуванням
     * @return mixed
     */
    public function get(string $key, $default = null)
    {
        if (empty($this->loadedConfigs)) {
            $this->load();
        }

        return $this->getValueByPath($this->loadedConfigs, $key, $default);
    }

    /**
     * Отримання значення за шляхом
     */
    private function getValueByPath(array $data, string $path, $default = null)
    {
        $keys = explode('.', $path);
        $value = $data;

        foreach ($keys as $key) {
            if (is_array($value) && isset($value[$key])) {
                $value = $value[$key];
            } else {
                return $default;
            }
        }

        return $value;
    }

    /**
     * Перевірка наявності ключа
     */
    public function has(string $key): bool
    {
        if (empty($this->loadedConfigs)) {
            $this->load();
        }

        return $this->hasValueByPath($this->loadedConfigs, $key);
    }

    /**
     * Перевірка наявності значення за шляхом
     */
    private function hasValueByPath(array $data, string $path): bool
    {
        $keys = explode('.', $path);
        $value = $data;

        foreach ($keys as $key) {
            if (is_array($value) && isset($value[$key])) {
                $value = $value[$key];
            } else {
                return false;
            }
        }

        return true;
    }

    /**
     * Отримання всієї конфігурації
     *
     * @return array<string, mixed>
     */
    public function all(): array
    {
        if (empty($this->loadedConfigs)) {
            $this->load();
        }

        return $this->loadedConfigs;
    }

    /**
     * Отримання поточного середовища
     */
    public function getEnvironment(): string
    {
        return $this->environment;
    }

    /**
     * Перезавантаження конфігурації
     */
    public function reload(): void
    {
        $this->loadedConfigs = [];
        $this->load();
    }
}
