<?php

declare(strict_types=1);

namespace App\EventSubscriber;

use App\Entity\Composant;
use App\Entity\CustomFieldValue;
use App\Entity\Piece;
use App\Service\ReferenceAutoGenerator;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;

#[AsDoctrineListener(event: Events::onFlush)]
final class ReferenceAutoSubscriber
{
    public function __construct(private readonly ReferenceAutoGenerator $generator) {}

    public function onFlush(OnFlushEventArgs $args): void
    {
        $em  = $args->getObjectManager();
        $uow = $em->getUnitOfWork();

        /** @var array<string, Piece> */
        $piecesToRecalculate = [];

        /** @var array<string, Composant> */
        $composantsToRecalculate = [];

        foreach ($uow->getScheduledEntityInsertions() as $entity) {
            if ($entity instanceof Piece) {
                $piecesToRecalculate[$entity->getId()] = $entity;
            } elseif ($entity instanceof Composant) {
                $composantsToRecalculate[$entity->getId()] = $entity;
            }
        }

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if ($entity instanceof Piece) {
                $piecesToRecalculate[$entity->getId()] = $entity;
            } elseif ($entity instanceof Composant) {
                $composantsToRecalculate[$entity->getId()] = $entity;
            }
        }

        // For CFV insertions: the new CFV is not yet in the DB, so the lazy-loaded
        // collection won't contain it. We must add it manually so the generator sees it.
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
            if (!$entity instanceof CustomFieldValue) {
                continue;
            }
            if ($entity->getPiece()) {
                $piece = $entity->getPiece();
                if (!$piece->getCustomFieldValues()->contains($entity)) {
                    $piece->getCustomFieldValues()->add($entity);
                }
                $piecesToRecalculate[$piece->getId()] = $piece;
            } elseif ($entity->getComposant()) {
                $composant = $entity->getComposant();
                if (!$composant->getCustomFieldValues()->contains($entity)) {
                    $composant->getCustomFieldValues()->add($entity);
                }
                $composantsToRecalculate[$composant->getId()] = $composant;
            }
        }

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if (!$entity instanceof CustomFieldValue) {
                continue;
            }
            if ($entity->getPiece()) {
                $piece                                = $entity->getPiece();
                $piecesToRecalculate[$piece->getId()] = $piece;
            } elseif ($entity->getComposant()) {
                $composant                                    = $entity->getComposant();
                $composantsToRecalculate[$composant->getId()] = $composant;
            }
        }

        // For CFV deletions: remove from collection so the generator doesn't see stale values.
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
            if (!$entity instanceof CustomFieldValue) {
                continue;
            }
            if ($entity->getPiece()) {
                $piece = $entity->getPiece();
                $piece->getCustomFieldValues()->removeElement($entity);
                $piecesToRecalculate[$piece->getId()] = $piece;
            } elseif ($entity->getComposant()) {
                $composant = $entity->getComposant();
                $composant->getCustomFieldValues()->removeElement($entity);
                $composantsToRecalculate[$composant->getId()] = $composant;
            }
        }

        $pieceMeta     = $em->getClassMetadata(Piece::class);
        $composantMeta = $em->getClassMetadata(Composant::class);

        foreach ($piecesToRecalculate as $piece) {
            $newRef = $this->generator->generate($piece);
            if ($piece->getReferenceAuto() !== $newRef) {
                $piece->setReferenceAuto($newRef);
                $uow->recomputeSingleEntityChangeSet($pieceMeta, $piece);
            }
        }

        foreach ($composantsToRecalculate as $composant) {
            $newRef = $this->generator->generate($composant);
            if ($composant->getReferenceAuto() !== $newRef) {
                $composant->setReferenceAuto($newRef);
                $uow->recomputeSingleEntityChangeSet($composantMeta, $composant);
            }
        }
    }
}
