<?php

/**
 * Централізований обробник винятків
 * Усуває дублювання коду обробки помилок по всьому проекту
 *
 * @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;

/**
 * Централізований обробник винятків
 * Надає методи для безпечної обробки та логування помилок
 */
final class ExceptionHandler
{
    /**
     * Безпечно виконати callback з обробкою помилок
     *
     * @param callable $callback Функція для виконання
     * @param mixed $defaultValue Значення за замовчуванням у разі помилки
     * @param string|null $errorMessage Повідомлення про помилку для логування
     * @param array<string, mixed> $context Додатковий контекст для логування
     * @return mixed Результат виконання callback або $defaultValue
     */
    public static function safeExecute(
        callable $callback,
        mixed $defaultValue = null,
        ?string $errorMessage = null,
        array $context = []
    ): mixed {
        try {
            return $callback();
        } catch (Throwable $e) {
            if ($errorMessage !== null) {
                self::logError($errorMessage, $e, $context);
            }
            return $defaultValue;
        }
    }

    /**
     * Безпечно виконати callback з обробкою помилок та повернути результат
     *
     * @param callable $callback Функція для виконання
     * @param callable|null $onError Callback для обробки помилки
     * @return mixed Результат виконання callback або результат $onError
     */
    public static function safeExecuteWithHandler(
        callable $callback,
        ?callable $onError = null
    ): mixed {
        try {
            return $callback();
        } catch (Throwable $e) {
            if ($onError !== null) {
                return $onError($e);
            }
            self::logError('Помилка виконання операції', $e);
            return null;
        }
    }

    /**
     * Безпечно залогувати помилку
     *
     * @param string $message Повідомлення про помилку
     * @param Throwable $exception Виняток
     * @param array<string, mixed> $context Додатковий контекст
     * @return void
     */
    public static function logError(
        string $message,
        Throwable $exception,
        array $context = []
    ): void {
        // Використовуємо SafeLogger, якщо доступний, інакше fallback
        if (class_exists(SafeLogger::class)) {
            SafeLogger::error($message, array_merge([
                'error' => $exception->getMessage(),
                'code' => $exception->getCode(),
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'trace' => $exception->getTraceAsString(),
                'exception' => $exception,
            ], $context));
        } else {
            // Fallback безпосередньо до error_log
            try {
                error_log($message . ' ' . json_encode(array_merge([
                    'error' => $exception->getMessage(),
                    'code' => $exception->getCode(),
                    'file' => $exception->getFile(),
                    'line' => $exception->getLine(),
                ], $context), JSON_UNESCAPED_UNICODE));
            } catch (Throwable $e) {
                // Ігноруємо помилки fallback логування
            }
        }
    }

    /**
     * Безпечно залогувати попередження
     *
     * @param string $message Повідомлення
     * @param array<string, mixed> $context Контекст
     * @return void
     */
    public static function logWarning(
        string $message,
        array $context = []
    ): void {
        if (class_exists(SafeLogger::class)) {
            SafeLogger::warning($message, $context);
        } else {
            try {
                error_log('[WARNING] ' . $message . ' ' . json_encode($context, JSON_UNESCAPED_UNICODE));
            } catch (Throwable $e) {
                // Ігноруємо помилки fallback логування
            }
        }
    }

    /**
     * Безпечно залогувати інформацію
     *
     * @param string $message Повідомлення
     * @param array<string, mixed> $context Контекст
     * @return void
     */
    public static function logInfo(
        string $message,
        array $context = []
    ): void {
        if (class_exists(SafeLogger::class)) {
            SafeLogger::info($message, $context);
        } else {
            try {
                error_log('[INFO] ' . $message . ' ' . json_encode($context, JSON_UNESCAPED_UNICODE));
            } catch (Throwable $e) {
                // Ігноруємо помилки fallback логування
            }
        }
    }

    /**
     * Безпечно залогувати виняток
     *
     * @param Throwable $exception Виняток
     * @param array<string, mixed> $context Додатковий контекст
     * @return void
     */
    public static function logException(
        Throwable $exception,
        array $context = []
    ): void {
        if (class_exists(SafeLogger::class)) {
            SafeLogger::error('Exception: ' . $exception->getMessage(), array_merge([
                'exception' => $exception,
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'trace' => $exception->getTraceAsString(),
            ], $context));
        } else {
            try {
                error_log('[EXCEPTION] ' . $exception->getMessage() . ' ' . json_encode(array_merge([
                    'file' => $exception->getFile(),
                    'line' => $exception->getLine(),
                ], $context), JSON_UNESCAPED_UNICODE));
            } catch (Throwable $e) {
                // Ігноруємо помилки fallback логування
            }
        }
    }

    /**
     * Перевірити, чи виняток є помилкою логування
     * (використовується для запобігання рекурсії)
     *
     * @param Throwable $exception Виняток
     * @return bool
     */
    public 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');
    }
}
