<?php

declare(strict_types=1);

namespace App\Tests\Unit\Http;

use App\Infrastructure\Http\Middleware\InternalTokenMiddleware;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Psr7\Factory\ResponseFactory;
use Slim\Psr7\Factory\ServerRequestFactory;

/**
 * Token gate uses `hash_equals`, refuses an empty configured token, and
 * lets correct tokens through.
 */
final class InternalTokenMiddlewareTest extends TestCase
{
    public function testValidTokenPassesThrough(): void
    {
        $middleware = new InternalTokenMiddleware(new ResponseFactory(), 'shhh');
        [$handler, $response] = $this->dispatch($middleware, 'Bearer shhh');
        self::assertSame(204, $response->getStatusCode());
        self::assertTrue($handler->reached);
    }

    public function testWrongTokenIs401(): void
    {
        $middleware = new InternalTokenMiddleware(new ResponseFactory(), 'shhh');
        [$handler, $response] = $this->dispatch($middleware, 'Bearer wrong');
        self::assertSame(401, $response->getStatusCode());
        self::assertFalse($handler->reached);
    }

    public function testMissingHeaderIs401(): void
    {
        $middleware = new InternalTokenMiddleware(new ResponseFactory(), 'shhh');
        [$handler, $response] = $this->dispatch($middleware, null);
        self::assertSame(401, $response->getStatusCode());
        self::assertFalse($handler->reached);
    }

    public function testNonBearerSchemeIs401(): void
    {
        $middleware = new InternalTokenMiddleware(new ResponseFactory(), 'shhh');
        [$handler, $response] = $this->dispatch($middleware, 'Basic shhh');
        self::assertSame(401, $response->getStatusCode());
        self::assertFalse($handler->reached);
    }

    public function testEmptyConfiguredTokenFailsClosed(): void
    {
        $middleware = new InternalTokenMiddleware(new ResponseFactory(), '');
        [$handler, $response] = $this->dispatch($middleware, 'Bearer anything');
        self::assertSame(401, $response->getStatusCode());
        self::assertFalse($handler->reached);
    }

    /**
     * @return array{0: object, 1: ResponseInterface}
     */
    private function dispatch(InternalTokenMiddleware $middleware, ?string $auth): array
    {
        $request = (new ServerRequestFactory())->createServerRequest('POST', '/internal/jobs/tick');
        if ($auth !== null) {
            $request = $request->withHeader('Authorization', $auth);
        }

        $handler = new class () implements RequestHandlerInterface {
            public bool $reached = false;

            public function handle(ServerRequestInterface $request): ResponseInterface
            {
                $this->reached = true;

                return (new ResponseFactory())->createResponse(204);
            }
        };

        return [$handler, $middleware->process($request, $handler)];
    }
}
