<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Mailer\Command;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Mailer\ReviewInvitationMailer;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Messenger\Messaging\CommandHandlerInterface;
use CpCreation\VitiCore\Marketing\Model\ReviewInvitation;
use CpCreation\VitiCore\Marketing\Repository\ReviewInvitationRepository;
use CpCreation\VitiCore\Marketing\Repository\ReviewRequestConfigurationRepository;
use CpCreation\VitiCore\Marketing\Repository\ReviewSolicitationOptOutRepository;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;

#[AsMessageHandler]
final readonly class ReviewInvitationMailCommandHandler implements CommandHandlerInterface
{
    public function __construct(
        private ReviewInvitationRepository $invitationRepository,
        private ReviewRequestConfigurationRepository $configurationRepository,
        private ReviewSolicitationOptOutRepository $optOutRepository,
        private ReviewInvitationMailer $mailer,
    ) {}

    public function __invoke(ReviewInvitationMailCommand $command): void
    {
        $invitation = $this->invitationRepository->find($command->invitationId);
        if (!$invitation instanceof ReviewInvitation) {
            throw new UnrecoverableMessageHandlingException('Invitation d’avis introuvable.');
        }

        if (!hash_equals($invitation->getTokenHash(), hash('sha256', $command->token))) {
            throw new UnrecoverableMessageHandlingException('Jeton d’invitation d’avis invalide.');
        }

        if ($this->optOutRepository->hasOptedOut($invitation->getRecipientEmail())) {
            if (ReviewInvitation::STATUS_SCHEDULED === $invitation->getStatus()) {
                $invitation->cancel();
                $this->invitationRepository->save($invitation);
            }

            return;
        }

        if (ReviewInvitation::STATUS_SCHEDULED !== $invitation->getStatus()) {
            return;
        }

        $now = new \DateTimeImmutable();
        if ($invitation->getExpiresAt() <= $now) {
            $invitation->markExpired();
            $this->invitationRepository->save($invitation);

            return;
        }

        $configuration = $this->configurationRepository->findOne();
        if (null === $configuration || !$configuration->isEnabled()) {
            $invitation->cancel();
            $this->invitationRepository->save($invitation);

            return;
        }

        $this->mailer->send($invitation, $command->token, $configuration);
        $invitation->markSent($now);
        $this->invitationRepository->save($invitation);
    }
}
