<?php

/**
 * Централізована система логування
 * Підтримка різних рівнів логування, ротації файлів, фільтрації
 *
 * @package Flowaxy\Infrastructure\Logging
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Infrastructure\Logging;

use Flowaxy\Core\Contracts\LoggerInterface;
use Flowaxy\Core\System\PathResolver;
use Flowaxy\Support\Helpers\DatabaseHelper;
use Flowaxy\Support\Facades\Settings;
use PDO;

final class Logger implements LoggerInterface
{
    private static ?self $instance = null;
    private static bool $loadingSettings = false; // Прапорець для запобігання рекурсії
    private bool $settingsLoaded = false; // Прапорець завантаження налаштувань
    private string $logDir;
    private string $logFile;
    private int $maxFileSize = 10 * 1024 * 1024; // 10 MB
    private int $maxFiles = 5;
    private array $settings = [];
    private ?int $lastRotationTime = null;

    // Рівні логування
    public const int LEVEL_DEBUG = 0;
    public const int LEVEL_INFO = 1;
    public const int LEVEL_WARNING = 2;
    public const int LEVEL_ERROR = 3;
    public const int LEVEL_CRITICAL = 4;

    private const LEVEL_NAMES = [
        self::LEVEL_DEBUG => 'DEBUG',
        self::LEVEL_INFO => 'INFO',
        self::LEVEL_WARNING => 'WARNING',
        self::LEVEL_ERROR => 'ERROR',
        self::LEVEL_CRITICAL => 'CRITICAL',
    ];

    /**
     * Конструктор (приватний для Singleton)
     */
    private function __construct()
    {
        $this->logDir = PathResolver::logs() . DS;
        $this->logDir = rtrim($this->logDir, '/\\') . DS;
        $this->ensureLogDir();

        // Ім'я файлу логу з датою
        $this->logFile = $this->logDir . 'app-' . date('Y-m-d') . '.log';

        // НЕ завантажуємо налаштування в конструкторі, щоб уникнути циклічних залежностей
        // Налаштування будуть завантажені пізніше при першому логуванні або через reloadSettings()
    }

    /**
     * Отримання екземпляра класу (Singleton)
     *
     * @return self
     */
    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Створення директорії логів
     *
     * @return void
     */
    private function ensureLogDir(): void
    {
        // Перевіряємо існування директорії і створюємо, якщо не існує
        if (! is_dir($this->logDir)) {
            // Пробуємо створити директорію
            if (! @mkdir($this->logDir, 0755, true) && ! is_dir($this->logDir)) {
                // Не використовуємо Log::error тут, щоб уникнути циклічної залежності
                error_log("Logger::ensureLogDir: Failed to create directory: {$this->logDir}. Please check permissions.");
            } else {
                // Диагностика: успешное создание директории
                error_log("Logger::ensureLogDir: Directory created successfully: {$this->logDir}");
            }
        } else {
            // Проверяем права на запись
            if (! is_writable($this->logDir)) {
                error_log("Logger::ensureLogDir: Directory exists but is not writable: {$this->logDir}");
            } else {
                error_log("Logger::ensureLogDir: Directory exists and is writable: {$this->logDir}");
            }
        }
    }

    /**
     * Завантаження налаштувань з файлу logging.ini
     *
     * @return void
     */
    private function loadSettings(): void
    {
        // Запобігаємо рекурсії: якщо налаштування вже завантажуються, виходимо
        if (self::$loadingSettings) {
            return;
        }

        // Встановлюємо прапорець завантаження налаштувань
        self::$loadingSettings = true;

        // Налаштування за замовчуванням
        $this->settings = [
            'enabled' => true,
            'max_file_size' => $this->maxFileSize,
            'max_files' => $this->maxFiles,
            'retention_days' => 30,
            'rotation_type' => 'size',
            'rotation_time' => 24,
            'rotation_time_unit' => 'hours',
            'logging_levels' => 'DEBUG,INFO,WARNING,ERROR,CRITICAL', // За замовчуванням всі рівні для отладки
            'logging_types' => 'file',
        ];

        try {
            // Читаем настройки напрямую из файла logging.ini
            $loggingIniPath = PathResolver::storageConfig() . DS . 'system' . DS . 'logging.ini';

            if (file_exists($loggingIniPath) && is_readable($loggingIniPath)) {
                $iniData = parse_ini_file($loggingIniPath, true);

                if ($iniData !== false && isset($iniData['logging']) && is_array($iniData['logging'])) {
                    $loggingConfig = $iniData['logging'];

                    // Застосовуємо налаштування з файлу
                    if (isset($loggingConfig['enabled'])) {
                        $this->settings['enabled'] = in_array(strtolower($loggingConfig['enabled']), ['1', 'true', 'yes', 'on'], true);
                    }

                    if (isset($loggingConfig['levels']) && !empty($loggingConfig['levels'])) {
                        $this->settings['logging_levels'] = trim($loggingConfig['levels']);
                    } elseif (isset($loggingConfig['logging_levels']) && !empty($loggingConfig['logging_levels'])) {
                        // Совместимость со старым форматом
                        $this->settings['logging_levels'] = trim($loggingConfig['logging_levels']);
                    }

                    if (isset($loggingConfig['types']) && !empty($loggingConfig['types'])) {
                        $this->settings['logging_types'] = trim($loggingConfig['types']);
                    } elseif (isset($loggingConfig['logging_types']) && !empty($loggingConfig['logging_types'])) {
                        // Совместимость со старым форматом
                        $this->settings['logging_types'] = trim($loggingConfig['logging_types']);
                    }

                    if (isset($loggingConfig['max_file_size'])) {
                        $maxFileSize = (int)$loggingConfig['max_file_size'];
                        if ($maxFileSize > 0) {
                            $this->settings['max_file_size'] = $maxFileSize;
                            $this->maxFileSize = $maxFileSize;
                        }
                    }

                    if (isset($loggingConfig['retention_days'])) {
                        $retentionDays = (int)$loggingConfig['retention_days'];
                        if ($retentionDays > 0) {
                            $this->settings['retention_days'] = $retentionDays;
                            $this->settings['max_files'] = max(5, $retentionDays + 1);
                            $this->maxFiles = $this->settings['max_files'];
                        }
                    }

                    if (isset($loggingConfig['rotation_type'])) {
                        $this->settings['rotation_type'] = $loggingConfig['rotation_type'];
                    }

                    if (isset($loggingConfig['rotation_time'])) {
                        $this->settings['rotation_time'] = (int)$loggingConfig['rotation_time'];
                    }

                    if (isset($loggingConfig['rotation_time_unit'])) {
                        $this->settings['rotation_time_unit'] = $loggingConfig['rotation_time_unit'];
                    }

                    // Диагностика: логируем загруженные настройки
                    // error_log("Logger::loadSettings: Loaded from file. Enabled: " . ($this->settings['enabled'] ? 'yes' : 'no') . ", Levels: " . ($this->settings['logging_levels'] ?? 'default') . ", Types: " . ($this->settings['logging_types'] ?? 'default'));
                    // Тестовая запись в лог для проверки работы
                    // $testLine = "[" . date('Y-m-d H:i:s') . "] TEST: Logger initialized successfully from logging.ini\n";
                    // @file_put_contents($this->logFile, $testLine, FILE_APPEND | LOCK_EX);
                } else {
                    error_log("Logger::loadSettings: Failed to parse logging.ini or missing [logging] section. File exists: " . (file_exists($loggingIniPath) ? 'yes' : 'no') . ", Data: " . (is_array($iniData) ? 'parsed' : 'not parsed') . ", Keys: " . (is_array($iniData) ? implode(',', array_keys($iniData)) : 'N/A'));

                    // Тестовая запись в лог даже при ошибке парсинга
                    $testLine = "[" . date('Y-m-d H:i:s') . "] TEST: Logger initialized with default settings (failed to parse logging.ini)\n";
                    @file_put_contents($this->logFile, $testLine, FILE_APPEND | LOCK_EX);
                }
            } else {
                error_log("Logger::loadSettings: logging.ini not found or not readable. Path: {$loggingIniPath}, Exists: " . (file_exists($loggingIniPath) ? 'yes' : 'no') . ", Readable: " . (is_readable($loggingIniPath) ? 'yes' : 'no'));

                // Тестовая запись в лог даже без конфига (используем настройки по умолчанию)
                $testLine = "[" . date('Y-m-d H:i:s') . "] TEST: Logger initialized with default settings (logging.ini not found)\n";
                @file_put_contents($this->logFile, $testLine, FILE_APPEND | LOCK_EX);
            }
        } catch (\Throwable $e) {
            // У разі помилки використовуємо значення за замовчуванням
            // Не використовуємо Log::error тут, щоб уникнути циклічної залежності
            error_log('Logger::loadSettings помилка: ' . $e->getMessage() . ' | File: ' . $e->getFile() . ' | Line: ' . $e->getLine());
        } finally {
            // Скидаємо прапорець завантаження налаштувань
            self::$loadingSettings = false;
        }
    }

    /**
     * Отримання налаштування
     *
     * @param string $key Ключ налаштування
     * @param string $default Значення за замовчуванням
     * @return string
     */
    public function getSetting(string $key, string $default = ''): string
    {
        return (string)($this->settings[$key] ?? $default);
    }

    /**
     * Встановлення налаштування
     *
     * @param string $key Ключ налаштування
     * @param string $value Значення
     * @return void
     */
    public function setSetting(string $key, string $value): void
    {
        $this->settings[$key] = $value;

        // Зберігаємо в БД, якщо доступна
        if (class_exists(\Flowaxy\Support\Facades\Settings::class)) {
            Settings::manager()->set('logger_' . $key, $value);
        }
    }

    /**
     * Перезавантаження налаштувань з БД
     * Викликати після зміни налаштувань логування
     *
     * @return void
     */
    public function reloadSettings(): void
    {
        $this->settingsLoaded = false;
        self::$loadingSettings = false;
        $this->loadSettings();
        $this->settingsLoaded = true;
    }

    /**
     * Очищення кешу налаштувань (викликати після зміни налаштувань логування)
     *
     * @return void
     */
    public function clearSettingsCache(): void
    {
        $this->settingsLoaded = false;
        self::$loadingSettings = false;
    }

    /**
     * Логування повідомлення
     *
     * @param int $level Рівень логування
     * @param string $message Повідомлення
     * @param array $context Контекст (додаткові дані)
     * @return void
     */
    public function log(int $level, string $message, array $context = []): void
    {
        // Ліниве завантаження налаштувань при першому використанні
        if (! $this->settingsLoaded) {
            $this->loadSettings();
            $this->settingsLoaded = true;
        }

        // Якщо логування вимкнено, не логуємо
        if (! ($this->settings['enabled'] ?? true)) {
            error_log("Logger::log: Logging is disabled. Message: {$message}");
            return;
        }

        // Перевіряємо чи дозволено логування цього рівня
        $levelName = self::LEVEL_NAMES[$level] ?? 'UNKNOWN';
        $allowedLevels = $this->getAllowedLevels();

        // Якщо список дозволених рівнів порожній або рівень не в списку - не логуємо
        if (empty($allowedLevels) || ! in_array($levelName, $allowedLevels, true)) {
            error_log("Logger::log: Level {$levelName} not allowed. Allowed: " . implode(',', $allowedLevels) . " | Message: {$message}");
            return;
        }

        $timestamp = date('Y-m-d H:i:s');
        $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
        $uri = $_SERVER['REQUEST_URI'] ?? '/';
        $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';

        // Формуємо контекстний рядок
        $contextStr = '';
        if (! empty($context)) {
            $contextStr = ' | Context: ' . json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }

        // Формуємо рядок логу
        $logLine = sprintf(
            "[%s] %s: %s | IP: %s | %s %s%s\n",
            $timestamp,
            $levelName,
            $message,
            $ip,
            $method,
            $uri,
            $contextStr
        );

        // Отримуємо дозволені типи логування
        $loggingTypes = $this->getLoggingTypes();

        // Логуємо у файл
        if (in_array('file', $loggingTypes)) {
            $this->writeToFile($logLine);
        }

        // Логуємо в error_log
        if (in_array('error_log', $loggingTypes)) {
            // Використовуємо error_log для фінального виводу, щоб уникнути циклічної залежності
            error_log(trim($logLine));
        }
    }

    /**
     * Запис у файл з ротацією
     *
     * @param string $logLine Рядок для запису
     * @return void
     */
    private function writeToFile(string $logLine): void
    {
        $rotationType = $this->settings['rotation_type'] ?? 'size';
        $shouldRotate = false;

        // Перевіряємо ротацію по розміру
        if ($rotationType === 'size' || $rotationType === 'both') {
            if (file_exists($this->logFile) && filesize($this->logFile) >= $this->settings['max_file_size']) {
                $shouldRotate = true;
            }
        }

        // Перевіряємо ротацію по часу
        if ($rotationType === 'time' || $rotationType === 'both') {
            $rotationTime = (int)($this->settings['rotation_time'] ?? 24);
            $rotationTimeUnit = $this->settings['rotation_time_unit'] ?? 'hours';

            // Конвертуємо в секунди
            $rotationSeconds = $rotationTimeUnit === 'days'
                ? $rotationTime * 24 * 60 * 60
                : $rotationTime * 60 * 60;

            $currentTime = time();

            // Якщо файл існує, перевіряємо час останньої ротації
            if (file_exists($this->logFile)) {
                if ($this->lastRotationTime === null) {
                    // Встановлюємо час створення файлу як час останньої ротації
                    $this->lastRotationTime = filemtime($this->logFile);
                }

                if (($currentTime - $this->lastRotationTime) >= $rotationSeconds) {
                    $shouldRotate = true;
                }
            } else {
                // Новий файл - встановлюємо час ротації
                $this->lastRotationTime = $currentTime;
            }
        }

        // Виконуємо ротацію якщо потрібно
        if ($shouldRotate) {
            $this->rotateLogs();
            $this->lastRotationTime = time();
        }

        // Записуємо у файл
        $result = @file_put_contents($this->logFile, $logLine, FILE_APPEND | LOCK_EX);

        // Диагностика: если запись не удалась, пишем в error_log
        if ($result === false) {
            $error = error_get_last();
            $dirExists = is_dir($this->logDir);
            $dirWritable = $dirExists && is_writable($this->logDir);
            $fileExists = file_exists($this->logFile);
            $fileWritable = $fileExists && is_writable($this->logFile);
            $parentDir = dirname($this->logDir);
            $parentWritable = is_dir($parentDir) && is_writable($parentDir);

            error_log(
                "Logger::writeToFile: Failed to write log. " .
                    "File: {$this->logFile}, " .
                    "Error: " . ($error['message'] ?? 'Unknown error') . ", " .
                    "Dir exists: " . ($dirExists ? 'yes' : 'no') . ", " .
                    "Dir writable: " . ($dirWritable ? 'yes' : 'no') . ", " .
                    "File exists: " . ($fileExists ? 'yes' : 'no') . ", " .
                    "File writable: " . ($fileWritable ? 'yes' : 'no') . ", " .
                    "Parent dir writable: " . ($parentWritable ? 'yes' : 'no') . ", " .
                    "LogDir: {$this->logDir}"
            );
        } else {
            // Первая успешная запись - диагностика
            static $firstWrite = true;
            if ($firstWrite) {
                $firstWrite = false;
                error_log("Logger::writeToFile: First log entry written successfully to: {$this->logFile}");
            }
        }
    }

    /**
     * Ротація логів
     *
     * @return void
     */
    private function rotateLogs(): void
    {
        $pattern = $this->logDir . 'app-*.log';
        $files = glob($pattern);

        if ($files === false) {
            return;
        }

        $retentionDays = $this->settings['retention_days'] ?? 30;
        $cutoffTime = time() - ($retentionDays * 24 * 60 * 60);

        // Видаляємо старі файли за датою створення
        foreach ($files as $file) {
            $fileTime = @filemtime($file);
            if ($fileTime !== false && $fileTime < $cutoffTime) {
                @unlink($file);
            }
        }

        // Отримуємо список файлів заново після видалення
        $files = glob($pattern);
        if ($files === false) {
            $files = [];
        }

        // Сортуємо за датою зміни (нові першими)
        usort($files, function ($a, $b) {
            // Використовуємо @filemtime, щоб уникнути PHP warnings, якщо файл вже видалений/недоступний
            $timeA = @filemtime($a);
            $timeB = @filemtime($b);

            if ($timeA === false && $timeB === false) {
                return 0;
            }
            if ($timeA === false) {
                return 1;
            }
            if ($timeB === false) {
                return -1;
            }

            // Нові файли (більший mtime) повинні бути першими
            return $timeB <=> $timeA;
        });

        // Видаляємо старі файли понад ліміт
        $maxFiles = $this->settings['max_files'];
        for ($i = $maxFiles; $i < count($files); $i++) {
            @unlink($files[$i]);
        }

        // Перейменовуємо поточний файл
        if (file_exists($this->logFile)) {
            $newName = $this->logDir . 'app-' . date('Y-m-d') . '-' . time() . '.log';
            @rename($this->logFile, $newName);
        }
    }

    /**
     * Логування DEBUG
     *
     * @param string $message Повідомлення
     * @param array $context Контекст
     * @return void
     */
    public function logDebug(string $message, array $context = []): void
    {
        $this->log(self::LEVEL_DEBUG, $message, $context);
    }

    /**
     * Логування INFO
     *
     * @param string $message Повідомлення
     * @param array $context Контекст
     * @return void
     */
    public function logInfo(string $message, array $context = []): void
    {
        $this->log(self::LEVEL_INFO, $message, $context);
    }

    /**
     * Логування WARNING
     *
     * @param string $message Повідомлення
     * @param array $context Контекст
     * @return void
     */
    public function logWarning(string $message, array $context = []): void
    {
        $this->log(self::LEVEL_WARNING, $message, $context);
    }

    /**
     * Логування ERROR
     *
     * @param string $message Повідомлення
     * @param array $context Контекст
     * @return void
     */
    public function logError(string $message, array $context = []): void
    {
        $this->log(self::LEVEL_ERROR, $message, $context);
    }

    /**
     * Логування CRITICAL
     *
     * @param string $message Повідомлення
     * @param array $context Контекст
     * @return void
     */
    public function logCritical(string $message, array $context = []): void
    {
        $this->log(self::LEVEL_CRITICAL, $message, $context);
    }

    /**
     * Логування винятку
     *
     * @param \Throwable $exception Виняток
     * @param array $context Додатковий контекст
     * @return void
     */
    public function logException(\Throwable $exception, array $context = []): void
    {
        $context['exception'] = [
            'class' => get_class($exception),
            'message' => $exception->getMessage(),
            'code' => $exception->getCode(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'trace' => $exception->getTraceAsString(),
        ];

        $this->logError('Виняток: ' . $exception->getMessage(), $context);
    }

    /**
     * Логування SQL запитів
     *
     * @param string $query SQL запит
     * @param array $params Параметри запиту
     * @param float|null $executionTime Час виконання в секундах
     * @return void
     */
    public function logSql(string $query, array $params = [], ?float $executionTime = null): void
    {
        // Ліниве завантаження налаштувань
        if (! $this->settingsLoaded) {
            $this->loadSettings();
            $this->settingsLoaded = true;
        }

        // Перевіряємо, чи дозволено логування SQL запитів
        // Перевіряємо через logging_types (може містити 'sql_queries')
        $loggingTypes = $this->getLoggingTypes();
        $logSqlEnabled = in_array('sql_queries', $loggingTypes, true);

        // Також перевіряємо окреме налаштування
        if (! $logSqlEnabled && isset($this->settings['log_sql_queries'])) {
            $logSqlEnabled = (bool)$this->settings['log_sql_queries'];
        }

        if (! $logSqlEnabled) {
            return;
        }

        $context = [
            'type' => 'sql',
            'query' => $query,
            'params' => $params,
        ];

        if ($executionTime !== null) {
            $context['execution_time'] = $executionTime;
            $message = sprintf('SQL Query (%.4fs): %s', $executionTime, $query);
        } else {
            $message = 'SQL Query: ' . $query;
        }

        // Логуємо як DEBUG з контекстом SQL
        // Але перевіряємо, чи дозволено логування SQL окремо
        // Якщо DEBUG вимкнено, але SQL логування увімкнено, все одно логуємо
        if (in_array('DEBUG', $this->getAllowedLevels(), true)) {
            $this->log(self::LEVEL_DEBUG, $message, $context);
        } else {
            // Якщо DEBUG вимкнено, але SQL логування увімкнено, логуємо як INFO
            // Але тільки якщо INFO дозволено
            if (in_array('INFO', $this->getAllowedLevels(), true)) {
                $this->log(self::LEVEL_INFO, $message, $context);
            }
        }
    }

    /**
     * Логування помилок бази даних
     *
     * @param string $message Повідомлення про помилку
     * @param array $context Контекст (query, params, error_code, etc.)
     * @return void
     */
    public function logDbError(string $message, array $context = []): void
    {
        // Ліниве завантаження налаштувань
        if (! $this->settingsLoaded) {
            $this->loadSettings();
            $this->settingsLoaded = true;
        }

        // Перевіряємо, чи дозволено логування помилок БД
        // Перевіряємо через logging_types (може містити 'db_errors')
        $loggingTypes = $this->getLoggingTypes();
        $logDbErrorsEnabled = in_array('db_errors', $loggingTypes, true);

        // Також перевіряємо окреме налаштування
        if (! $logDbErrorsEnabled && isset($this->settings['log_db_errors'])) {
            $logDbErrorsEnabled = (bool)$this->settings['log_db_errors'];
        }

        if (! $logDbErrorsEnabled) {
            return;
        }

        $context['type'] = 'db_error';
        $this->log(self::LEVEL_ERROR, 'DB Error: ' . $message, $context);
    }

    /**
     * Отримання останніх записів логу
     *
     * @param int $lines Кількість рядків
     * @return array
     */
    public function getRecentLogs(int $lines = 100): array
    {
        if (! file_exists($this->logFile)) {
            return [];
        }

        $content = @file_get_contents($this->logFile);
        if ($content === false) {
            return [];
        }

        $allLines = explode("\n", $content);
        $allLines = array_filter($allLines, fn($line) => ! empty(trim($line)));

        return array_slice($allLines, -$lines);
    }

    /**
     * Очищення логів
     *
     * @return bool
     */
    public function clearLogs(): bool
    {
        $pattern = $this->logDir . 'app-*.log';
        $files = glob($pattern);

        if ($files === false) {
            return false;
        }

        $success = true;
        foreach ($files as $file) {
            if (! @unlink($file)) {
                $success = false;
            }
        }

        return $success;
    }

    /**
     * Отримання статистики логів
     *
     * @return array<string, mixed>
     */
    public function getStats(): array
    {
        $pattern = $this->logDir . 'app-*.log';
        $files = glob($pattern);

        if ($files === false) {
            return [
                'total_files' => 0,
                'total_size' => 0,
                'latest_file' => null,
                'latest_size' => 0,
            ];
        }

        $totalSize = 0;
        $latestFile = null;
        $latestTime = 0;

        foreach ($files as $file) {
            $size = @filesize($file);
            if ($size !== false) {
                $totalSize += $size;
            }

            $mtime = @filemtime($file);
            if ($mtime !== false && $mtime > $latestTime) {
                $latestTime = $mtime;
                $latestFile = $file;
            }
        }

        $latestSize = $latestFile ? @filesize($latestFile) : 0;

        return [
            'total_files' => count($files),
            'total_size' => $totalSize,
            'latest_file' => $latestFile ? basename($latestFile) : null,
            'latest_size' => $latestSize !== false ? $latestSize : 0,
        ];
    }

    /**
     * Отримання дозволених рівнів логування
     *
     * @return array<int, string>
     */
    private function getAllowedLevels(): array
    {
        // Перевіряємо налаштування (множественний вибір)
        if (isset($this->settings['logging_levels']) && ! empty($this->settings['logging_levels'])) {
            $levels = is_array($this->settings['logging_levels'])
                ? $this->settings['logging_levels']
                : explode(',', $this->settings['logging_levels']);

            return array_map('trim', array_filter($levels));
        }

        // Якщо налаштування не встановлено, дозволяємо тільки важливі рівні за замовчуванням
        return ['WARNING', 'ERROR', 'CRITICAL'];
    }

    /**
     * Отримання дозволених типів логування
     *
     * @return array<int, string>
     */
    private function getLoggingTypes(): array
    {
        if (isset($this->settings['logging_types']) && ! empty($this->settings['logging_types'])) {
            $types = is_array($this->settings['logging_types'])
                ? $this->settings['logging_types']
                : explode(',', $this->settings['logging_types']);

            return array_map('trim', array_filter($types));
        }

        // Якщо налаштування не встановлено, дозволяємо тільки файл за замовчуванням
        return ['file'];
    }

    // Запобігання клонуванню
    private function __clone() {}

    /**
     * @return void
     * @throws \Exception
     */
    public function __wakeup(): void
    {
        throw new \Exception('Неможливо десеріалізувати singleton');
    }
}
