<?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 'Association E-Cosplay <'.$this->adminEmail.'>';
    }

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

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

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

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

        $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, int $priority = 3): 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)
            ->priority($priority);

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

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

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

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

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

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

        $dnsReportUrl = $this->urlGenerator->generate('app_dns_report', [
            'token' => $messageId,
        ], UrlGeneratorInterface::ABSOLUTE_URL);
        $html = str_replace('__DNS_REPORT_URL__', $dnsReportUrl, $html);

        // Injection du bloc liste des pieces jointes (hors .asc, .p7z, smime)
        if ($attachments) {
            $html = $this->injectAttachmentsList($html, $attachments);
        }

        $email->html($html);

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

        // Ajout automatique du fichier VCF (fiche contact E-Cosplay)
        $vcfPath = $this->generateVcf();
        if (null !== $vcfPath) {
            $email->attachFromPath($vcfPath, 'Association-E-Cosplay.vcf', 'text/vcard');
        }

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

        $this->send($email);

        // Nettoyage du fichier VCF temporaire
        if (null !== $vcfPath) {
            @unlink($vcfPath);
        }
    }

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

    /**
     * Injecte un bloc HTML listant les pieces jointes dans le corps du mail,
     * juste avant le footer dark (#111827). Exclut .asc, .p7z et smime.
     *
     * @param array<array{path: string, name: string}> $attachments
     */
    private function injectAttachmentsList(string $html, array $attachments): string
    {
        $excluded = ['.asc', '.p7z'];
        $filtered = [];

        foreach ($attachments as $a) {
            $name = $a['name'];
            $path = $a['path'];
            $ext = strtolower(pathinfo($name, \PATHINFO_EXTENSION));
            if (\in_array('.'.$ext, $excluded, true) || str_contains(strtolower($name), 'smime')) {
                continue;
            }
            $size = file_exists($path) ? filesize($path) : 0;
            $filtered[] = ['name' => $name, 'size' => $size];
        }

        if ([] === $filtered) {
            return $html;
        }

        $items = '';
        foreach ($filtered as $f) {
            $sizeStr = $this->formatFileSize($f['size']);
            $items .= '<tr>'
                .'<td style="background-color: #f9fafb; border: 1px solid #e5e5e5; padding-top: 12px; padding-bottom: 12px; padding-left: 16px; padding-right: 16px;">'
                .'<table role="presentation" cellpadding="0" cellspacing="0" border="0"><tbody><tr>'
                .'<td style="padding-right: 12px; vertical-align: middle;"><span style="font-size: 24px;">&#128206;</span></td>'
                .'<td style="vertical-align: middle;">'
                .'<p style="font-family: Arial, Helvetica, sans-serif; font-size: 13px; font-weight: 700; margin-top: 0; margin-right: 0; margin-bottom: 2px; margin-left: 0;">'.htmlspecialchars($f['name'], \ENT_QUOTES, 'UTF-8').'</p>'
                .'<p style="font-family: Arial, Helvetica, sans-serif; font-size: 11px; color: #888888; margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0;">Piece jointe ('.$sizeStr.')</p>'
                .'</td>'
                .'</tr></tbody></table>'
                .'</td>'
                .'</tr>';
        }

        $block = '<tr><td style="padding-top: 0; padding-bottom: 24px; padding-left: 32px; padding-right: 32px;">'
            .'<p style="font-family: Arial, Helvetica, sans-serif; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: #9ca3af; margin-top: 0; margin-bottom: 8px;">Pieces jointes</p>'
            .'<table role="presentation" width="100%" cellpadding="0" cellspacing="4" border="0">'
            .$items
            .'</table>'
            .'</td></tr>';

        // Injecte avant le footer dark
        $marker = '<td align="center" style="background-color: #111827';
        $pos = strpos($html, $marker);
        if (false !== $pos) {
            $trPos = strrpos(substr($html, 0, $pos), '<tr>');
            if (false !== $trPos) {
                return substr($html, 0, $trPos).$block.substr($html, $trPos);
            }
        }

        return $html;
    }

    /**
     * Genere un fichier VCF (vCard 3.0) pour la fiche contact Association E-Cosplay.
     */
    private function generateVcf(): ?string
    {
        $vcf = implode("\r\n", [
            'BEGIN:VCARD',
            'VERSION:3.0',
            'N:E-Cosplay;Association;;;',
            'FN:Association E-Cosplay',
            'ORG:Association E-Cosplay',
            'TEL;TYPE=WORK,VOICE:+33679348802',
            'EMAIL;TYPE=INTERNET,PREF:client@e-cosplay.fr',
            'EMAIL;TYPE=INTERNET:client@e-cosplay.fr',
            'ADR;TYPE=WORK:;;42 rue de Saint-Quentin;Beautor;;02800;France',
            'URL:https://www.e-cosplay.fr',
            'URL:https://crm.e-cosplay.fr',
            'NOTE:SIREN 943121517 - SIRET 943 121 517 00011 - APE 9329Z',
            'CATEGORIES:Association,Cosplay,Evenementiel',
            'REV:'.date('Ymd\THis\Z'),
            'END:VCARD',
        ]);

        $tmpPath = tempnam(sys_get_temp_dir(), 'vcf_');
        if (false === $tmpPath) {
            return null; // @codeCoverageIgnore
        }

        file_put_contents($tmpPath, $vcf);

        return $tmpPath;
    }

    private function formatFileSize(int $bytes): string
    {
        if ($bytes >= 1048576) {
            return number_format($bytes / 1048576, 1, ',', ' ').' Mo';
        }
        if ($bytes >= 1024) {
            return number_format($bytes / 1024, 0, ',', ' ').' Ko';
        }

        return $bytes.' o';
    }

    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');
    }
}
