<?php

declare(strict_types=1);

namespace App\Tests\Module\Transport\Api;

use ApiPlatform\Symfony\Bundle\Test\Client;
use App\Module\Core\Infrastructure\DataFixtures\RbacDemoFixtures;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;

/**
 * Matrice RBAC du repertoire transporteurs par role metier (spec-back M4 § 5.2 +
 * ERP-153/158). Valide 200/403 par verbe pour bureau / compta / commerciale /
 * usine ; l'archivage reste admin seul (gating CarrierProcessor, RG-4.14). Jumeau
 * de SupplierRBACMatrixTest (M2).
 *
 * Matrice § 5.2 — rappel :
 *  - bureau      : view + manage (PAS archive)
 *  - commerciale : view seul (ni manage ni archive)
 *  - compta      : aucun acces (403 sur view ET manage)
 *  - usine       : aucun acces (403 partout)
 *  - archive     : admin seul
 *
 * @internal
 */
final class CarrierRBACMatrixTest extends AbstractCarrierApiTestCase
{
    private const string PWD = RbacDemoFixtures::DEMO_PASSWORD;

    protected function setUp(): void
    {
        parent::setUp();

        // Seed idempotent via la commande applicative (roles + matrice § 5.2 +
        // comptes demo) — meme chemin qu'en recette.
        self::bootKernel();
        $application = new Application(self::$kernel);
        $application->setAutoExit(false);
        $exit = $application->run(
            new ArrayInput([
                'command'           => 'app:seed-rbac',
                '--with-demo-users' => true,
                '--password'        => self::PWD,
            ]),
            new NullOutput(),
        );
        self::assertSame(
            0,
            $exit,
            'app:seed-rbac a echoue : les permissions transport.carriers.* sont-elles synchronisees (app:sync-permissions) ?',
        );

        self::ensureKernelShutdown();
    }

    public function testUsineIsForbiddenEverywhere(): void
    {
        $seed   = $this->seedCarrier('Usine Target');
        $client = $this->authAs('usine');

        $client->request('GET', '/api/carriers', ['headers' => ['Accept' => self::LD]]);
        self::assertResponseStatusCodeSame(403);

        $client->request('GET', '/api/carriers/'.$seed->getId(), ['headers' => ['Accept' => self::LD]]);
        self::assertResponseStatusCodeSame(403);

        $client->request('POST', '/api/carriers', [
            'headers' => ['Content-Type' => self::LD],
            'json'    => $this->validMainPayload('Usine Post'),
        ]);
        self::assertResponseStatusCodeSame(403);
    }

    public function testComptaHasNoAccess(): void
    {
        $seed   = $this->seedCarrier('Compta Target');
        $client = $this->authAs('compta');

        // PAS view (matrice § 5.2 : Compta sans acces transporteurs).
        $client->request('GET', '/api/carriers', ['headers' => ['Accept' => self::LD]]);
        self::assertResponseStatusCodeSame(403);

        // PAS manage : creation refusee.
        $client->request('POST', '/api/carriers', [
            'headers' => ['Content-Type' => self::LD],
            'json'    => $this->validMainPayload('Compta Post'),
        ]);
        self::assertResponseStatusCodeSame(403);

        $client->request('PATCH', '/api/carriers/'.$seed->getId(), [
            'headers' => ['Content-Type' => self::MERGE],
            'json'    => ['name' => 'Renamed By Compta'],
        ]);
        self::assertResponseStatusCodeSame(403);
    }

    public function testBureauHasViewAndManageButNoArchive(): void
    {
        $seed   = $this->seedCarrier('Bureau Target');
        $client = $this->authAs('bureau');

        // view
        $client->request('GET', '/api/carriers', ['headers' => ['Accept' => self::LD]]);
        self::assertResponseStatusCodeSame(200);

        // manage : creation OK
        $client->request('POST', '/api/carriers', [
            'headers' => ['Content-Type' => self::LD],
            'json'    => $this->validMainPayload('Bureau Created'),
        ]);
        self::assertResponseStatusCodeSame(201);

        // manage : edition formulaire principal OK
        $client->request('PATCH', '/api/carriers/'.$seed->getId(), [
            'headers' => ['Content-Type' => self::MERGE],
            'json'    => ['name' => 'Bureau Renamed'],
        ]);
        self::assertResponseStatusCodeSame(200);

        // PAS archive : archivage refuse (RG-4.14, gating CarrierProcessor).
        $client->request('PATCH', '/api/carriers/'.$seed->getId(), [
            'headers' => ['Content-Type' => self::MERGE],
            'json'    => ['isArchived' => true],
        ]);
        self::assertResponseStatusCodeSame(403);
    }

    public function testCommercialeHasViewOnly(): void
    {
        $seed   = $this->seedCarrier('Commerciale Target');
        $client = $this->authAs('commerciale');

        // view (consultation « Tout »)
        $client->request('GET', '/api/carriers', ['headers' => ['Accept' => self::LD]]);
        self::assertResponseStatusCodeSame(200);

        // PAS manage : creation refusee
        $client->request('POST', '/api/carriers', [
            'headers' => ['Content-Type' => self::LD],
            'json'    => $this->validMainPayload('Commerciale Post'),
        ]);
        self::assertResponseStatusCodeSame(403);

        // PAS manage : edition refusee
        $client->request('PATCH', '/api/carriers/'.$seed->getId(), [
            'headers' => ['Content-Type' => self::MERGE],
            'json'    => ['name' => 'Renamed By Commerciale'],
        ]);
        self::assertResponseStatusCodeSame(403);
    }

    private function authAs(string $role): Client
    {
        return $this->authenticatedClient($role, self::PWD);
    }
}
