<?php

declare(strict_types=1);

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Tests\Commerce\Catalog\Api\OpenApi;

use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\Model\Components;
use ApiPlatform\OpenApi\Model\Info;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\OpenApi\Model\PathItem;
use ApiPlatform\OpenApi\Model\Paths;
use ApiPlatform\OpenApi\OpenApi;
use CpCreation\VitiCore\Commerce\Catalog\Api\OpenApi\CommerceOpenApiFactory;
use PHPUnit\Framework\TestCase;

final class CommerceOpenApiFactoryTest extends TestCase
{
    public function testBearerSchemeIsAppliedOnlyToCommerceOperations(): void
    {
        $paths = new Paths();
        $paths->addPath(
            '/api/commerce/v1/products',
            new PathItem(
                get: new Operation(operationId: 'commerce_products_get'),
                post: new Operation(operationId: 'commerce_products_post'),
            ),
        );
        $paths->addPath(
            '/api/products',
            new PathItem(get: new Operation(operationId: 'products_get')),
        );
        $factory = new CommerceOpenApiFactory(new class ($paths) implements OpenApiFactoryInterface {
            public function __construct(private readonly Paths $paths) {}

            public function __invoke(array $context = []): OpenApi
            {
                return new OpenApi(new Info('Test', '1.0.0'), [], $this->paths);
            }
        });

        $openApi = $factory();

        $scheme = $openApi->getComponents()->getSecuritySchemes()?->offsetGet(
            CommerceOpenApiFactory::SECURITY_SCHEME,
        );
        self::assertNotNull($scheme);
        self::assertSame('http', $scheme->getType());
        self::assertSame('bearer', $scheme->getScheme());
        self::assertSame('opaque', $scheme->getBearerFormat());
        self::assertSame(
            [[CommerceOpenApiFactory::SECURITY_SCHEME => []]],
            $openApi->getPaths()->getPath('/api/commerce/v1/products')?->getGet()?->getSecurity(),
        );
        self::assertSame(
            [[CommerceOpenApiFactory::SECURITY_SCHEME => []]],
            $openApi->getPaths()->getPath('/api/commerce/v1/products')?->getPost()?->getSecurity(),
        );
        self::assertNull($openApi->getPaths()->getPath('/api/products')?->getGet()?->getSecurity());
        self::assertSame([], $openApi->getSecurity());
        $unauthorized = $openApi->getPaths()->getPath('/api/commerce/v1/products')
            ?->getGet()?->getResponses()['401'] ?? null;
        self::assertNotNull($unauthorized);
        self::assertSame('Jeton bearer absent ou invalide.', $unauthorized->getDescription());
        self::assertArrayHasKey('application/problem+json', $unauthorized->getContent() ?? []);
        self::assertArrayNotHasKey('401', $openApi->getPaths()->getPath('/api/products')?->getGet()?->getResponses() ?? []);
    }

    public function testEveryCatalogSchemaPropertyIsDocumentedAsRequired(): void
    {
        $catalogSchema = new \ArrayObject([
            'type' => 'object',
            'properties' => new \ArrayObject([
                'id' => ['type' => 'string'],
                'nullable_value' => ['type' => ['string', 'null']],
            ]),
        ]);
        $unrelatedSchema = [
            'type' => 'object',
            'properties' => new \ArrayObject([
                'id' => ['type' => 'string'],
            ]),
        ];
        $components = new Components(new \ArrayObject([
            'CatalogProductResource' => $catalogSchema,
            'UnrelatedResource' => $unrelatedSchema,
        ]));
        $factory = new CommerceOpenApiFactory(new class ($components) implements OpenApiFactoryInterface {
            public function __construct(private readonly Components $components) {}

            public function __invoke(array $context = []): OpenApi
            {
                return new OpenApi(new Info('Test', '1.0.0'), [], new Paths(), $this->components);
            }
        });

        $schemas = $factory()->getComponents()->getSchemas();

        self::assertSame(
            ['id', 'nullable_value'],
            $schemas?->offsetGet('CatalogProductResource')['required'] ?? null,
        );
        self::assertArrayNotHasKey('required', $schemas?->offsetGet('UnrelatedResource') ?? []);
    }
}
