<?php

declare(strict_types=1);

namespace App\Support;

/**
 * Защита от CSV/formula-инъекции (OWASP «Formula Injection»).
 *
 * Ячейка выгрузки, начинающаяся с = + - @ (а также TAB/CR), при открытии файла
 * в Excel/LibreOffice трактуется как формула и может исполниться
 * (`=HYPERLINK(...)`, `=cmd|...`). Нейтрализация — префикс апострофом: ячейка
 * показывается как текст, формула не исполняется.
 *
 * Применять ТОЛЬКО к свободному тексту (комментарии, контакты, имена, телефон) —
 * НЕ к числовым колонкам, где ведущий `-` — это легитимный знак минуса.
 */
final class CsvFormulaGuard
{
    /** Символы-триггеры формул Excel/LibreOffice. */
    private const TRIGGERS = ['=', '+', '-', '@', "\t", "\r"];

    /**
     * Нейтрализует formula-инъекцию в строковом значении. null/пустую строку и
     * безопасные значения возвращает без изменений.
     */
    public static function neutralize(?string $value): ?string
    {
        if ($value === null || $value === '') {
            return $value;
        }

        return in_array($value[0], self::TRIGGERS, true) ? "'".$value : $value;
    }

    /**
     * Опасна ли ячейка произвольного типа? true только для НЕ-числовой строки,
     * начинающейся с формульного символа. Числа (int/float и числовые строки,
     * где ведущий `-`/`+` — знак) безопасны.
     */
    public static function isDangerous(mixed $value): bool
    {
        if (! is_string($value) || $value === '' || is_numeric($value)) {
            return false;
        }

        return in_array($value[0], self::TRIGGERS, true);
    }

    /**
     * Нейтрализует ячейку отчёта произвольного типа (для писателей-форматтеров,
     * где колонки смешанные). Числа не трогает, опасный текст префиксует
     * апострофом, null → пустая строка.
     */
    public static function neutralizeCell(mixed $value): string
    {
        if ($value === null) {
            return '';
        }

        return self::isDangerous($value) ? "'".$value : (string) $value;
    }
}
