<?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\Workflow\EventSubscriber;

use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserGroup;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Notification\FlagProviders\WorkflowTransitionNotificationFlagProvider;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Workflow\Event\Event;
use Symfony\Component\Workflow\Registry;

class NotificationOnTransition implements EventSubscriberInterface
{
    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly \Twig\Environment $engine,
        private readonly MetadataExtractor $metadataExtractor,
        private readonly Security $security,
        private readonly Registry $registry,
        private readonly EntityWorkflowManager $entityWorkflowManager,
    ) {}

    public static function getSubscribedEvents(): array
    {
        return [
            'workflow.completed' => ['onCompletedSendNotification', 2048],
        ];
    }

    /**
     * Send a notification to:.
     *
     * * the dests of the new step, or the members of a user group if the user group has no email;
     * * the users which subscribed to workflow, on each step, or on final;
     *
     * **Warning** take care that this method must be executed **after** the dest users are added to
     * the step (@see{EntityWorkflowStep::addDestUser}). Currently, this is done during
     *
     * @see{EntityWorkflowTransitionEventSubscriber::addDests}.
     */
    public function onCompletedSendNotification(Event $event): void
    {
        if (!$event->getSubject() instanceof EntityWorkflow) {
            return;
        }

        /** @var EntityWorkflow $entityWorkflow */
        $entityWorkflow = $event->getSubject();

        /** @var array<string, User> $dests array of unique values, where keys is the object's hash */
        $dests = [];

        $title = $this->entityWorkflowManager->getHandler($entityWorkflow)->getEntityTitle($entityWorkflow);

        foreach (array_merge(
            // the subscriber to each step
            $entityWorkflow->getSubscriberToStep()->toArray(),
            // the subscriber to final, only if final
            $entityWorkflow->isFinal() ? $entityWorkflow->getSubscriberToFinal()->toArray() : [],
            // the dests for the current step
            $entityWorkflow->getCurrentStep()->getDestUser()->toArray(),
            // the cc users for the current step
            $entityWorkflow->getCurrentStep()->getCcUser()->toArray(),
            // the users within groups
            $entityWorkflow->getCurrentStep()->getDestUserGroups()->reduce(
                function (array $accumulator, UserGroup $userGroup) {
                    if ($userGroup->hasEmail()) {
                        // this prevent users to be notified twice if they will already be notiied by the group
                        return $accumulator;
                    }

                    foreach ($userGroup->getUsers() as $user) {
                        $accumulator[] = $user;
                    }

                    return $accumulator;
                },
                []
            ),
        ) as $dest) {
            $dests[spl_object_hash($dest)] = $dest;
        }

        $place = $this->metadataExtractor->buildArrayPresentationForPlace($entityWorkflow);
        $workflow = $this->metadataExtractor->buildArrayPresentationForWorkflow(
            $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName())
        );

        foreach ($dests as $subscriber) {
            if (
                $this->security->getUser() === $subscriber
            ) {
                continue;
            }

            $context = [
                'entity_workflow' => $entityWorkflow,
                'dest' => $subscriber,
                'place' => $place,
                'workflow' => $workflow,
                'is_dest' => \in_array($subscriber->getId(), array_map(
                    static fn (User $u) => $u->getId(),
                    $entityWorkflow->getCurrentStep()->getDestUser()->toArray()
                ), true),
                'title' => $title,
            ];

            $notification = new Notification();
            $notification
                ->setRelatedEntityId($entityWorkflow->getId())
                ->setRelatedEntityClass(EntityWorkflow::class)
                ->setTitle($this->engine->render('@ChillMain/Workflow/workflow_notification_on_transition_completed_title.fr.txt.twig', $context))
                ->setMessage($this->engine->render('@ChillMain/Workflow/workflow_notification_on_transition_completed_content.fr.txt.twig', $context))
                ->addAddressee($subscriber)
                ->setType(WorkflowTransitionNotificationFlagProvider::FLAG);
            $this->entityManager->persist($notification);
        }
    }
}
