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

use Ferienpass\AdminBundle\Controller\TagsResponses;
use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\HttpCache\CacheTag;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

/**
 * ESI fragment that renders the admin user profile dropdown.
 *
 * The parent admin page is cached by user-context hash (roles + host assignments),
 * so two different users with the same role/host profile would otherwise see
 * each other's name and email in the cached HTML. Rendering the dropdown as an
 * ESI fragment means Varnish caches it in a separate entry keyed by session
 * cookie (Vary: Cookie), keeping the outer page cacheable while the dropdown
 * stays per-user.
 *
 * Not a mapped route: Symfony's FragmentListener (enabled by
 * `framework.fragments.path: /_fragment`) signs a URI referencing this
 * controller and dispatches it at request time:
 *
 *   {{ render_esi(controller('Ferienpass\\AdminBundle\\Controller\\Fragment\\UserNavigationController')) }}
 */
final class UserNavigationController extends AbstractController
{
    use TagsResponses;

    public function __invoke(): Response
    {
        $user = $this->getUser();

        $response = $user instanceof User
            ? $this->render('@FerienpassAdmin/layout/_profile_dropdown.html.twig')
            : new Response('');

        // Same TTL as every other cacheable route (see fos_http_cache.yaml).
        // Invalidation is tag-based: when the user entity changes, the
        // CacheInvalidationListener emits a purge on its user.{uuid} tag;
        // anonymous variants never need invalidation.
        $response->setPublic();
        $response->setSharedMaxAge(31556926);

        // The FOS user-context-hash already splits cache entries by role
        // group, but two distinct users in the same group would collide on
        // the same fragment entry — Vary: Cookie partitions that further
        // per-session.
        $response->setVary(['Cookie']);

        if ($user instanceof User) {
            // Use the FOS response tagger instead of setting `xkey` by hand —
            // this goes through the configured header formatter and stays
            // consistent with `tagResponse()` elsewhere in the codebase.
            $this->tagResponse(CacheTag::user($user));
        }

        return $response;
    }
}
