<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Marketing;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Mailer\Command\AbandonedCartReminderMailCommand;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Marketing\Model\AbandonedCartRule;
use CpCreation\VitiCore\Marketing\Repository\AbandonedCartConfigurationRepository;
use CpCreation\VitiCore\Marketing\Repository\AbandonedCartReminderRepository;
use CpCreation\VitiCore\Marketing\Repository\AbandonedCartRuleRepository;
use Symfony\Component\Messenger\MessageBusInterface;

final readonly class AbandonedCartReminderDispatcher
{
    public function __construct(
        private AbandonedCartConfigurationRepository $configurationRepository,
        private AbandonedCartRuleRepository $ruleRepository,
        private AbandonedCartReminderRepository $reminderRepository,
        private CartRepository $cartRepository,
        private AbandonedCartRuleSelector $ruleSelector,
        private AbandonedCartReminderIssuer $reminderIssuer,
        private MessageBusInterface $messageBus,
    ) {
    }

    public function dispatchDue(?\DateTimeImmutable $now = null, int $limit = 100): AbandonedCartDispatchResult
    {
        $configuration = $this->configurationRepository->findOne();
        if (null === $configuration || !$configuration->isEnabled()) {
            return new AbandonedCartDispatchResult(0, 0, 0);
        }

        $now ??= new \DateTimeImmutable();
        $enabledAt = $configuration->getEnabledAt() ?? $now;
        $limit = max(1, min(500, $limit));
        $eligibleCount = 0;
        $skippedCount = 0;
        $pendingMessages = [];
        $rules = $this->ruleRepository->findEnabledForConfiguration($configuration);

        if ([] === $rules) {
            return new AbandonedCartDispatchResult(0, 0, 0);
        }

        $minimumDelayHours = min(array_map(
            static fn (AbandonedCartRule $rule): int => $rule->getDelayHours(),
            $rules,
        ));
        $carts = $this->cartRepository->findEligibleForAbandonedCartReminder(
            $now->modify(\sprintf('-%d hours', $minimumDelayHours)),
            $enabledAt,
            $limit,
        );
        $eligibleCount = \count($carts);

        foreach ($carts as $cart) {
            $recipientEmail = $cart->getEmail();
            if (null === $recipientEmail || false === filter_var($recipientEmail, \FILTER_VALIDATE_EMAIL)) {
                ++$skippedCount;

                continue;
            }

            $dueRules = array_filter(
                $rules,
                static fn (AbandonedCartRule $rule): bool => null !== $cart->getAbandonedAt()
                    && $cart->getAbandonedAt() <= $now->modify(\sprintf('-%d hours', $rule->getDelayHours())),
            );
            $rule = $this->ruleSelector->selectMostAdvantageous($dueRules, $cart);
            if (null === $rule) {
                ++$skippedCount;

                continue;
            }

            $issued = $this->reminderIssuer->issue($rule, $cart, $recipientEmail, $now);
            $pendingMessages[] = [$issued->reminder, $issued->token];
        }

        $this->reminderRepository->flush();

        foreach ($pendingMessages as [$reminder, $token]) {
            $this->messageBus->dispatch(new AbandonedCartReminderMailCommand(
                reminderId: $reminder->getId()->toString(),
                token: $token,
            ));
        }

        return new AbandonedCartDispatchResult(
            eligibleCount: $eligibleCount,
            createdCount: \count($pendingMessages),
            skippedCount: $skippedCount,
        );
    }
}
