<?php

/*
 * This file is part of the Serenity package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Mercure\Transport;

use Symfony\Component\Mercure\Exception\InvalidArgumentException;
use Symfony\Component\Mercure\Exception\RuntimeException as MercureRuntimeException;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Notifier\Exception\LogicException;
use Symfony\Component\Notifier\Exception\RuntimeException;
use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException;
use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Message\MessageInterface;
use Symfony\Component\Notifier\Message\SentMessage;
use Symfony\Component\Notifier\Transport\AbstractTransport;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

final class MercureCustomTransport extends AbstractTransport
{
    /**
     * @param array<string> $topics
     */
    public function __construct(
        private readonly HubInterface $hub,
        private readonly string $hubId,
        private readonly array $topics,
        ?HttpClientInterface $client = null,
        ?EventDispatcherInterface $dispatcher = null,
    ) {
        parent::__construct($client, $dispatcher);
    }

    protected function doSend(MessageInterface $message): SentMessage
    {
        if (!$message instanceof ChatMessage) {
            throw new UnsupportedMessageTypeException(__CLASS__, ChatMessage::class, $message);
        }

        if (($options = $message->getOptions()) && !$options instanceof MercureCustomOptions) {
            throw new LogicException(\sprintf('The "%s" transport only supports instances of "%s" for options.', __CLASS__, MercureCustomOptions::class));
        }
        $options ??= new MercureCustomOptions($this->topics);
        $update = new Update($options->getTopics(), $message->getSubject(), $options->isPrivate(), $options->getId(), $options->getType(), $options->getRetry());

        try {
            $messageId = $this->hub->publish($update);

            $sentMessage = new SentMessage($message, (string) $this);
            $sentMessage->setMessageId($messageId);

            return $sentMessage;
        } catch (MercureRuntimeException|InvalidArgumentException $e) {
            throw new RuntimeException('Unable to post the Mercure message: '.$e->getMessage(), $e->getCode(), $e);
        }
    }

    public function __toString(): string
    {
        return \sprintf('mercure://%s%s', $this->hubId, '?'.http_build_query(['topic' => $this->topics], '', '&'));
    }

    public function supports(MessageInterface $message): bool
    {
        return $message instanceof ChatMessage && (null === $message->getOptions() || $message->getOptions() instanceof MercureCustomOptions);
    }
}
