<?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\Tests\AccompanyingPeriod\SocialIssueConsistency;

use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\PrePersistEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * @internal
 *
 * @coversNothing
 */
final class AccompanyingPeriodSocialIssueConsistencyEntityListenerTest extends TestCase
{
    use ProphecyTrait;

    public function testPrePersist()
    {
        $socialIssues = new ArrayCollection([
            $parent = new SocialIssue(),
            $child = (new SocialIssue())->setParent($parent),
            $grandChild = (new SocialIssue())->setParent($child),
            $grandGrandChild = (new SocialIssue())->setParent($grandChild),
        ]);
        $period = (new AccompanyingPeriod())->addSocialIssue($unrelated = new SocialIssue());
        $entity = $this->generateClass($period, $socialIssues);
        $consistency = new AccompanyingPeriodSocialIssueConsistencyEntityListener();

        $consistency->prePersist($entity, $this->generateLifecycleArgs($period, Step::PrePersist));

        $this->assertCount(2, $period->getSocialIssues());
        $this->assertContains($grandGrandChild, $period->getSocialIssues());
        $this->assertContains($unrelated, $period->getSocialIssues());

        $this->assertCount(1, $entity->getSocialIssues());
        $this->assertContains($grandGrandChild, $entity->getSocialIssues());
    }

    public function testPrePersistAccompanyingPeriod()
    {
        $arraySocialIssues = [
            $parent = new SocialIssue(),
            $child = (new SocialIssue())->setParent($parent),
            $grandChild = (new SocialIssue())->setParent($child),
            $grandGrandChild = (new SocialIssue())->setParent($grandChild),
        ];
        $period = new AccompanyingPeriod();
        $consistency = new AccompanyingPeriodSocialIssueConsistencyEntityListener();

        foreach ($arraySocialIssues as $issue) {
            $period->addSocialIssue($issue);
        }

        $consistency->prePersistAccompanyingPeriod($period, $this->generateLifecycleArgs($period, Step::PrePersist));

        $this->assertCount(1, $period->getSocialIssues());
        $this->assertSame($grandGrandChild, $period->getSocialIssues()->first());
    }

    public function testPreUpdate()
    {
        $socialIssues = new ArrayCollection([
            $parent = new SocialIssue(),
            $child = (new SocialIssue())->setParent($parent),
            $grandChild = (new SocialIssue())->setParent($child),
            $grandGrandChild = (new SocialIssue())->setParent($grandChild),
        ]);
        $period = (new AccompanyingPeriod())->addSocialIssue($unrelated = new SocialIssue());
        $entity = $this->generateClass($period, $socialIssues);
        $consistency = new AccompanyingPeriodSocialIssueConsistencyEntityListener();

        $consistency->preUpdate($entity, $this->generateLifecycleArgs($period, Step::PreUpdate));

        $this->assertCount(2, $period->getSocialIssues());
        $this->assertContains($grandGrandChild, $period->getSocialIssues());
        $this->assertContains($unrelated, $period->getSocialIssues());

        $this->assertCount(1, $entity->getSocialIssues());
        $this->assertContains($grandGrandChild, $entity->getSocialIssues());
    }

    public function testPreUpdateAccompanyingPeriod()
    {
        $arraySocialIssues = [
            $parent = new SocialIssue(),
            $child = (new SocialIssue())->setParent($parent),
            $grandChild = (new SocialIssue())->setParent($child),
            $grandGrandChild = (new SocialIssue())->setParent($grandChild),
        ];
        $period = new AccompanyingPeriod();
        $consistency = new AccompanyingPeriodSocialIssueConsistencyEntityListener();

        foreach ($arraySocialIssues as $issue) {
            $period->addSocialIssue($issue);
        }

        $consistency->prePersistAccompanyingPeriod($period, $this->generateLifecycleArgs($period, Step::PrePersist));

        $this->assertCount(1, $period->getSocialIssues());
        $this->assertSame($grandGrandChild, $period->getSocialIssues()->first());
    }

    protected function generateClass(AccompanyingPeriod $period, Collection $socialIssues): AccompanyingPeriodLinkedWithSocialIssuesEntityInterface
    {
        return new class ($period, $socialIssues) implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface {
            public function __construct(public $period, public $socialIssues) {}

            public function getAccompanyingPeriod(): AccompanyingPeriod
            {
                return $this->period;
            }

            public function getSocialIssues(): Collection
            {
                return $this->socialIssues;
            }

            public function removeSocialIssue(SocialIssue $issue): AccompanyingPeriodLinkedWithSocialIssuesEntityInterface
            {
                $this->socialIssues->removeElement($issue);

                return $this;
            }
        };
    }

    protected function generateLifecycleArgs(AccompanyingPeriod $period, Step $step): PrePersistEventArgs|PreUpdateEventArgs
    {
        $entityManager = $this->prophesize(EntityManagerInterface::class);

        $dummyChanges = [];

        return match ($step) {
            Step::PrePersist => new PrePersistEventArgs($period, $entityManager->reveal()),
            Step::PreUpdate => new PreUpdateEventArgs($period, $entityManager->reveal(), $dummyChanges),
        };
    }
}

enum Step
{
    case PrePersist;
    case PreUpdate;
}
