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

namespace IMATHUZH\Messenger\Tests\Rest;

use IMATHUZH\Messenger\Rest\RestProcessor;
use IMATHUZH\Messenger\Rest\RestRequest;
use IMATHUZH\Messenger\Rest\ServerRequestInterface;
use PHPUnit\Framework\TestCase;

class TestedRestProcessor extends RestProcessor
{
    public int $responseCode = 0;
    public array $responseBody = [];

    protected function output(int $httpCode, array $response): void
    {
        $this->responseCode = $httpCode;
        $this->responseBody = $response;
    }
}

class RestProcessorTest extends TestCase
{
    public function testSuccessfulRouting(): void
    {
        $processor = new TestedRestProcessor('/', [
            '/' => ['GET' => fn() => "root"],
            '/abc/' => ['GET' => fn() => "abc"]
        ]);

        $processor->process(new RestRequest('/abc/', 'GET'));
        $this->assertEquals(200, $processor->responseCode);
        $this->assertEquals("abc", $processor->responseBody['data']);

        $processor->process(new RestRequest('/', 'GET'));
        $this->assertEquals(200, $processor->responseCode);
        $this->assertEquals("root", $processor->responseBody['data']);
    }

    public function testRoutingWithArguments(): void
    {
        $processor = new TestedRestProcessor('/api', [
            '/abc/' => ['GET' => fn(ServerRequestInterface $req) => $req->getParams()]
        ]);

        $processor->process(new RestRequest('/api/abc/def/ghi', 'GET'));
        $this->assertEquals(200, $processor->responseCode);
        // Expected args: def, ghi
        // Note: RestProcessor extracts arguments by splitting the part after the slug
        $this->assertEquals(['def', 'ghi'], $processor->responseBody['data']);
    }

    public function testNotFound(): void
    {
        $processor = new TestedRestProcessor('/', [
            '/abc/' => ['GET' => fn() => "abc"]
        ]);

        $processor->process(new RestRequest('/xyz/', 'GET'));
        $this->assertEquals(404, $processor->responseCode);
        $this->assertFalse($processor->responseBody['success']);
    }

    public function testInvalidMethod(): void
    {
        $processor = new TestedRestProcessor('/', [
            '/abc/' => ['GET' => fn() => "abc"]
        ]);

        $processor->process(new RestRequest('/abc/', 'POST'));
        // findHandler checks for method existence, so if not found it returns empty match -> 404
        // Wait, if it matches the slug but not the method, findHandler returns empty string?
        // Let's check findHandler: it requires isset($action[$method])
        $this->assertEquals(404, $processor->responseCode);
    }

    public function testBaseUrlMismatch(): void
    {
        $processor = new TestedRestProcessor('/api', []);
        $processor->process(new RestRequest('/other/abc', 'GET'));
        $this->assertEquals(405, $processor->responseCode);
        $this->assertEquals('invalid endpoint', $processor->responseBody['reason']);
    }

    public function testRuntimeExceptionHandling(): void
    {
        $processor = new TestedRestProcessor('/', [
            '/' => ['GET' => function(ServerRequestInterface $req) {
                $req->reject("Bad request", 400);
            }]
        ]);

        $processor->process(new RestRequest('/', 'GET'));
        $this->assertEquals(400, $processor->responseCode);
        $this->assertEquals("Bad request", $processor->responseBody['reason']);
    }

    public function testGenericExceptionHandling(): void
    {
        $processor = new TestedRestProcessor('/', [
            '/' => ['GET' => function() {
                throw new \Exception("Internal error");
            }]
        ]);

        $processor->process(new RestRequest('/', 'GET'));
        $this->assertEquals(500, $processor->responseCode);
        $this->assertEquals("Internal error", $processor->responseBody['reason']);
    }
}
