<?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\Twig;

use Twig\Environment;
use Twig\Extension\SandboxExtension;
use Twig\Extra\Intl\IntlExtension;
use Twig\Loader\ArrayLoader;
use Twig\Sandbox\SecurityPolicyInterface;

/**
 * Renders user- or admin-authored free-text snippets (notification
 * subject/content/SMS text, mailing body) through a *sandboxed* Twig
 * environment so that the only thing an author can do is print the
 * whitelisted context tokens (e.g. `{{ offer.name }}`) and run a small set of
 * safe filters.
 *
 * This deliberately does NOT share the application's main Twig\Environment:
 * the main environment exposes the full filter/function/tag set (including
 * `raw`, arbitrary function calls and template inheritance) which turns the
 * `createTemplate()->render()` sinks into a server-side template injection /
 * remote-code-execution vector reachable by ROLE_HOST.
 *
 * The sandbox forbids ALL Twig functions, allows only a minimal safe filter
 * and tag set, and restricts method/property access to plain getter-style
 * reads on Ferienpass domain objects (see {@see NotificationSecurityPolicy}).
 */
final class SandboxedTextRenderer
{
    private readonly Environment $twig;

    public function __construct(?SecurityPolicyInterface $policy = null)
    {
        $this->twig = new Environment(new ArrayLoader(), [
            'autoescape' => false,
            'cache' => false,
            'strict_variables' => false,
        ]);

        // Register only side-effect-free formatting filters
        // (format_currency, format_datetime, country_name, …). The sandbox
        // policy still decides which of them an author may actually call.
        if (class_exists(IntlExtension::class)) {
            $this->twig->addExtension(new IntlExtension());
        }

        $sandbox = new SandboxExtension($policy ?? new NotificationSecurityPolicy(), true);
        $this->twig->addExtension($sandbox);
    }

    /**
     * @param array<string, mixed> $context
     */
    public function render(string $template, array $context): string
    {
        return $this->twig->createTemplate($template)->render($context);
    }
}
