<?php

declare(strict_types=1);

namespace App\Tests\Unit\Service;

use App\Service\MercurePublisher;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;

/**
 * MercurePublisher's contract is small but load-bearing:
 *   1. The topic format includes the device id (the SPA subscribes by id).
 *   2. The payload is published as JSON.
 *   3. A throwing hub MUST NOT propagate — a flaky Mercure container can
 *      never break a poll response or a settings PATCH for the user.
 */
class MercurePublisherTest extends TestCase
{
    public function test_publishes_to_device_topic_with_json_payload(): void
    {
        $captured = null;
        $hub = $this->createMock(HubInterface::class);
        $hub->expects($this->once())
            ->method('publish')
            ->willReturnCallback(function (Update $u) use (&$captured) {
                $captured = $u;
                return 'urn:uuid:test';
            });

        $publisher = new MercurePublisher($hub, new NullLogger());
        $publisher->publishDevice(42, ['id' => 42, 'name' => 'Living Room']);

        $this->assertNotNull($captured);
        $this->assertSame(
            ['https://wevisto.com/devices/42'],
            $captured->getTopics(),
        );
        $this->assertSame('{"id":42,"name":"Living Room"}', $captured->getData());
    }

    public function test_swallows_hub_exceptions_so_callers_never_blow_up(): void
    {
        $hub = $this->createMock(HubInterface::class);
        $hub->method('publish')->willThrowException(new \RuntimeException('hub down'));

        $publisher = new MercurePublisher($hub, new NullLogger());

        // No exception should escape — if this throws, the test fails.
        $publisher->publishDevice(42, ['id' => 42]);
        $this->expectNotToPerformAssertions();
    }
}
