<?php

namespace App\Mail\Transport;

use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\Exception\HttpTransportException;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractApiTransport;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
 * toSend v2 Email API transport - no official Symfony bridge exists.
 */
class ToSendApiTransport extends AbstractApiTransport
{
    private const ENDPOINT = 'https://api.tosend.com/v2/emails';

    public function __construct(
        private readonly string $key,
        ?\Symfony\Contracts\HttpClient\HttpClientInterface $client = null,
        ?\Psr\EventDispatcher\EventDispatcherInterface $dispatcher = null,
        ?\Psr\Log\LoggerInterface $logger = null,
    ) {
        parent::__construct($client, $dispatcher, $logger);
    }

    public function __toString(): string
    {
        return 'tosend+api://api.tosend.com';
    }

    protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $envelope): ResponseInterface
    {
        $response = $this->client->request('POST', self::ENDPOINT, [
            'auth_bearer' => $this->key,
            'json'        => $this->getPayload($email, $envelope),
        ]);

        try {
            $statusCode = $response->getStatusCode();
            $result     = $response->toArray(false);
        } catch (TransportExceptionInterface $e) {
            throw new HttpTransportException('Could not reach the remote toSend server.', $response, 0, $e);
        }

        // 200 = all recipients accepted, 207 = partial batch success.
        if (!in_array($statusCode, [200, 207], true)) {
            $message = $result['message'] ?? $response->getContent(false);

            throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $message, $statusCode), $response);
        }

        $sentMessage->setMessageId($result['message_id'] ?? '');

        return $response;
    }

    private function getPayload(Email $email, Envelope $envelope): array
    {
        $addrString = fn (Address $a) => $a->getName() ? sprintf('%s <%s>', $a->getName(), $a->getAddress()) : $a->getAddress();
        $addrObject = fn (Address $a) => array_filter([
            'email' => $a->getAddress(),
            'name'  => $a->getName() ?: null,
        ]);

        $payload = [
            'from'    => $addrString($envelope->getSender()),
            'to'      => array_map($addrObject, $this->getRecipients($email, $envelope)),
            'subject' => $email->getSubject() ?? '',
        ];

        if ($cc = $email->getCc()) {
            $payload['cc'] = array_map($addrObject, $cc);
        }
        if ($bcc = $email->getBcc()) {
            $payload['bcc'] = array_map($addrObject, $bcc);
        }
        if ($replyTo = $email->getReplyTo()) {
            $payload['reply_to'] = $addrString($replyTo[0]);
        }
        if (null !== $text = $email->getTextBody()) {
            $payload['text'] = $text;
        }
        if (null !== $html = $email->getHtmlBody()) {
            $payload['html'] = $html;
        }

        $skipHeaders = ['from', 'to', 'cc', 'bcc', 'subject', 'content-type', 'reply-to', 'mime-version'];
        $headers     = [];
        foreach ($email->getHeaders()->all() as $name => $header) {
            if (in_array(strtolower($name), $skipHeaders, true)) {
                continue;
            }
            $headers[$header->getName()] = $header->getBodyAsString();
        }
        if ($headers) {
            $payload['headers'] = $headers;
        }

        if ($attachments = $email->getAttachments()) {
            $payload['attachments'] = array_map(fn (DataPart $a) => [
                'type'    => $a->getContentType(),
                'name'    => $a->getFilename() ?? 'attachment',
                'content' => base64_encode($a->getBody()),
            ], $attachments);
        }

        return $payload;
    }
}
