<?php

declare(strict_types=1);

namespace Flowaxy\Infrastructure\Config;

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

use function defined;
use function file_exists;
use function is_array;
use function is_dir;
use function json_decode;
use function json_encode;
use function mkdir;
use const DIRECTORY_SEPARATOR;
use const DS;
use const JSON_THROW_ON_ERROR;
use const JSON_UNESCAPED_UNICODE;

/**
 * Менеджер настроек тем
 * Сохраняет настройки в storage/config/themes/{slug}.ini
 */
final class ThemeSettingsManager
{
    /**
     * Получить путь к файлу настроек темы
     *
     * @param string $slug
     * @return string
     */
    private static function getSettingsPath(string $slug): string
    {
        $ds = defined('DS') ? DS : DIRECTORY_SEPARATOR;
        $themesConfigDir = PathResolver::storageConfig() . $ds . 'themes';

        // Создаем директорию, если её нет
        if (!is_dir($themesConfigDir)) {
            if (!@mkdir($themesConfigDir, 0755, true) && !is_dir($themesConfigDir)) {
                try {
                    Log::Error('ThemeSettingsManager: Failed to create themes config directory', [
                        'dir' => $themesConfigDir,
                    ]);
                } catch (Throwable $logError) {
                    // Ignore logging errors
                }
            }
        }

        return $themesConfigDir . $ds . $slug . '.ini';
    }

    /**
     * Загрузить настройки темы
     *
     * @param string $slug
     * @return array<string, mixed>
     */
    public static function getSettings(string $slug): array
    {
        $settingsPath = self::getSettingsPath($slug);

        if (!file_exists($settingsPath)) {
            return [];
        }

        try {
            if (class_exists(IniHelper::class)) {
                $config = IniHelper::readFile($settingsPath, true);
                if (is_array($config) && isset($config['settings']) && is_array($config['settings'])) {
                    return self::decodeValues($config['settings']);
                }
            } else {
                $config = parse_ini_file($settingsPath, true);
                if ($config !== false && isset($config['settings']) && is_array($config['settings'])) {
                    return self::decodeValues($config['settings']);
                }
            }
        } catch (Throwable $e) {
            try {
                Log::Error('ThemeSettingsManager: Failed to load theme settings', [
                    'error' => $e->getMessage(),
                    'file' => $settingsPath,
                    'slug' => $slug,
                ]);
            } catch (Throwable $logError) {
                // Ignore logging errors
            }
        }

        return [];
    }

    /**
     * Получить значение настройки
     *
     * @param string $slug
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
    public static function getValue(string $slug, string $key, mixed $default = null): mixed
    {
        $settings = self::getSettings($slug);
        return $settings[$key] ?? $default;
    }

    /**
     * Установить значение настройки
     *
     * @param string $slug
     * @param string $key
     * @param mixed $value
     * @return bool
     */
    public static function setValue(string $slug, string $key, mixed $value): bool
    {
        $settings = self::getSettings($slug);
        $settings[$key] = $value;
        return self::saveSettings($slug, $settings);
    }

    /**
     * Сохранить настройки темы
     *
     * @param string $slug
     * @param array<string, mixed> $settings
     * @return bool
     */
    public static function saveSettings(string $slug, array $settings): bool
    {
        $settingsPath = self::getSettingsPath($slug);

        try {
            // Кодируем значения для INI
            $encodedSettings = self::encodeValues($settings);

            $data = ['settings' => $encodedSettings];

            if (class_exists(IniHelper::class)) {
                $result = IniHelper::writeFile($settingsPath, $data);
            } else {
                // Fallback: записываем напрямую
                $content = "[settings]\n";
                if (!empty($encodedSettings)) {
                    foreach ($encodedSettings as $key => $value) {
                        $formattedValue = self::formatIniValue($value);
                        $content .= "{$key} = {$formattedValue}\n";
                    }
                }
                // Создаем файл даже если настройки пустые
                $result = @file_put_contents($settingsPath, $content) !== false;
            }

            if ($result && file_exists($settingsPath)) {
                @chmod($settingsPath, 0644);
            }

            return $result;
        } catch (Throwable $e) {
            try {
                Log::Error('ThemeSettingsManager: Failed to save theme settings', [
                    'error' => $e->getMessage(),
                    'file' => $settingsPath,
                    'slug' => $slug,
                ]);
            } catch (Throwable $logError) {
                // Ignore logging errors
            }
            return false;
        }
    }

    /**
     * Удалить настройки темы
     *
     * @param string $slug
     * @return bool
     */
    public static function deleteSettings(string $slug): bool
    {
        $settingsPath = self::getSettingsPath($slug);

        if (file_exists($settingsPath)) {
            return @unlink($settingsPath);
        }

        return true;
    }

    /**
     * Кодировать значения для INI (массивы и объекты в JSON)
     *
     * @param array<string, mixed> $values
     * @return array<string, string>
     */
    private static function encodeValues(array $values): array
    {
        $encoded = [];
        foreach ($values as $key => $value) {
            if (is_array($value) || is_object($value)) {
                $encoded[$key] = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
            } else {
                $encoded[$key] = (string)$value;
            }
        }
        return $encoded;
    }

    /**
     * Декодировать значения из INI (JSON в массивы)
     *
     * @param array<string, string> $values
     * @return array<string, mixed>
     */
    private static function decodeValues(array $values): array
    {
        $decoded = [];
        foreach ($values as $key => $value) {
            // Пытаемся декодировать JSON
            if (is_string($value) && ($value[0] === '{' || $value[0] === '[')) {
                try {
                    $decodedValue = json_decode($value, true, 512, JSON_THROW_ON_ERROR);
                    $decoded[$key] = $decodedValue;
                } catch (Throwable $e) {
                    // Если не JSON, оставляем как строку
                    $decoded[$key] = $value;
                }
            } else {
                $decoded[$key] = $value;
            }
        }
        return $decoded;
    }

    /**
     * Форматировать значение для INI
     *
     * @param mixed $value
     * @return string
     */
    private static function formatIniValue(mixed $value): string
    {
        if (is_bool($value)) {
            return $value ? '1' : '0';
        }
        $stringValue = (string)$value;
        if (str_contains($stringValue, ' ') ||
            str_contains($stringValue, '=') ||
            str_contains($stringValue, ';') ||
            str_contains($stringValue, '$') ||
            str_contains($stringValue, '"') ||
            str_contains($stringValue, "\n") ||
            str_contains($stringValue, "\r")
        ) {
            return '"' . addslashes($stringValue) . '"';
        }
        return $stringValue;
    }
}
