<?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;

/**
 * SMTP2GO v3 Email API transport - no official Symfony bridge exists.
 */
class Smtp2GoApiTransport extends AbstractApiTransport
{
    private const ENDPOINT = 'https://api.smtp2go.com/v3/email/send';

    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 'smtp2go+api://api.smtp2go.com';
    }

    protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $envelope): ResponseInterface
    {
        $response = $this->client->request('POST', self::ENDPOINT, [
            'headers' => [
                'X-Smtp2go-Api-Key' => $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 SMTP2GO server.', $response, 0, $e);
        }

        $data      = $result['data'] ?? [];
        $succeeded = $data['succeeded'] ?? 0;

        if (200 !== $statusCode || $succeeded < 1 || !empty($data['error'])) {
            $message = $data['error'] ?? $this->describeFailures($data['failures'] ?? null) ?? $response->getContent(false);

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

        $sentMessage->setMessageId($data['email_id'] ?? '');

        return $response;
    }

    private function describeFailures(?array $failures): ?string
    {
        if (!$failures) {
            return null;
        }

        return implode('; ', array_map(
            fn ($f) => is_array($f) ? ($f['error'] ?? json_encode($f)) : (string) $f,
            $failures
        ));
    }

    private function getPayload(Email $email, Envelope $envelope): array
    {
        $stringify = fn (Address $a) => $a->getName() ? sprintf('%s <%s>', $a->getName(), $a->getAddress()) : $a->getAddress();

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

        if ($cc = $email->getCc()) {
            $payload['cc'] = array_map($stringify, $cc);
        }
        if ($bcc = $email->getBcc()) {
            $payload['bcc'] = array_map($stringify, $bcc);
        }
        if (null !== $text = $email->getTextBody()) {
            $payload['text_body'] = $text;
        }
        if (null !== $html = $email->getHtmlBody()) {
            $payload['html_body'] = $html;
        }

        $customHeaders = [];
        if ($replyTo = $email->getReplyTo()) {
            $customHeaders[] = ['header' => 'Reply-To', 'value' => implode(', ', array_map($stringify, $replyTo))];
        }

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

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

        return $payload;
    }
}
