<?php

/**
 * Безпечний логер без дублювання try-catch блоків
 * Централізує логування з автоматичною обробкою помилок
 *
 * @package Flowaxy\Support\Helpers
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Support\Helpers;

use Throwable;

use function class_exists;
use function error_log;
use function function_exists;
use function json_encode;
use function str_contains;

use const JSON_UNESCAPED_UNICODE;

/**
 * Безпечний логер
 * Надає методи для безпечного логування без необхідності обгортати кожен виклик у try-catch
 */
final class SafeLogger
{
    /**
     * Безпечно залогувати помилку
     *
     * @param string $message Повідомлення
     * @param array<string, mixed> $context Контекст
     * @return void
     */
    public static function error(string $message, array $context = []): void
    {
        self::safeLog(function () use ($message, $context) {
            if (function_exists('logError')) {
                logError($message, $context);
            } elseif (class_exists(\Flowaxy\Support\Facades\Log::class)) {
                \Flowaxy\Support\Facades\Log::Error($message, $context);
            } else {
                error_log($message . ' ' . json_encode($context, JSON_UNESCAPED_UNICODE));
            }
        });
    }

    /**
     * Безпечно залогувати попередження
     *
     * @param string $message Повідомлення
     * @param array<string, mixed> $context Контекст
     * @return void
     */
    public static function warning(string $message, array $context = []): void
    {
        self::safeLog(function () use ($message, $context) {
            if (function_exists('logWarning')) {
                logWarning($message, $context);
            } elseif (class_exists(\Flowaxy\Support\Facades\Log::class)) {
                \Flowaxy\Support\Facades\Log::Warning($message, $context);
            } else {
                error_log('[WARNING] ' . $message . ' ' . json_encode($context, JSON_UNESCAPED_UNICODE));
            }
        });
    }

    /**
     * Безпечно залогувати інформацію
     *
     * @param string $message Повідомлення
     * @param array<string, mixed> $context Контекст
     * @return void
     */
    public static function info(string $message, array $context = []): void
    {
        self::safeLog(function () use ($message, $context) {
            if (function_exists('logInfo')) {
                logInfo($message, $context);
            } elseif (class_exists(\Flowaxy\Support\Facades\Log::class)) {
                \Flowaxy\Support\Facades\Log::Info($message, $context);
            } else {
                error_log('[INFO] ' . $message . ' ' . json_encode($context, JSON_UNESCAPED_UNICODE));
            }
        });
    }

    /**
     * Безпечно залогувати відлагодження
     *
     * @param string $message Повідомлення
     * @param array<string, mixed> $context Контекст
     * @return void
     */
    public static function debug(string $message, array $context = []): void
    {
        self::safeLog(function () use ($message, $context) {
            if (function_exists('logDebug')) {
                logDebug($message, $context);
            } elseif (class_exists(\Flowaxy\Support\Facades\Log::class)) {
                \Flowaxy\Support\Facades\Log::Debug($message, $context);
            } else {
                // Debug повідомлення не логуються в error_log за замовчуванням
            }
        });
    }

    /**
     * Безпечно залогувати критичну помилку
     *
     * @param string $message Повідомлення
     * @param array<string, mixed> $context Контекст
     * @return void
     */
    public static function critical(string $message, array $context = []): void
    {
        self::safeLog(function () use ($message, $context) {
            if (function_exists('logCritical')) {
                logCritical($message, $context);
            } elseif (class_exists(\Flowaxy\Support\Facades\Log::class)) {
                \Flowaxy\Support\Facades\Log::Critical($message, $context);
            } else {
                error_log('[CRITICAL] ' . $message . ' ' . json_encode($context, JSON_UNESCAPED_UNICODE));
            }
        });
    }

    /**
     * Безпечно залогувати виняток
     *
     * @param Throwable $exception Виняток
     * @param array<string, mixed> $context Додатковий контекст
     * @return void
     */
    public static function exception(Throwable $exception, array $context = []): void
    {
        self::safeLog(function () use ($exception, $context) {
            if (function_exists('logException')) {
                logException($exception, $context);
            } elseif (class_exists(\Flowaxy\Support\Facades\Log::class)) {
                \Flowaxy\Support\Facades\Log::exception($exception);
            } else {
                error_log('[EXCEPTION] ' . $exception->getMessage() . ' ' . json_encode($context, JSON_UNESCAPED_UNICODE));
            }
        });
    }

    /**
     * Безпечно виконати логування з обробкою помилок
     *
     * @param callable $logCallback Callback для логування
     * @return void
     */
    private static function safeLog(callable $logCallback): void
    {
        try {
            $logCallback();
        } catch (Throwable $e) {
            // Якщо логування не вдалося, використовуємо error_log як fallback
            // Але тільки якщо це не помилка самого логування (щоб уникнути рекурсії)
            if (!self::isLoggingError($e)) {
                try {
                    error_log('SafeLogger: Failed to log - ' . $e->getMessage());
                } catch (Throwable $fallbackError) {
                    // Ігноруємо помилки fallback логування
                }
            }
        }
    }

    /**
     * Перевірити, чи виняток є помилкою логування
     * (використовується для запобігання рекурсії)
     *
     * @param Throwable $exception Виняток
     * @return bool
     */
    private static function isLoggingError(Throwable $exception): bool
    {
        $message = $exception->getMessage();
        $file = $exception->getFile();

        // Перевіряємо, чи помилка пов'язана з логуванням
        return str_contains($file, 'Log') ||
               str_contains($file, 'Logger') ||
               str_contains($message, 'log') ||
               str_contains($message, 'Logger');
    }
}
