<?php declare(strict_types=1);
/**
 * @package messenger
 * @author kputyr
 * @date 17.04.2025
 */

namespace IMATHUZH\Messenger\Test;

use IMATHUZH\Messenger\MessageDispatcher;
use IMATHUZH\Messenger\MessagePublisherInterface;
use IMATHUZH\Messenger\MessagePublisherError;
use PHPUnit\Framework\TestCase;



class MessageDispatcherTest extends TestCase
{
    protected function createMockPublisher($channels, $message): MessagePublisherInterface
    {
        $publisher = $this->createMock(MessagePublisherInterface::class);
        $publisher->expects($this->once())
            ->method('publishMessage')
            ->with($channels, $message);
        return $publisher;
    }

    public function testAddPublisher()
    {
        $name = 'test';
        $payload = ['value' => 42, 'message' => 'Don\'t panic!'];
        $encoded = json_encode([
            'type' => 'message',
            'name' => $name,
            'payload' => $payload
        ]);
        $channels = ['chA', 'chB'];

        $dispatcher = new MessageDispatcher();
        $result = $dispatcher->addPublisher($this->createMockPublisher($channels, $encoded));
        $this->assertSame($result, $dispatcher);
        $dispatcher->addPublisher($this->createMockPublisher($channels, $encoded));
        $dispatcher->publishMessage($channels, $name, $payload);
    }

    public static function urlProvider()
    {
        return [
            [ 'https://test.com/noslash',
              ['one', 'ch_a', 'private'],
              'https://test.com/noslash/pub-one,ch_a,private'
            ],
            [ 'https://test.com/trailing/slash/',
              ['one', 'ch_a', 'private'],
              'https://test.com/trailing/slash/pub-one,ch_a,private'
            ]
        ];
    }

    protected function createStubPublisher(array $errors = [])
    {
        $publisher = $this->createStub(MessagePublisherInterface::class);
        $publisher->method('publishMessage')->willReturnCallback(
            function($channels, $msg) use($errors) { yield from $errors; }
        );
        return $publisher;
    }

    protected function assertPublisherError(
        MessagePublisherInterface $expectedPublisher,
        string $expectedKey,
        string $expectedError,
        $value
    ) {
        $this->assertInstanceOf(MessagePublisherError::class, $value);
        $this->assertSame($expectedPublisher, $value->publisher);
        $this->assertSame($expectedKey, $value->key);
        $this->assertSame($expectedError, $value->message);
    }

    public function testPublishMessageErrors()
    {
        $name = 'test';
        $payload = ['value' => 42, 'message' => 'Don\'t panic!'];
        $encoded = json_encode([
            'type' => 'message',
            'name' => $name,
            'payload' => $payload
        ]);
        $channels = ['chA', 'chB'];

        $publishers = [
            $this->createStubPublisher(),
            $this->createStubPublisher([
                'first' => 'first error',
                'second' => 'second error'
            ]),
            $this->createStubPublisher()
        ];

        $dispatcher = new MessageDispatcher();
        foreach($publishers as $publisher) {
            $dispatcher->addPublisher($publisher);
        }
        $errors = $dispatcher->publishMessage($channels, $name, $payload);
        $this->assertCount(2, $errors);
        $this->assertPublisherError($publishers[1], 'first', 'first error', $errors[0]);
        $this->assertPublisherError($publishers[1], 'second', 'second error', $errors[1]);
    }


}