<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Doctrine;

use App\Shared\Domain\Contract\BlamableInterface;
use App\Shared\Domain\Contract\TimestampableInterface;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\Event\PrePersistEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Events;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * Listener Doctrine global qui remplit automatiquement les colonnes
 * Timestampable + Blamable.
 *
 * Pattern aligne sur AuditListener (cf.
 * src/Module/Core/Infrastructure/Doctrine/AuditListener.php) : declare via
 * #[AsDoctrineListener], auto-wire par le DoctrineBundle.
 *
 * Regle Blamable : si aucun utilisateur n'est authentifie (CLI, cron,
 * migration), les FK `created_by` / `updated_by` restent a null. L'affichage
 * front gere le libelle « Systeme » pour null.
 */
#[AsDoctrineListener(event: Events::prePersist)]
#[AsDoctrineListener(event: Events::preUpdate)]
final class TimestampableBlamableSubscriber
{
    // L'horloge est injectee (et non un `new DateTimeImmutable()` direct) pour
    // que les tests puissent figer/avancer le temps de facon deterministe via
    // ClockSensitiveTrait (cf. ERP-98). En prod, le service `clock` delegue a
    // l'horloge systeme reelle.
    public function __construct(
        private readonly Security $security,
        private readonly ClockInterface $clock,
    ) {}

    public function prePersist(PrePersistEventArgs $args): void
    {
        $entity = $args->getObject();
        $now    = $this->clock->now();
        $user   = $this->security->getUser();

        if ($entity instanceof TimestampableInterface) {
            $entity->setCreatedAt($now);
            $entity->setUpdatedAt($now);
        }

        if ($entity instanceof BlamableInterface && $user instanceof UserInterface) {
            $entity->setCreatedBy($user);
            $entity->setUpdatedBy($user);
        }
    }

    public function preUpdate(PreUpdateEventArgs $args): void
    {
        $entity = $args->getObject();
        $user   = $this->security->getUser();

        if ($entity instanceof TimestampableInterface) {
            $entity->setUpdatedAt($this->clock->now());
        }

        if ($entity instanceof BlamableInterface && $user instanceof UserInterface) {
            $entity->setUpdatedBy($user);
        }
    }
}
