<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Tests\Repository;

use Ferienpass\CoreBundle\Entity\Host;
use Ferienpass\CoreBundle\Entity\Offer;
use Ferienpass\CoreBundle\Fixtures\Doctrine\OrmTestCase;

/**
 * An offer references its hosts and its participant-list recipients rather than owning
 * them, so a copy has to carry the very same rows over — but in a collection of its own.
 *
 * Recipients had no filter at all and fell through to the catch-all collection filter,
 * which deep-copies every element: flushing a copied offer failed outright with "A new
 * entity was found through the relationship Offer#participantListRecipients". Hosts were
 * kept, which does reference the same rows but hands the copy the original's
 * PersistentCollection — the copy route renders an edit form straight onto that copy, so
 * adding a host there was flushed onto the original offer as well.
 */
class OfferCopyReferencesTest extends OrmTestCase
{
    public function testCopyCarriesTheManagedRecipientsOver(): void
    {
        $user = $this->createUser();
        $offer = $this->createOffer();
        $offer->addParticipantListRecipient($user);
        $this->em->flush();

        $copy = $this->em->getRepository(Offer::class)->createCopy($offer);

        self::assertCount(1, $copy->getParticipantListRecipients());
        self::assertSame($user, $copy->getParticipantListRecipients()->first());
    }

    public function testCopyWithRecipientsCanBeFlushed(): void
    {
        $user = $this->createUser();
        $offer = $this->createOffer();
        $offer->addParticipantListRecipient($user);
        $this->em->flush();

        $copy = $this->em->getRepository(Offer::class)->createCopy($offer);
        $this->em->persist($copy);
        $this->em->flush();

        $copyId = $copy->getId();
        $this->em->clear();

        $reloaded = $this->em->find(Offer::class, $copyId);
        self::assertCount(1, $reloaded->getParticipantListRecipients());
        self::assertSame($user->getId(), $reloaded->getParticipantListRecipients()->first()->getId());
    }

    public function testEditingTheCopysRecipientsLeavesTheOriginalAlone(): void
    {
        $user = $this->createUser();
        $other = $this->createUser('other@example.com');
        $offer = $this->createOffer();
        $offer->addParticipantListRecipient($user);
        $this->em->flush();

        $copy = $this->em->getRepository(Offer::class)->createCopy($offer);
        $copy->removeParticipantListRecipient($user);
        $copy->addParticipantListRecipient($other);

        $this->em->persist($copy);
        $this->em->flush();

        $offerId = $offer->getId();
        $copyId = $copy->getId();
        $this->em->clear();

        $recipients = $this->em->find(Offer::class, $offerId)->getParticipantListRecipients();
        self::assertCount(1, $recipients);
        self::assertSame($user->getId(), $recipients->first()->getId());

        $copied = $this->em->find(Offer::class, $copyId)->getParticipantListRecipients();
        self::assertCount(1, $copied);
        self::assertSame($other->getId(), $copied->first()->getId());
    }

    public function testEditingTheCopysHostsLeavesTheOriginalAlone(): void
    {
        $host = $this->createHost('Host A', 'host-a');
        $other = $this->createHost('Host B', 'host-b');

        $offer = $this->createOffer();
        $offer->addHost($host);
        $this->em->flush();

        $copy = $this->em->getRepository(Offer::class)->createCopy($offer);
        self::assertSame($host, $copy->getHosts()->first());

        $copy->addHost($other);

        $this->em->persist($copy);
        $this->em->flush();

        $offerId = $offer->getId();
        $copyId = $copy->getId();
        $this->em->clear();

        $hosts = $this->em->find(Offer::class, $offerId)->getHosts();
        self::assertCount(1, $hosts);
        self::assertSame($host->getId(), $hosts->first()->getId());

        self::assertCount(2, $this->em->find(Offer::class, $copyId)->getHosts());
    }

    private function createHost(string $name, string $alias): Host
    {
        $host = new Host();
        $host->setName($name);
        $host->setAlias($alias);

        $this->em->persist($host);

        return $host;
    }
}
