<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency;

use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Doctrine\ORM\Event\PrePersistEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;

/**
 * This service listens for preUpdate and prePersist events on some entities
 * and ensure consistency of SocialIssue with an associated AccompanyingPeriod.
 *
 * The entity must implements interface Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface.
 *
 * This subscriber is not called automatically: for performance reasons, this
 * apply only on entities which are configured. See https://symfony.com/doc/4.4/doctrine/events.html#doctrine-entity-listeners
 */
final class AccompanyingPeriodSocialIssueConsistencyEntityListener
{
    public function prePersist(AccompanyingPeriodLinkedWithSocialIssuesEntityInterface $entity, PrePersistEventArgs $eventArgs)
    {
        $this->ensureConsistencyEntity($entity);
    }

    public function prePersistAccompanyingPeriod(AccompanyingPeriod $period, PrePersistEventArgs $eventArgs)
    {
        $this->ensureConsistencyAccompanyingPeriod($period);
    }

    public function preUpdate(AccompanyingPeriodLinkedWithSocialIssuesEntityInterface $entity, PreUpdateEventArgs $eventArgs)
    {
        $this->ensureConsistencyEntity($entity);
    }

    public function preUpdateAccompanyingPeriod(AccompanyingPeriod $period, PreUpdateEventArgs $eventArgs)
    {
        $this->ensureConsistencyAccompanyingPeriod($period);
    }

    private function ensureConsistencyAccompanyingPeriod(AccompanyingPeriod $period): void
    {
        $ancestors = SocialIssue::findAncestorSocialIssues($period->getSocialIssues());

        foreach ($ancestors as $ancestor) {
            $period->removeSocialIssue($ancestor);
        }
    }

    private function ensureConsistencyEntity(AccompanyingPeriodLinkedWithSocialIssuesEntityInterface $entity): void
    {
        if (null === $period = $entity->getAccompanyingPeriod()) {
            return;
        }
        // remove issues parents on the entity itself
        $ancestors = SocialIssue::findAncestorSocialIssues($entity->getSocialIssues());

        foreach ($ancestors as $ancestor) {
            $entity->removeSocialIssue($ancestor);
        }

        foreach ($entity->getSocialIssues() as $issue) {
            // the entity itself test if the social issue is already associated, or not
            $period->addSocialIssue($issue);
        }

        $this->ensureConsistencyAccompanyingPeriod($period);
    }
}
