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

namespace IMATHUZH\Messenger\Test;

use IMATHUZH\Messenger\HttpPublisher;
use IMATHUZH\Messenger\Constants;
use PHPUnit\Framework\TestCase;

/**
 * Replaces the actual touch action with the one that
 * registers the file to be touched and returns the
 * desired result of the action.
 */
class MockedHttpPublisher extends HttpPublisher
{
    /** The mocked HTTP response */
    public $response = null;

    /** The list of files on which the action was called */
    public $targets = [];

    /** Exposes the current HTTP options */
    public function httpOption(string $key) {
        return $this->options[$key] ?? null;
    }

    /** The mocked action */
    protected function action(string $url): ?string
    {
        $this->targets[] = $url;
        return is_array($this->response)
            ? $this->response[0] : null;
    }
}


class HttpPublisherTest extends TestCase
{
    public function testPublishMessageRequest()
    {
        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => 'secret-token'
        ];
        $options = [ 'timeout' => 30 ];
        $msg = json_encode(['value' => 42, 'message' => 'Don\'t panic!']);

        $publisher = new MockedHttpPublisher('https://test.com/:pub:', $headers, $options);
        
        foreach($publisher->publishMessage(['anything'], $msg) as $error) { }

        $this->assertSame('POST', $publisher->httpOption('method'));
        $this->assertSame(Constants::USER_AGENT, $publisher->httpOption('user_agent'));
        $this->assertSame($options['timeout'], $publisher->httpOption('timeout'));
        $this->assertSame($headers, $publisher->httpOption('header'));
        $this->assertSame($msg, $publisher->httpOption('content'));
    }

    public static function urlProvider()
    {
        return [
            ':pub: at the end' => [
                'https://test.com/path/pub-:pub:',
                ['one', 'ch_a', 'private'],
                'https://test.com/path/pub-one,ch_a,private'
            ],
            ':pub: in the middle' => [
                'https://test.com/inside/:pub:/trailing/',
                ['one', 'ch_a', 'private'],
                'https://test.com/inside/one,ch_a,private/trailing/'
            ],
            'no :pub:' => [
                'https://test.com/messenger/endpoint',
                ['one', 'two', 'three'],
                'https://test.com/messenger/endpoint'
            ]
        ];
    }

    /**
     * @dataProvider urlProvider
     */
    public function testPublishMessageUrl(string $baseUrl, array $channels, string $targetUrl)
    {
        $headers = [ 'Content-Type' => 'application/json' ];
        $msg = json_encode(['value' => 42, 'message' => 'Don\'t panic!']);

        $publisher = new MockedHttpPublisher($baseUrl, $headers);
        
        foreach($publisher->publishMessage($channels, $msg) as $error) { }
        $this->assertSame($publisher->targets, [$targetUrl]);
    }

    public function testPublishToNoChannels()
    {
        $publisher = new MockedHttpPublisher('http://localhost/messanger');
        
        foreach($publisher->publishMessage([], 'message') as $error) { }
        $this->assertSame($publisher->targets, []);
    }

    public static function responseProvider()
    {
        return [ [
            null,
            "bad request"
        ], [
            ['Funny header'],
            "bad response"
        ], [
            ['HTTP/1.1 403 Forbidden'],
            "invalid HTTP status 403"
        ], [
            ['HTTP/1.1 305 See other'],
            "invalid HTTP status 305"
        ], [
            ['HTTP/1.1 200 OK'],
            null
        ], [
            ['HTTP/1.1 201 Created'],
            null
        ], [
            ['HTTP/1.1 202 Accepted'],
            null
        ] ];
    }

    /**
     * @dataProvider responseProvider
     */
    public function testPublishMessageErrors($response, $expectedError)
    {
        $publisher = new MockedHttpPublisher('http://localhost:8080/messenger/');
        $msg = 'some message';
        $channels = ['anything'];

        $errors = 0;
        $publisher->response = $response;
        foreach($publisher->publishMessage($channels, $msg) as $url => $error) {
            $this->assertEquals($publisher->targets[0], $url);
            $this->assertEquals($expectedError, $error);
            $errors++;
        }
        $this->assertEquals($expectedError ? 1 : 0, $errors);
    }

}