<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Controller;

use Chill\MainBundle\Entity\Workflow\EntityWorkflowSend;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSendView;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\Exception\HandlerWithPublicViewNotFoundException;
use Chill\MainBundle\Workflow\Messenger\PostPublicViewMessage;
use Chill\MainBundle\Workflow\Templating\EntityWorkflowViewMetadataDTO;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Annotation\Route;
use Twig\Environment;

final readonly class WorkflowViewSendPublicController
{
    public const LOG_PREFIX = '[workflow-view-send-public-controller] ';

    public function __construct(
        private EntityManagerInterface $entityManager,
        private LoggerInterface $chillLogger,
        private EntityWorkflowManager $entityWorkflowManager,
        private ClockInterface $clock,
        private Environment $environment,
        private MessageBusInterface $messageBus,
    ) {}

    #[Route('/public/main/workflow/send/{uuid}/view/{verificationKey}', name: 'chill_main_workflow_send_view_public', methods: ['GET'])]
    public function __invoke(EntityWorkflowSend $workflowSend, string $verificationKey, Request $request): Response
    {
        if (50 < $workflowSend->getNumberOfErrorTrials()) {
            throw new AccessDeniedHttpException('number of trials exceeded, no more access allowed');
        }

        if ($verificationKey !== $workflowSend->getPrivateToken()) {
            $this->chillLogger->info(self::LOG_PREFIX.'Invalid trial for this send', ['client_ip' => $request->getClientIp()]);
            $workflowSend->increaseErrorTrials();
            $this->entityManager->flush();

            throw new AccessDeniedHttpException('invalid verification key');
        }

        if ($this->clock->now() > $workflowSend->getExpireAt()) {
            return new Response(
                $this->environment->render('@ChillMain/Workflow/workflow_view_send_public_expired.html.twig'),
                409
            );
        }

        if (100 < $workflowSend->getViews()->count()) {
            $this->chillLogger->info(self::LOG_PREFIX.'100 view reached, not allowed to see it again');
            throw new AccessDeniedHttpException('100 views reached, not allowed to see it again');
        }

        try {
            $metadata = new EntityWorkflowViewMetadataDTO(
                $workflowSend->getViews()->count(),
                100 - $workflowSend->getViews()->count(),
            );
            $response = new Response(
                $this->entityWorkflowManager->renderPublicView($workflowSend, $metadata),
            );

            $view = new EntityWorkflowSendView($workflowSend, $this->clock->now(), $request->getClientIp());
            $this->entityManager->persist($view);
            $this->messageBus->dispatch(new PostPublicViewMessage($view->getId()));
            $this->entityManager->flush();

            return $response;
        } catch (HandlerWithPublicViewNotFoundException $e) {
            throw new \RuntimeException('Could not render the public view', previous: $e);
        }
    }
}
