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

use Contao\CoreBundle\Controller\AbstractFragmentController;
use Contao\CoreBundle\EventListener\SubrequestCacheSubscriber;
use Symfony\Component\HttpFoundation\Response;

/**
 * Base class for fragment controllers whose output contains per-user data.
 *
 * Marks every rendered response `private` AND stamps the
 * {@see SubrequestCacheSubscriber::MERGE_CACHE_HEADER}, so Contao's
 * cache-merge subscriber propagates the `private` state up to the parent
 * page response. The practical effect: any page that embeds a fragment
 * extending this class will come back to Varnish as `private` — regardless
 * of the page's own declared Cache-Control — and Varnish won't cache it.
 *
 * Use this instead of extending {@see AbstractFragmentController} directly
 * whenever the fragment renders anything tied to the current user
 * (firstname, email, personal data forms, application lists, CSRF tokens
 * inside per-user forms, etc.). If a fragment is genuinely public and safe
 * to share across role buckets, keep extending `AbstractFragmentController`.
 *
 * For responses built OUTSIDE `$this->render()` — e.g. `renderBlock(…)`
 * output thrown through a TurboStreamResponseException — call
 * {@see self::markResponseAsPrivate()} on the Response explicitly.
 */
abstract class AbstractPrivateFragmentController extends AbstractFragmentController
{
    /**
     * Override the parent's render to always mark the response as private.
     * Covers the overwhelmingly common case where a fragment controller
     * ends with `return $this->render(…)`.
     */
    protected function render(?string $view = null, array $parameters = [], ?Response $response = null): Response
    {
        return $this->markResponseAsPrivate(parent::render($view, $parameters, $response));
    }

    /**
     * Mark an arbitrary Response as "private + contribute to the page's
     * cache merge". Needed for responses that weren't produced by
     * `$this->render()` — e.g. `$this->renderBlock(…)` outputs thrown via
     * `TurboStreamResponseException`.
     */
    protected function markResponseAsPrivate(Response $response): Response
    {
        $response->setPrivate();
        $response->headers->addCacheControlDirective('no-store');
        $response->headers->set(SubrequestCacheSubscriber::MERGE_CACHE_HEADER, '1');

        return $response;
    }
}
