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

use Contao\CoreBundle\Controller\AbstractController as ContaoAbstractController;
use Contao\CoreBundle\EventListener\SubrequestCacheSubscriber;
use Contao\PageModel;
use Ferienpass\CmsBundle\Page\PageBuilder;
use Ferienpass\CmsBundle\Page\PageBuilderFactory;
use Symfony\Component\HttpFoundation\Response;

class AbstractController extends ContaoAbstractController
{
    public static function getSubscribedServices(): array
    {
        $services = parent::getSubscribedServices();
        $services[PageBuilderFactory::class] = PageBuilderFactory::class;

        return $services;
    }

    protected function checkToken(): void
    {
        $this->denyAccessUnlessGranted('ROLE_MEMBER');
    }

    protected function createPageBuilder(?PageModel $pageModel = null): PageBuilder
    {
        return $this->container->get(PageBuilderFactory::class)->create($pageModel);
    }

    /**
     * Stamp a page-level response as strictly per-user (private + no-store).
     *
     * Use this on every CMS page controller that requires authentication
     * (`/account`, `/meine-anmeldungen`, `/merkliste`, etc.). The fragment-
     * merge architecture (Contao's `SubrequestCacheSubscriber` propagating
     * `private` upward from any embedded
     * {@see \Ferienpass\CoreBundle\Controller\AbstractPrivateFragmentController}
     * sub-response) is supposed to make these pages private automatically,
     * but the chain is easy to break: one fragment forgetting to stamp the
     * merge sentinel, an Article fragment mixed in, a future controller
     * that bypasses PageBuilder — and the page comes back to Varnish as
     * `public, s-maxage=1y` from Contao's `FrontendTemplate::setCacheHeaders`,
     * caching User A's data under the role-bucket key.
     *
     * Calling this explicitly is belt-and-braces: independent of the merge
     * mechanism, the response goes out `private, no-store, must-revalidate`
     * and Varnish refuses to cache it.
     *
     * Also stamps {@see SubrequestCacheSubscriber::MERGE_CACHE_HEADER} so
     * that IF this response is ever rendered inside a parent that itself
     * has the sentinel, the `private` propagates upward — which is what we
     * want for nested-page scenarios that don't currently exist but are
     * cheap to support.
     */
    protected function markPageAsPrivate(Response $response): Response
    {
        $response->setPrivate();
        // setPrivate() removes `public` but NOT `s-maxage`. If Contao's
        // FrontendTemplate had already stamped `public, s-maxage=1y` before
        // we got the response, the s-maxage directive would linger alongside
        // `private` — contradictory but technically permitted. Most caches
        // ignore s-maxage on a private response, but Varnish has been known
        // to honour it under specific VCL configurations. Clear it
        // explicitly so the contract is unambiguous.
        $response->headers->removeCacheControlDirective('s-maxage');
        $response->headers->addCacheControlDirective('no-store');
        $response->headers->addCacheControlDirective('must-revalidate');
        $response->setMaxAge(0);
        $response->headers->set('Vary', 'Cookie', false);
        $response->headers->set(SubrequestCacheSubscriber::MERGE_CACHE_HEADER, '1');

        return $response;
    }
}
