<?php

declare(strict_types=1);

namespace Apexl\Tests\Core\FeatureTests\Relations;

use apexl\entityCore\collections\EntityCollection;
use app\module\newtonsTrailers\Entity\Hire;
use app\module\newtonsTrailers\Entity\Segment;
use PHPUnit\Framework\Attributes\Test;

final class HasManyTest extends AbstractRelationTest
{
    #[Test]
    public function notSet(): void
    {
        $hire = Hire::factory()->create();

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(0, $hire->segments);
    }

    #[Test]
    public function fetch(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();

        self::$db->executeSql(
            <<<'SQL'
            INSERT INTO hires__segments (hire_id, segment_id, weight)
                VALUES
                    (:hire, :segment1, 1),
                    (:hire, :segment2, 3),
                    (:hire, :segment4, 2)
            SQL
            ,
            [
                ':hire' => $hire->id,
                ':segment1' => $segment1->id,
                ':segment2' => $segment2->id,
                ':segment4' => $segment4->id,
            ]
        );

        $found = Hire::find(
            id: $hire->id,
            fields: [
                'segments.id',
            ],
        );

        $this->assertInstanceOf(EntityCollection::class, $found->segments);
        $this->assertCount(3, $found->segments);

        $this->assertEquals($segment1->id, $found->segments[0]->id);
        $this->assertEquals($segment4->id, $found->segments[1]->id);
        $this->assertEquals($segment2->id, $found->segments[2]->id);
    }

    #[Test]
    public function setEntityCollection(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();

        $hire->relation('segments')->set(new EntityCollection([$segment1, $segment4, $segment2]));

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment4->id, $hire->segments[1]->id);
        $this->assertEquals($segment2->id, $hire->segments[2]->id);

        $found = Hire::find(
            id: $hire->id,
            fields: [
                'segments.id',
            ],
        );

        $this->assertInstanceOf(EntityCollection::class, $found->segments);
        $this->assertCount(3, $found->segments);

        $this->assertEquals($segment1->id, $found->segments[0]->id);
        $this->assertEquals($segment4->id, $found->segments[1]->id);
        $this->assertEquals($segment2->id, $found->segments[2]->id);


    }

    #[Test]
    public function setArrayOfEntities(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();

        $hire->relation('segments')->set([$segment1, $segment4, $segment2]);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment4->id, $hire->segments[1]->id);
        $this->assertEquals($segment2->id, $hire->segments[2]->id);

    }

    #[Test]
    public function setArrayOfIds(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();

        $hire->relation('segments')->set([$segment1->id, $segment4->id, $segment2->id]);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment4->id, $hire->segments[1]->id);
        $this->assertEquals($segment2->id, $hire->segments[2]->id);

    }

    #[Test]
    public function setMixedArray(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();

        $hire->relation('segments')->set([$segment1, $segment4->id, $segment2]);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment4->id, $hire->segments[1]->id);
        $this->assertEquals($segment2->id, $hire->segments[2]->id);

    }

    #[Test]
    public function setEntity(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();

        $hire->relation('segments')->set($segment3);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(1, $hire->segments);

        $this->assertEquals($segment3->id, $hire->segments[0]->id);

    }

    #[Test]
    public function setEntityId(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();

        $hire->relation('segments')->set($segment3->id);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(1, $hire->segments);

        $this->assertEquals($segment3->id, $hire->segments[0]->id);

    }

    #[Test]
    public function replace(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();
        $segment5 = Segment::factory()->create();

        $hire->relation('segments')->set(new EntityCollection([$segment1, $segment4, $segment2]));

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment4->id, $hire->segments[1]->id);
        $this->assertEquals($segment2->id, $hire->segments[2]->id);

        $hire->relation('segments')->set(new EntityCollection([$segment2, $segment3, $segment5]));

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $this->assertEquals($segment2->id, $hire->segments[0]->id);
        $this->assertEquals($segment3->id, $hire->segments[1]->id);
        $this->assertEquals($segment5->id, $hire->segments[2]->id);

        $found = Hire::find(
            id: $hire->id,
            fields: [
                'segments.id',
            ],
        );

        $this->assertInstanceOf(EntityCollection::class, $found->segments);
        $this->assertCount(3, $found->segments);

        $this->assertEquals($segment2->id, $found->segments[0]->id);
        $this->assertEquals($segment3->id, $found->segments[1]->id);
        $this->assertEquals($segment5->id, $found->segments[2]->id);
    }

    #[Test]
    public function clear(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();

        $hire->relation('segments')->set(new EntityCollection([$segment1, $segment2, $segment3]));

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $hire->relation('segments')->clear();

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(0, $hire->segments);
    }

    #[Test]
    public function addEntity(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();
        $segment5 = Segment::factory()->create();

        $hire->relation('segments')->set(new EntityCollection([$segment1, $segment2, $segment3]));

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $hire->relation('segments')->add($segment5);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(4, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment2->id, $hire->segments[1]->id);
        $this->assertEquals($segment3->id, $hire->segments[2]->id);
        $this->assertEquals($segment5->id, $hire->segments[3]->id);

    }

    #[Test]
    public function addEntityId(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();
        $segment5 = Segment::factory()->create();

        $hire->relation('segments')->set(new EntityCollection([$segment1, $segment2, $segment3]));

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $hire->relation('segments')->add($segment5->id);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(4, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment2->id, $hire->segments[1]->id);
        $this->assertEquals($segment3->id, $hire->segments[2]->id);
        $this->assertEquals($segment5->id, $hire->segments[3]->id);
    }

    #[Test]
    public function removeEntity(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();
        $segment5 = Segment::factory()->create();

        $hire->relation('segments')->set(new EntityCollection([$segment1, $segment2, $segment3]));

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $hire->relation('segments')->remove($segment2);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(2, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment3->id, $hire->segments[1]->id);

    }

    #[Test]
    public function removeEntityId(): void
    {
        $hire = Hire::factory()->create();
        $segment1 = Segment::factory()->create();
        $segment2 = Segment::factory()->create();
        $segment3 = Segment::factory()->create();
        $segment4 = Segment::factory()->create();
        $segment5 = Segment::factory()->create();

        $hire->relation('segments')->set(new EntityCollection([$segment1, $segment2, $segment3]));

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(3, $hire->segments);

        $hire->relation('segments')->remove($segment2->id);

        $this->assertInstanceOf(EntityCollection::class, $hire->segments);
        $this->assertCount(2, $hire->segments);

        $this->assertEquals($segment1->id, $hire->segments[0]->id);
        $this->assertEquals($segment3->id, $hire->segments[1]->id);

    }

}