<?php

declare(strict_types=1);

namespace App\Tests\Api\Entity;

use App\Entity\Piece;
use App\Enum\ModelCategory;
use App\Tests\AbstractApiTestCase;

/**
 * @internal
 */
class PieceTest extends AbstractApiTestCase
{
    public function testGetCollection(): void
    {
        $this->createPiece('Joint A');
        $this->createPiece('Joint B');

        $client = $this->createViewerClient();
        $client->request('GET', '/api/pieces');

        $this->assertResponseIsSuccessful();
        $this->assertJsonContainsHydraCollection();
        $this->assertJsonContains(['totalItems' => 2]);
    }

    public function testGetItem(): void
    {
        $p = $this->createPiece('Joint A', 'REF-J001');

        $client = $this->createViewerClient();
        $client->request('GET', self::iri('pieces', $p->getId()));

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains([
            'name'      => 'Joint A',
            'reference' => 'REF-J001',
        ]);
    }

    public function testPost(): void
    {
        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/pieces', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'      => 'Nouvelle pièce',
                'reference' => 'REF-NEW',
            ],
        ]);

        $this->assertResponseStatusCodeSame(201);
        $this->assertJsonContains(['name' => 'Nouvelle pièce']);
    }

    public function testPostWithType(): void
    {
        $mt = $this->createModelType('Joint', 'JOINT-001', ModelCategory::PIECE);

        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/pieces', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'      => 'Pièce typée',
                'typePiece' => self::iri('model_types', $mt->getId()),
            ],
        ]);

        $this->assertResponseStatusCodeSame(201);
    }

    public function testPut(): void
    {
        $p = $this->createPiece('Joint A');

        $client = $this->createGestionnaireClient();
        $client->request('PUT', self::iri('pieces', $p->getId()), [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => ['name' => 'Joint A Renommé'],
        ]);

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['name' => 'Joint A Renommé']);
    }

    public function testPatch(): void
    {
        $p = $this->createPiece('Joint A');

        $client = $this->createGestionnaireClient();
        $client->request('PATCH', self::iri('pieces', $p->getId()), [
            'headers' => ['Content-Type' => 'application/merge-patch+json'],
            'json'    => ['prix' => '15.50'],
        ]);

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['prix' => '15.50']);
    }

    public function testDelete(): void
    {
        $p = $this->createPiece('ToDelete');

        $client = $this->createGestionnaireClient();
        $client->request('DELETE', self::iri('pieces', $p->getId()));

        $this->assertResponseStatusCodeSame(204);
    }

    public function testUnauthenticatedAccess(): void
    {
        $client = $this->createUnauthenticatedClient();
        $client->request('GET', '/api/pieces');

        $this->assertResponseStatusCodeSame(401);
    }

    public function testViewerCannotWrite(): void
    {
        $client = $this->createViewerClient();
        $client->request('POST', '/api/pieces', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => ['name' => 'Blocked'],
        ]);

        $this->assertResponseStatusCodeSame(403);
    }

    public function testUniqueReferenceConstraint(): void
    {
        $this->createPiece('Joint A', 'REF-UNIQUE');

        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/pieces', [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'      => 'Joint B',
                'reference' => 'REF-UNIQUE',
            ],
        ]);

        $this->assertResponseStatusCodeSame(422);
    }

    public function testGetItemReturnsProductIds(): void
    {
        $product1 = $this->createProduct('Huile A', 'HUILE-A');
        $product2 = $this->createProduct('Graisse B', 'GRAISSE-B');

        $piece = $this->createPiece('Joint avec produits', 'REF-JP');
        $piece->addProduct($product1);
        $piece->addProduct($product2);

        $em = $this->getEntityManager();
        $em->persist($piece);
        $em->flush();

        $client = $this->createViewerClient();
        $client->request('GET', self::iri('pieces', $piece->getId()));

        $this->assertResponseIsSuccessful();
        $data = $client->getResponse()->toArray();

        $this->assertArrayHasKey('productIds', $data);
        $this->assertCount(2, $data['productIds']);
        $this->assertContains($product1->getId(), $data['productIds']);
        $this->assertContains($product2->getId(), $data['productIds']);
    }

    public function testPieceProductRelationSurvivesRefetch(): void
    {
        $product = $this->createProduct('Huile', 'HUILE-REL');
        $piece   = $this->createPiece('Joint relation', 'REF-REL');
        $piece->addProduct($product);

        $em = $this->getEntityManager();
        $em->persist($piece);
        $em->flush();
        $em->clear();

        // Re-fetch the piece to verify relation persisted
        $refetched = $em->find(Piece::class, $piece->getId());
        $this->assertNotNull($refetched);
        $this->assertCount(1, $refetched->getProducts());
        $this->assertSame($product->getId(), $refetched->getProducts()->first()->getId());
        $this->assertSame([$product->getId()], $refetched->getProductIds());
    }
}
