<?php

/**
 * Хелпер для роботи з INI
 * Методи для парсингу та генерації INI
 *
 * @package Flowaxy\Support\Helpers
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Support\Helpers;

final class IniHelper
{
    /**
     * Парсити INI рядок
     *
     * @param string $ini
     * @param bool $processSections
     * @param int $scannerMode
     * @return array<string, mixed>|false
     */
    public static function parse(string $ini, bool $processSections = true, int $scannerMode = INI_SCANNER_NORMAL): array|false
    {
        return parse_ini_string($ini, $processSections, $scannerMode);
    }

    /**
     * Генерувати INI з даних
     *
     * @param array<string, mixed> $data
     * @return string
     */
    public static function dump(array $data): string
    {
        $output = '';

        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $output .= "[{$key}]\n";
                foreach ($value as $subKey => $subValue) {
                    $output .= self::formatIniValue($subKey, $subValue);
                }
                $output .= "\n";
            } else {
                $output .= self::formatIniValue($key, $value);
            }
        }

        return $output;
    }

    /**
     * Форматувати значення INI
     *
     * @param string $key
     * @param mixed $value
     * @return string
     */
    private static function formatIniValue(string $key, mixed $value): string
    {
        if (is_bool($value)) {
            $value = $value ? '1' : '0';
        } elseif (is_string($value)) {
            // Пустые строки должны быть в кавычках
            if ($value === '') {
                $value = '""';
            } elseif (str_contains($value, ' ') ||
                str_contains($value, '=') ||
                str_contains($value, ';') ||
                str_contains($value, '$') ||
                str_contains($value, '"') ||
                str_contains($value, "\n") ||
                str_contains($value, "\r")
            ) {
                // Хеши паролей и другие строки с спецсимволами должны быть в кавычках
                $value = '"' . addslashes($value) . '"';
            }
        }

        return "{$key} = {$value}\n";
    }

    /**
     * Прочитати INI з файлу
     *
     * @param string $path
     * @param bool $processSections
     * @param int $scannerMode
     * @return array<string, mixed>|false
     */
    public static function readFile(string $path, bool $processSections = true, int $scannerMode = INI_SCANNER_NORMAL): array|false
    {
        if (!FileHelper::exists($path)) {
            return false;
        }

        return parse_ini_file($path, $processSections, $scannerMode);
    }

    /**
     * Записати INI у файл
     *
     * @param string $path
     * @param array<string, mixed> $data
     * @param bool $addHeader Добавить заголовок с комментариями
     * @return bool
     */
    public static function writeFile(string $path, array $data, bool $addHeader = false): bool
    {
        $ini = self::dump($data);

        if ($addHeader) {
            $header = "; Auto-generated by Flowaxy CLI\n";
            $header .= "; Do not edit manually if you're not sure\n\n";
            $ini = $header . $ini;
        }

        return FileHelper::put($path, $ini) !== false;
    }

    /**
     * Отримати секцію з INI
     *
     * @param array<string, mixed> $ini
     * @param string $section
     * @return array<string, mixed>|false
     */
    public static function getSection(array $ini, string $section): array|false
    {
        return $ini[$section] ?? false;
    }

    /**
     * Встановити секцію в INI
     *
     * @param array<string, mixed> $ini
     * @param string $section
     * @param array<string, mixed> $values
     * @return array<string, mixed>
     */
    public static function setSection(array &$ini, string $section, array $values): array
    {
        $ini[$section] = $values;

        return $ini;
    }

    /**
     * Отримати значення з INI
     *
     * @param array<string, mixed> $ini
     * @param string $key
     * @param string|null $section
     * @param mixed $default
     * @return mixed
     */
    public static function getValue(array $ini, string $key, ?string $section = null, mixed $default = null): mixed
    {
        if ($section !== null) {
            return $ini[$section][$key] ?? $default;
        }

        return $ini[$key] ?? $default;
    }

    /**
     * Встановити значення в INI
     *
     * @param array<string, mixed> $ini
     * @param string $key
     * @param mixed $value
     * @param string|null $section
     * @return array<string, mixed>
     */
    public static function setValue(array &$ini, string $key, mixed $value, ?string $section = null): array
    {
        if ($section !== null) {
            if (!isset($ini[$section])) {
                $ini[$section] = [];
            }
            $ini[$section][$key] = $value;
        } else {
            $ini[$key] = $value;
        }

        return $ini;
    }

    /**
     * Валідувати INI файл
     *
     * @param string $path
     * @return bool
     */
    public static function validate(string $path): bool
    {
        if (!FileHelper::exists($path)) {
            return false;
        }

        try {
            $result = self::readFile($path, true);
            return is_array($result) && $result !== false;
        } catch (\Exception $e) {
            return false;
        }
    }
}
