<?php

declare(strict_types=1);

namespace App\Tests\Api\Controller;

use App\Entity\SkeletonPieceRequirement;
use App\Entity\SkeletonProductRequirement;
use App\Enum\ModelCategory;
use App\Tests\AbstractApiTestCase;

/**
 * @internal
 */
class ModelTypeSyncControllerTest extends AbstractApiTestCase
{
    // ── sync-preview ────────────────────────────────────────────────

    public function testPreviewReturnsNoImpactWhenNoItems(): void
    {
        $mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);

        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync-preview', [
            'json' => [
                'structure' => [
                    'pieces'        => [],
                    'products'      => [],
                    'subcomponents' => [],
                    'customFields'  => [],
                ],
            ],
        ]);

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(0, $data['itemCount']);
        $this->assertSame($mt->getId(), $data['modelTypeId']);
    }

    public function testPreviewDetectsNewSlots(): void
    {
        $mt        = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
        $pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
        $this->createComposant('C1', null, $mt);

        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync-preview', [
            'json' => [
                'structure' => [
                    'pieces'        => [['typePieceId' => $pieceType->getId(), 'position' => 0]],
                    'products'      => [],
                    'subcomponents' => [],
                    'customFields'  => [],
                ],
            ],
        ]);

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(1, $data['itemCount']);
        $this->assertSame(1, $data['additions']['pieceSlots']);
    }

    public function testPreview403ForViewer(): void
    {
        $mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);

        $client = $this->createViewerClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync-preview', [
            'json' => ['structure' => []],
        ]);

        $this->assertResponseStatusCodeSame(403);
    }

    public function testPreview401ForUnauthenticated(): void
    {
        $mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);

        $client = $this->createUnauthenticatedClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync-preview', [
            'json' => ['structure' => []],
        ]);

        $this->assertResponseStatusCodeSame(401);
    }

    public function testPreview404ForUnknownModelType(): void
    {
        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/nonexistent-id/sync-preview', [
            'json' => ['structure' => []],
        ]);

        $this->assertResponseStatusCodeSame(404);
    }

    // ── sync ────────────────────────────────────────────────────────

    public function testSyncAddsSlots(): void
    {
        $mt        = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
        $pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
        $this->createComposant('C1', null, $mt);

        // Add a skeleton requirement (simulates a PATCH that already happened)
        $em  = $this->getEntityManager();
        $req = new SkeletonPieceRequirement();
        $req->setModelType($mt);
        $req->setTypePiece($pieceType);
        $req->setPosition(0);
        $em->persist($req);
        $em->flush();

        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync', [
            'json' => [
                'confirmDeletions'   => false,
                'confirmTypeChanges' => false,
            ],
        ]);

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(1, $data['itemsUpdated']);
        $this->assertSame(1, $data['additions']['pieceSlots']);
    }

    public function testSyncDeletesSlotsWithConfirmation(): void
    {
        $mt        = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
        $pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
        $composant = $this->createComposant('C1', null, $mt);
        $this->createComposantPieceSlot($composant, $pieceType, null, 1, 0);

        // No skeleton requirements → slot is orphaned
        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync', [
            'json' => [
                'confirmDeletions'   => true,
                'confirmTypeChanges' => false,
            ],
        ]);

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(1, $data['deletions']['pieceSlots']);
    }

    public function testSyncSkipsDeletionsWithoutConfirmation(): void
    {
        $mt        = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
        $pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
        $composant = $this->createComposant('C1', null, $mt);
        $this->createComposantPieceSlot($composant, $pieceType, null, 1, 0);

        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync', [
            'json' => [
                'confirmDeletions'   => false,
                'confirmTypeChanges' => false,
            ],
        ]);

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(0, $data['deletions']['pieceSlots']);
    }

    public function testSync403ForViewer(): void
    {
        $mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);

        $client = $this->createViewerClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync', [
            'json' => ['confirmDeletions' => false, 'confirmTypeChanges' => false],
        ]);

        $this->assertResponseStatusCodeSame(403);
    }

    public function testSync404ForUnknownModelType(): void
    {
        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/nonexistent-id/sync', [
            'json' => ['confirmDeletions' => false, 'confirmTypeChanges' => false],
        ]);

        $this->assertResponseStatusCodeSame(404);
    }

    public function testSyncIsIdempotent(): void
    {
        $mt        = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
        $pieceType = $this->createModelType('Piece Type', 'PT-001', ModelCategory::PIECE);
        $this->createComposant('C1', null, $mt);

        $em  = $this->getEntityManager();
        $req = new SkeletonPieceRequirement();
        $req->setModelType($mt);
        $req->setTypePiece($pieceType);
        $req->setPosition(0);
        $em->persist($req);
        $em->flush();

        $client = $this->createGestionnaireClient();

        // First sync
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync', [
            'json' => ['confirmDeletions' => false, 'confirmTypeChanges' => false],
        ]);
        $this->assertResponseIsSuccessful();
        $data1 = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(1, $data1['itemsUpdated']);

        // Second sync — idempotent, no changes
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync', [
            'json' => ['confirmDeletions' => false, 'confirmTypeChanges' => false],
        ]);
        $this->assertResponseIsSuccessful();
        $data2 = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(0, $data2['itemsUpdated']);
    }

    public function testSyncWorksForPieceCategory(): void
    {
        $mt          = $this->createModelType('Piece Cat', 'PC-001', ModelCategory::PIECE);
        $productType = $this->createModelType('Prod Type', 'PD-001', ModelCategory::PRODUCT);
        $this->createPiece('P1', 'P1-REF', $mt);

        $em  = $this->getEntityManager();
        $req = new SkeletonProductRequirement();
        $req->setModelType($mt);
        $req->setTypeProduct($productType);
        $req->setPosition(0);
        $em->persist($req);
        $em->flush();

        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync', [
            'json' => ['confirmDeletions' => false, 'confirmTypeChanges' => false],
        ]);

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(1, $data['itemsUpdated']);
        $this->assertSame(1, $data['additions']['productSlots']);
    }

    public function testSyncWorksForProductCategory(): void
    {
        $mt = $this->createModelType('Prod Cat', 'PD-001', ModelCategory::PRODUCT);
        $this->createProduct('PR1', 'PR1-REF', $mt);
        $this->createCustomField('CF1', 'text', typeProduct: $mt, orderIndex: 0);

        $client = $this->createGestionnaireClient();
        $client->request('POST', '/api/model_types/'.$mt->getId().'/sync', [
            'json' => ['confirmDeletions' => false, 'confirmTypeChanges' => false],
        ]);

        $this->assertResponseIsSuccessful();
        $data = json_decode($client->getResponse()->getContent(), true);
        $this->assertSame(1, $data['itemsUpdated']);
        $this->assertSame(1, $data['additions']['customFieldValues']);
    }
}
