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

use Contao\CoreBundle\Filesystem\FilesystemItem;
use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Contao\CoreBundle\Image\ImageFactoryInterface;
use Contao\Image\ImageInterface;
use Contao\Image\ImportantPart;
use Contao\PageModel;
use Ferienpass\CoreBundle\ConsentManager\ConsentManager;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\DbafsLogo;
use Ferienpass\CoreBundle\Entity\DbafsMedia;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Entity\OfferMedia;
use Ferienpass\CoreBundle\Entity\Participant\ParticipantInterface;
use Ferienpass\CoreBundle\Service\OfferPrivateLinkService;
use Ferienpass\CoreBundle\Settings\PageConfigManager;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Attribute\AsTwigFilter;
use Twig\Attribute\AsTwigFunction;

class FerienpassTwigExtension
{
    public function __construct(
        private readonly VirtualFilesystemInterface $logosStorage,
        private readonly VirtualFilesystemInterface $mediaStorage,
        private readonly UrlGeneratorInterface $urlGenerator,
        private readonly UriSigner $uriSigner,
        private readonly ConsentManager $consentManager,
        private readonly PageConfigManager $pageConfigManager,
        private readonly OfferPrivateLinkService $privateLinkService,
        private readonly ImageFactoryInterface $imageFactory,
        #[Autowire(param: 'kernel.project_dir')]
        private readonly string $projectDir,
    ) {
    }

    #[AsTwigFunction('page_config')]
    public function getPageConfig(): \Ferienpass\CoreBundle\Entity\PageConfig
    {
        return $this->pageConfigManager->getPageConfig();
    }

    #[AsTwigFilter('to_filesystem_item')]
    public function toFilesystemItem(DbafsMedia|DbafsLogo|OfferMedia $entity): ?FilesystemItem
    {
        if ($entity instanceof DbafsLogo) {
            return $this->logosStorage->get($entity->getUuid());
        }

        if ($entity instanceof OfferMedia) {
            $entity = $entity->getMedia();
        }

        return $this->mediaStorage->get($entity->getUuid());
    }

    /**
     * Build an ImageInterface for an offer media entity and apply the stored
     * crop region (importantPart) so that {@code figure_html} honours it.
     *
     * Contao's ImageFactory only reads importantPart from FilesModel, which
     * does not cover our custom DbafsMedia storage; we therefore set it here.
     */
    #[AsTwigFilter('to_image')]
    public function toImage(DbafsMedia|OfferMedia $entity): ?ImageInterface
    {
        if ($entity instanceof OfferMedia) {
            $entity = $entity->getMedia();
        }

        try {
            $resolved = $this->mediaStorage->resolveUuid($entity->getUuid());
        } catch (\Throwable) {
            return null;
        }

        $path = \sprintf('%s/storage/media/%s', $this->projectDir, $resolved);

        try {
            $image = $this->imageFactory->create($path);
        } catch (\Throwable) {
            return null;
        }

        $importantPart = $entity->getImportantPart();
        if ($importantPart['width'] > 0 && $importantPart['height'] > 0) {
            $image->setImportantPart(new ImportantPart(
                (float) $importantPart['x'],
                (float) $importantPart['y'],
                (float) $importantPart['width'],
                (float) $importantPart['height'],
            ));
        }

        return $image;
    }

    #[AsTwigFunction('signed_path')]
    public function generateSignedUrl(string $route, array $parameters = []): string
    {
        $url = $this->urlGenerator->generate($route, $parameters);

        return $this->uriSigner->sign($url);
    }

    #[AsTwigFunction('get_page')]
    public function getPage(int|string $idOrAlias): ?PageModel
    {
        return PageModel::findByIdOrAlias($idOrAlias);
    }

    /**
     * @return array<int, \Ferienpass\CoreBundle\ConsentManager\ConsentState>
     */
    #[AsTwigFunction('consent_states')]
    public function getConsentStates(OfferInterface|Attendance $subject, ?ParticipantInterface $participant = null): array
    {
        if ($subject instanceof Attendance) {
            $states = $this->consentManager->forOfferAndParticipants($subject->getOffer(), [$subject->getParticipant()]);
            foreach ($states as $state) {
                $state->setConsentToken($subject->getConsentToken());
            }

            return $states;
        }

        return $this->consentManager->forOfferAndParticipants($subject, [$participant]);
    }

    #[AsTwigFunction('offer_private_link')]
    public function getOfferPrivateLink(OfferInterface $offer): string
    {
        return $this->urlGenerator->generate('cms_offer_private_link', $this->privateLinkService->urlParameters($offer), UrlGeneratorInterface::ABSOLUTE_URL);
    }
}
