<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Export;

/**
 * Neutralizes spreadsheet formula (CSV/DDE) injection in user-derived cell values.
 *
 * Public, unauthenticated registration input (participant names, e-mail, phone,
 * free-text answers) ends up in exported spreadsheets. PhpSpreadsheet's
 * DefaultValueBinder treats strings starting with "=" as formulas, and spreadsheet
 * applications evaluate payloads such as =HYPERLINK(...) or =cmd|'/c calc'!A1 when
 * staff open the export.
 *
 * Any string starting with a formula-trigger character (= + - @, TAB, CR, NUL) is
 * prefixed with a single quote so the spreadsheet application treats it as literal
 * text instead of a formula. Non-string values (numbers, dates, booleans, null) are
 * returned unchanged so intentional numeric/date cells keep their type.
 */
final class FormulaEscaper
{
    /**
     * Characters that, as the first character of a cell, can trigger formula evaluation.
     */
    private const TRIGGERS = ['=', '+', '-', '@', "\t", "\r", "\0"];

    public static function escape(mixed $value): mixed
    {
        if (!\is_string($value) || '' === $value) {
            return $value;
        }

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

        return $value;
    }
}
