<?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\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
 * Netcore (Pepipost) v5 Email API transport - no official Symfony bridge
 * exists for it. Payload shape mirrors SendGrid's v3 API, which Netcore's
 * v5 API was modeled on.
 */
class NetcoreApiTransport extends AbstractApiTransport
{
    private const HOST = 'emailapi.netcorecloud.net';

    public function __construct(
        private readonly string $key,
        private readonly string $region = 'us',
        ?\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 sprintf('netcore+api://%s', $this->getEndpoint());
    }

    protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $envelope): ResponseInterface
    {
        $response = $this->client->request('POST', 'https://' . $this->getEndpoint() . '/v5/mail/send', [
            'headers' => [
                '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 Netcore server.', $response, 0, $e);
        }

        if ($statusCode < 200 || $statusCode >= 300) {
            $message = $result['message'] ?? $result['error'] ?? $response->getContent(false);

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

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

        return $response;
    }

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

        $payload = [
            'from'    => $addr($envelope->getSender()),
            'subject' => $email->getSubject() ?? '',
            'content' => $this->getContent($email),
        ];

        $personalization = [
            'to' => array_map($addr, $this->getRecipients($email, $envelope)),
        ];
        if ($cc = array_map($addr, $email->getCc())) {
            $personalization['cc'] = $cc;
        }
        if ($bcc = array_map($addr, $email->getBcc())) {
            $personalization['bcc'] = $bcc;
        }
        $payload['personalizations'] = [$personalization];

        if ($replyTo = $email->getReplyTo()) {
            $payload['reply_to'] = $addr($replyTo[0]);
        }

        $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 ($email->getAttachments()) {
            $payload['attachments'] = $this->getAttachments($email);
        }

        return $payload;
    }

    private function getContent(Email $email): array
    {
        $content = [];
        if (null !== $text = $email->getTextBody()) {
            $content[] = ['type' => 'text/plain', 'value' => $text];
        }
        if (null !== $html = $email->getHtmlBody()) {
            $content[] = ['type' => 'text/html', 'value' => $html];
        }

        return $content;
    }

    private function getAttachments(Email $email): array
    {
        $attachments = [];
        foreach ($email->getAttachments() as $attachment) {
            $headers  = $attachment->getPreparedHeaders();
            $filename = $headers->getHeaderParameter('Content-Disposition', 'filename');

            $attachments[] = [
                'content'  => str_replace("\r\n", '', $attachment->bodyToString()),
                'type'     => $headers->get('Content-Type')->getBody(),
                'filename' => $filename,
            ];
        }

        return $attachments;
    }

    private function getEndpoint(): string
    {
        return $this->region === 'eu' ? 'apieu.netcorecloud.net' : self::HOST;
    }
}
