<?php

declare(strict_types=1);

namespace App\Tests\Api\Service;

use App\DTO\SyncConfirmation;
use App\Entity\SkeletonPieceRequirement;
use App\Enum\ModelCategory;
use App\Service\Sync\ComposantSyncStrategy;
use App\Tests\AbstractApiTestCase;

/**
 * @internal
 */
class ComposantSyncStrategyTest extends AbstractApiTestCase
{
    private ComposantSyncStrategy $strategy;

    protected function setUp(): void
    {
        parent::setUp();
        $this->strategy = static::getContainer()->get(ComposantSyncStrategy::class);
    }

    public function testSupportsComponentCategory(): void
    {
        $mt = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
        $this->assertTrue($this->strategy->supports($mt));
    }

    public function testPreviewNoImpactWhenNoComposants(): void
    {
        $mt     = $this->createModelType('Comp Cat', 'CC-001', ModelCategory::COMPONENT);
        $result = $this->strategy->preview($mt, ['pieces' => [], 'products' => [], 'subcomponents' => [], 'customFields' => []]);
        $this->assertSame(0, $result->itemCount);
        $this->assertFalse($result->hasImpact());
    }

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

        $result = $this->strategy->preview($mt, [
            'pieces'        => [['typePieceId' => $pieceType->getId(), 'position' => 0]],
            'products'      => [],
            'subcomponents' => [],
            'customFields'  => [],
        ]);

        $this->assertSame(1, $result->itemCount);
        $this->assertSame(1, $result->additions['pieceSlots']);
    }

    public function testPreviewDetectsSlotDeletion(): 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);

        $result = $this->strategy->preview($mt, [
            'pieces'        => [],
            'products'      => [],
            'subcomponents' => [],
            'customFields'  => [],
        ]);

        $this->assertSame(1, $result->deletions['pieceSlots']);
    }

    public function testPreviewNoImpactWhenSlotsMatch(): 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);

        $result = $this->strategy->preview($mt, [
            'pieces'        => [['typePieceId' => $pieceType->getId(), 'position' => 0]],
            'products'      => [],
            'subcomponents' => [],
            'customFields'  => [],
        ]);

        $this->assertFalse($result->hasImpact());
    }

    public function testExecuteAddsMissingSlots(): 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);

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

        $result = $this->strategy->execute($mt, new SyncConfirmation());

        $this->assertSame(1, $result->itemsUpdated);
        $this->assertSame(1, $result->additions['pieceSlots']);

        $em->refresh($composant);
        $this->assertSame(2, $composant->getVersion());
    }

    public function testExecutePreservesExistingSelections(): 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);
        $piece     = $this->createPiece('P1', 'P1-REF', $pieceType);
        $slot      = $this->createComposantPieceSlot($composant, $pieceType, $piece, 5, 0);

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

        $result = $this->strategy->execute($mt, new SyncConfirmation());

        // No changes — slot already matches
        $this->assertSame(0, $result->itemsUpdated);

        // Selection and quantity preserved
        $em->refresh($slot);
        $this->assertSame($piece->getId(), $slot->getSelectedPiece()->getId());
        $this->assertSame(5, $slot->getQuantity());
    }

    public function testExecuteDeletesSlotsOnlyWithConfirmation(): 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 should be deleted
        $result = $this->strategy->execute($mt, new SyncConfirmation(confirmDeletions: false));
        $this->assertSame(0, $result->deletions['pieceSlots']);

        // With confirmation
        $result = $this->strategy->execute($mt, new SyncConfirmation(confirmDeletions: true));
        $this->assertSame(1, $result->deletions['pieceSlots']);
    }

    public function testExecuteIsIdempotent(): 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);

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

        $result1 = $this->strategy->execute($mt, new SyncConfirmation());
        $this->assertSame(1, $result1->additions['pieceSlots']);

        $em->refresh($composant);
        $result2 = $this->strategy->execute($mt, new SyncConfirmation());
        $this->assertSame(0, $result2->itemsUpdated);
    }
}
