<?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\EventListener\HttpCache;

use Ferienpass\AdminBundle\Cache\FilteredPageTtlCalculator;
use Ferienpass\AdminBundle\Cache\FilterWithTtlRegistry;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

#[AsEventListener(event: KernelEvents::RESPONSE, priority: -1024)]
final class FilteredPageTtlListener
{
    private const int FLOOR_SECONDS = 300;
    private const int JITTER_MAX = 600;

    public function __construct(
        private readonly FilterWithTtlRegistry $registry,
        private readonly FilteredPageTtlCalculator $ttlCalculator,
    ) {
    }

    public function __invoke(ResponseEvent $event): void
    {
        if (!$event->isMainRequest()) {
            return;
        }

        $request = $event->getRequest();
        $filterParam = $request->query->all('filter');
        if (!\is_array($filterParam) || [] === $filterParam) {
            return;
        }

        $route = (string) $request->attributes->get('_route');
        $viewId = '' !== $route ? explode('.', $route)[0] : null;

        $ttl = null;
        foreach ($filterParam as $key => $value) {
            if (null === $value || '' === $value || false === $value || [] === $value) {
                continue;
            }
            $meta = $this->registry->get((string) $key);
            if (null === $meta) {
                continue;
            }

            if ([] !== $meta['views'] && (null === $viewId || !\in_array($viewId, $meta['views'], true))) {
                continue;
            }

            $seconds = $this->ttlCalculator->secondsUntilNextReset($meta['ttl']);
            $ttl = null === $ttl ? $seconds : min($ttl, $seconds);
        }

        if (null === $ttl) {
            return;
        }

        $response = $event->getResponse();
        $existing = $response->headers->getCacheControlDirective('s-maxage');
        $existing = is_numeric($existing) ? (int) $existing : null;

        $shortened = max(self::FLOOR_SECONDS, $ttl + random_int(0, self::JITTER_MAX));
        $finalTtl = min($existing ?? \PHP_INT_MAX, $shortened);

        if (null === $existing || $finalTtl < $existing) {
            $response->setSharedMaxAge($finalTtl);
        }
    }
}
