<?php

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Tests\VerifyDeprecations;

class DDC729Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
    use VerifyDeprecations;

    protected function setUp() : void
    {
        parent::setUp();

        try {
            $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($this->_em);
            $schemaTool->createSchema(
                [
                $this->_em->getClassMetadata(DDC729A::class),
                $this->_em->getClassMetadata(DDC729B::class),
                ]
            );
        } catch(\Exception $e) {

        }
    }

    /** @after */
    public function ensureTestGeneratedDeprecationMessages() : void
    {
        $this->assertHasDeprecationMessages();
    }

    public function testMergeManyToMany()
    {
        $a = new DDC729A();
        $b = new DDC729B();
        $a->related[] = $b;

        $this->_em->persist($a);
        $this->_em->persist($b);
        $this->_em->flush();
        $this->_em->clear();
        $aId = $a->id;

        $a = new DDC729A();
        $a->id = $aId;

        $this->assertInstanceOf(ArrayCollection::class, $a->related);

        $a = $this->_em->merge($a);

        $this->assertInstanceOf(PersistentCollection::class, $a->related);

        $this->assertFalse($a->related->isInitialized(), "Collection should not be marked initialized.");
        $this->assertFalse($a->related->isDirty(), "Collection should not be marked as dirty.");

        $this->_em->flush();
        $this->_em->clear();

        $a = $this->_em->find(DDC729A::class, $aId);
        $this->assertEquals(1, count($a->related));
    }

    public function testUnidirectionalMergeManyToMany()
    {
        $a = new DDC729A();
        $b1 = new DDC729B();
        $b2 = new DDC729B();
        $a->related[] = $b1;

        $this->_em->persist($a);
        $this->_em->persist($b1);
        $this->_em->persist($b2);
        $this->_em->flush();
        $this->_em->clear();
        $aId = $a->id;

        $a = new DDC729A();
        $a->id = $aId;

        $a = $this->_em->merge($a);

        $a->related->set(0, $this->_em->merge($b1));

        $a->related->set(1, $this->_em->merge($b2));

        $this->_em->flush();
        $this->_em->clear();

        $a = $this->_em->find(DDC729A::class, $aId);
        $this->assertEquals(2, count($a->related));
    }

    public function testBidirectionalMergeManyToMany()
    {
        $a = new DDC729A();
        $b1 = new DDC729B();
        $b2 = new DDC729B();
        $a->related[] = $b1;

        $this->_em->persist($a);
        $this->_em->persist($b1);
        $this->_em->persist($b2);
        $this->_em->flush();
        $this->_em->clear();
        $aId = $a->id;

        $a = new DDC729A();
        $a->id = $aId;

        $a = $this->_em->merge($a);

        $a->related->set(0, $this->_em->merge($b1));
        $b1->related->set(0, $a);

        $a->related->set(1, $this->_em->merge($b2));
        $b2->related->set(0, $a);

        $this->_em->flush();
        $this->_em->clear();

        $a = $this->_em->find(DDC729A::class, $aId);
        $this->assertEquals(2, count($a->related));
    }

    public function testBidirectionalMultiMergeManyToMany()
    {
        $a = new DDC729A();
        $b1 = new DDC729B();
        $b2 = new DDC729B();
        $a->related[] = $b1;

        $this->_em->persist($a);
        $this->_em->persist($b1);
        $this->_em->persist($b2);
        $this->_em->flush();
        $this->_em->clear();
        $aId = $a->id;

        $a = new DDC729A();
        $a->id = $aId;

        $a = $this->_em->merge($a);

        $a->related->set(0, $this->_em->merge($b1));
        $b1->related->set(0, $this->_em->merge($a));

        $a->related->set(1, $this->_em->merge($b2));
        $b2->related->set(0, $this->_em->merge($a));

        $this->_em->flush();
        $this->_em->clear();

        $a = $this->_em->find(DDC729A::class, $aId);
        $this->assertEquals(2, count($a->related));
    }
}

/**
 * @Entity
 */
class DDC729A
{
    /** @Id @GeneratedValue @Column(type="integer") */
    public $id;

    /** @ManyToMany(targetEntity="DDC729B", inversedBy="related") */
    public $related;

    public function __construct()
    {
        $this->related = new \Doctrine\Common\Collections\ArrayCollection();
    }
}

/**
 * @Entity
 */
class DDC729B
{
    /** @Id @GeneratedValue @Column(type="integer") */
    public $id;

    /** @ManyToMany(targetEntity="DDC729B", mappedBy="related") */
    public $related;

    public function __construct()
    {
        $this->related = new \Doctrine\Common\Collections\ArrayCollection();
    }
}
