<?php

namespace App\Service;

use App\Entity\EmailTracking;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Mailer\Messenger\SendEmailMessage;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Mime\Crypto\SMimeSigner;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class MailerService
{
    public function __construct(
        private MessageBusInterface $bus,
        #[Autowire('%kernel.project_dir%')] private string $projectDir,
        #[Autowire(env: 'SMIME_PASSPHRASE')] private string $smimePassphrase,
        #[Autowire('%admin_email%')] private string $adminEmail,
        private UrlGeneratorInterface $urlGenerator,
        private UnsubscribeManager $unsubscribeManager,
        private EntityManagerInterface $em,
    ) {
    }

    public function getAdminEmail(): string
    {
        return $this->adminEmail;
    }

    public function getAdminFrom(): string
    {
        return 'E-Ticket <'.$this->adminEmail.'>';
    }

    public function send(Email $email): void
    {
        $publicKeyPath = $this->projectDir.'/public/key.asc';

        if (file_exists($publicKeyPath)) {
            $email->attachFromPath($publicKeyPath, 'public_key.asc', 'application/pgp-keys');
        }

        $certificate = $this->projectDir.'/config/cert/certificate.pem';
        $privateKey = $this->projectDir.'/config/cert/private-key.pem';

        if (file_exists($certificate) && file_exists($privateKey)) {
            $signer = new SMimeSigner($certificate, $privateKey, $this->smimePassphrase);
            $email = $signer->sign($email);
        }

        $this->bus->dispatch(new SendEmailMessage($email));
    }

    /**
     * @param array<array{path: string, name?: string}>|null $attachments
     */
    public function sendEmail(string $to, string $subject, string $content, ?string $from = null, ?string $replyTo = null, bool $withUnsubscribe = true, ?array $attachments = null): void
    {
        $from ??= $this->getAdminFrom();
        $canUnsubscribe = $withUnsubscribe && !$this->isWhitelisted($to);

        if ($canUnsubscribe && $this->unsubscribeManager->isUnsubscribed($to)) {
            return;
        }

        $email = (new Email())
            ->from($from)
            ->to($to)
            ->subject($subject)
            ->html($content);

        if ($replyTo) {
            $email->replyTo($replyTo);
        }

        if ($attachments) {
            foreach ($attachments as $attachment) {
                $email->attachFromPath($attachment['path'], $attachment['name'] ?? null);
            }
        }

        $messageId = bin2hex(random_bytes(16));
        $email->getHeaders()->addIdHeader('Message-ID', $messageId.'@e-cosplay.fr');

        $tracking = new EmailTracking($messageId, $to, $subject);
        $this->em->persist($tracking);
        $this->em->flush();

        $trackingUrl = $this->urlGenerator->generate('app_email_track', [
            'messageId' => $messageId,
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        $html = $email->getHtmlBody();
        $html = str_replace('https://ticket.e-cosplay.fr/logo.jpg', $trackingUrl, $html);
        $email->html($html);

        if ($canUnsubscribe) {
            $this->addUnsubscribeHeaders($email, $to);
        }

        $this->send($email);
    }

    private function isWhitelisted(string $email): bool
    {
        return strtolower(trim($email)) === strtolower($this->adminEmail);
    }

    private function addUnsubscribeHeaders(Email $email, string $to): void
    {
        $token = $this->unsubscribeManager->generateToken($to);

        $unsubscribeUrl = $this->urlGenerator->generate('app_unsubscribe', [
            'email' => $to,
            'token' => $token,
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        $email->getHeaders()->addTextHeader(
            'List-Unsubscribe',
            sprintf('<%s>, <mailto:unsubscribe@e-cosplay.fr?subject=unsubscribe-%s>', $unsubscribeUrl, urlencode($to))
        );
        $email->getHeaders()->addTextHeader('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click');
    }
}
