<?php

declare(strict_types=1);

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

use CpCreation\VitiCore\Contact\Repository\ContactRepository;
use CpCreation\VitiCore\Marketing\Model\ProductReview;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;

final readonly class ReviewModerationMailer
{
    public function __construct(
        private MailerInterface $mailer,
        private ContactRepository $contactRepository,
        private TranslatorInterface $translator,
        private Environment $twig,
        private UrlGeneratorInterface $urlGenerator,
    ) {}

    public function sendSubmissionNotification(ProductReview $review, bool $resubmission = false): void
    {
        $contact = $this->contactRepository->findOne();
        if (null === $contact || null === $contact->getEmailToShop()) {
            throw new \RuntimeException('Aucune adresse boutique n’est configurée pour notifier le dépôt d’un avis.');
        }

        $cart = $review->getInvitation()->getCart();
        $locale = $cart->getLocale();
        $company = $contact->getCompany();
        $productLabel = $this->productLabel($review);
        $moderationUrl = $this->urlGenerator->generate(
            'admin_marketing_product_review_moderate',
            ['id' => $review->getId()->toString()],
            UrlGeneratorInterface::ABSOLUTE_URL,
        );
        $canPublish = \in_array($review->getStatus(), [
            ProductReview::STATUS_PENDING,
            ProductReview::STATUS_REJECTED,
        ], true);
        $canReject = ProductReview::STATUS_PENDING === $review->getStatus();
        $context = [
            'contact' => $contact,
            'company' => $company,
            'product' => $review->getProduct(),
            'product_label' => $productLabel,
            'review' => $review,
            'resubmission' => $resubmission,
            'locale' => $locale,
            'moderation_url' => $moderationUrl,
            'publish_url' => $canPublish ? $moderationUrl . '?intent=publish#review-publish' : null,
            'reject_url' => $canReject ? $moderationUrl . '?intent=reject#review-reject' : null,
        ];
        $email = new Email()
            ->subject($this->translator->trans(
                $resubmission
                    ? 'marketing.review.submission_mail.resubmitted_subject'
                    : 'marketing.review.submission_mail.subject',
                ['%product%' => $productLabel],
                'messages',
                $locale,
            ))
            ->from(new Address($contact->getEmailFrom(), $company))
            ->sender($contact->getEmailFrom())
            ->returnPath($contact->getEmailFrom())
            ->to($contact->getEmailToShop())
            ->html($this->twig->render('mail/review_submitted.html.twig', $context))
            ->text($this->twig->render('mail/review_submitted.txt.twig', $context));

        EmailLogMetadata::attach($email, 'review_submitted', $cart->getUser(), $cart);
        $this->mailer->send($email);
    }

    public function sendRejection(ProductReview $review): void
    {
        $contact = $this->contactRepository->findOne();
        if (null === $contact) {
            throw new \RuntimeException('Aucun contact boutique n’est configuré pour notifier le rejet d’un avis.');
        }

        $cart = $review->getInvitation()->getCart();
        $locale = $cart->getLocale();
        $company = $contact->getCompany();
        $productLabel = $this->productLabel($review);
        $subject = $this->translator->trans(
            'marketing.review.moderation_mail.subject',
            ['%product%' => $productLabel],
            'messages',
            $locale,
        );
        $context = [
            'contact' => $contact,
            'company' => $company,
            'product' => $review->getProduct(),
            'product_label' => $productLabel,
            'reason_code' => $review->getRejectionReasonCode(),
            'reason_detail' => $review->getRejectionReason(),
            'locale' => $locale,
        ];

        $email = new Email()
            ->subject($subject)
            ->from(new Address($contact->getEmailFrom(), $company))
            ->sender($contact->getEmailFrom())
            ->returnPath($contact->getEmailFrom())
            ->to($review->getInvitation()->getRecipientEmail())
            ->html($this->twig->render('mail/review_rejected.html.twig', $context))
            ->text($this->twig->render('mail/review_rejected.txt.twig', $context));

        EmailLogMetadata::attach($email, 'review_rejected', $cart->getUser(), $cart);
        $this->mailer->send($email);
    }

    public function sendWithdrawal(ProductReview $review): void
    {
        $contact = $this->contactRepository->findOne();
        if (null === $contact) {
            throw new \RuntimeException('Aucun contact boutique n’est configuré pour notifier le retrait d’un avis.');
        }

        $cart = $review->getInvitation()->getCart();
        $locale = $cart->getLocale();
        $company = $contact->getCompany();
        $productLabel = $this->productLabel($review);
        $subject = $this->translator->trans(
            'marketing.review.withdrawal_mail.subject',
            ['%company%' => $company],
            'messages',
            $locale,
        );
        $context = [
            'contact' => $contact,
            'company' => $company,
            'product' => $review->getProduct(),
            'product_label' => $productLabel,
            'reason_code' => $review->getWithdrawalReasonCode(),
            'reason_detail' => $review->getWithdrawalReason(),
            'locale' => $locale,
        ];

        $email = new Email()
            ->subject($subject)
            ->from(new Address($contact->getEmailFrom(), $company))
            ->sender($contact->getEmailFrom())
            ->returnPath($contact->getEmailFrom())
            ->to($review->getInvitation()->getRecipientEmail())
            ->html($this->twig->render('mail/review_withdrawn.html.twig', $context))
            ->text($this->twig->render('mail/review_withdrawn.txt.twig', $context));

        EmailLogMetadata::attach($email, 'review_withdrawn', $cart->getUser(), $cart);
        $this->mailer->send($email);
    }

    private function productLabel(ProductReview $review): string
    {
        $productLabel = $review->getProduct()->getFullName();
        $variantLabel = $review->getCartItem()?->getVariant()?->getLabel();

        return null !== $variantLabel && '' !== $variantLabel
            ? $productLabel . ' — ' . $variantLabel
            : $productLabel;
    }
}
