<?php

declare(strict_types=1);

namespace Apexl\Tests\Core\FeatureTests\Relations;

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

final class BelongsToManyTest extends AbstractRelationTestBase
{
    #[Test]
    public function notSet(): void
    {
        $company = Company::factory()->create();

        $company->withLazyLoadAllowed(function (Company $company): void {
            $this->assertInstanceOf(EntityCollection::class, $company->types);
            $this->assertCount(0, $company->types);
        });
    }

    #[Test]
    public function fetch(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type2 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type3 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

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

        $this->assertEquals($type1->id, $found->types[0]->id);
        $this->assertEquals($type2->id, $found->types[1]->id);
        $this->assertEquals($type3->id, $found->types[2]->id);
    }

    #[Test]
    public function setEntityCollection(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create();
        $type2 = CompanyType::factory()->create();
        $type3 = CompanyType::factory()->create();

        $company->relation('types')->set(new EntityCollection([$type1, $type2, $type3]));

        $this->assertInstanceOf(EntityCollection::class, $company->types);
        $this->assertCount(3, $company->types);

        $this->assertEquals($type1->id, $company->types[0]->id);
        $this->assertEquals($type2->id, $company->types[1]->id);
        $this->assertEquals($type3->id, $company->types[2]->id);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

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

        $this->assertEquals($type1->id, $found->types[0]->id);
        $this->assertEquals($type2->id, $found->types[1]->id);
        $this->assertEquals($type3->id, $found->types[2]->id);


    }

    #[Test]
    public function setArrayOfEntities(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create();
        $type2 = CompanyType::factory()->create();
        $type3 = CompanyType::factory()->create();

        $company->relation('types')->set([$type1, $type2, $type3]);

        $this->assertInstanceOf(EntityCollection::class, $company->types);
        $this->assertCount(3, $company->types);

        $this->assertEquals($type1->id, $company->types[0]->id);
        $this->assertEquals($type2->id, $company->types[1]->id);
        $this->assertEquals($type3->id, $company->types[2]->id);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

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

        $this->assertEquals($type1->id, $found->types[0]->id);
        $this->assertEquals($type2->id, $found->types[1]->id);
        $this->assertEquals($type3->id, $found->types[2]->id);
    }

    #[Test]
    public function setArrayOfIds(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create();
        $type2 = CompanyType::factory()->create();
        $type3 = CompanyType::factory()->create();

        $company->relation('types')->set([$type1->id, $type2->id, $type3->id]);

        $this->assertInstanceOf(EntityCollection::class, $company->types);
        $this->assertCount(3, $company->types);

        $this->assertEquals($type1->id, $company->types[0]->id);
        $this->assertEquals($type2->id, $company->types[1]->id);
        $this->assertEquals($type3->id, $company->types[2]->id);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

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

        $this->assertEquals($type1->id, $found->types[0]->id);
        $this->assertEquals($type2->id, $found->types[1]->id);
        $this->assertEquals($type3->id, $found->types[2]->id);
    }

    #[Test]
    public function setMixedArray(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create();
        $type2 = CompanyType::factory()->create();
        $type3 = CompanyType::factory()->create();

        $company->relation('types')->set([$type1, $type2->id, $type3]);

        $this->assertInstanceOf(EntityCollection::class, $company->types);
        $this->assertCount(3, $company->types);

        $this->assertEquals($type1->id, $company->types[0]->id);
        $this->assertEquals($type2->id, $company->types[1]->id);
        $this->assertEquals($type3->id, $company->types[2]->id);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

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

        $this->assertEquals($type1->id, $found->types[0]->id);
        $this->assertEquals($type2->id, $found->types[1]->id);
        $this->assertEquals($type3->id, $found->types[2]->id);
    }

    #[Test]
    public function setEntity(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create();
        $type2 = CompanyType::factory()->create();
        $type3 = CompanyType::factory()->create();

        $company->relation('types')->set($type2);

        $this->assertInstanceOf(EntityCollection::class, $company->types);
        $this->assertCount(1, $company->types);

        $this->assertEquals($type2->id, $company->types[0]->id);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

        $this->assertInstanceOf(EntityCollection::class, $found->types);
        $this->assertCount(1, $found->types);

        $this->assertEquals($type2->id, $found->types[0]->id);
    }

    #[Test]
    public function setEntityId(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create();
        $type2 = CompanyType::factory()->create();
        $type3 = CompanyType::factory()->create();

        $company->relation('types')->set($type2->id);

        $this->assertInstanceOf(EntityCollection::class, $company->types);
        $this->assertCount(1, $company->types);

        $this->assertEquals($type2->id, $company->types[0]->id);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

        $this->assertInstanceOf(EntityCollection::class, $found->types);
        $this->assertCount(1, $found->types);

        $this->assertEquals($type2->id, $found->types[0]->id);

    }

    #[Test]
    public function replace(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create();
        $type2 = CompanyType::factory()->create();
        $type3 = CompanyType::factory()->create();
        $type4 = CompanyType::factory()->create();
        $type5 = CompanyType::factory()->create();

        $company->relation('types')->set(new EntityCollection([$type1, $type2, $type3]));

        $this->assertInstanceOf(EntityCollection::class, $company->types);
        $this->assertCount(3, $company->types);

        $company->relation('types')->set(new EntityCollection([$type1, $type4, $type5]));


        $this->assertEquals($type1->id, $company->types[0]->id);
        $this->assertEquals($type4->id, $company->types[1]->id);
        $this->assertEquals($type5->id, $company->types[2]->id);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

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

        $this->assertEquals($type1->id, $found->types[0]->id);
        $this->assertEquals($type4->id, $found->types[1]->id);
        $this->assertEquals($type5->id, $found->types[2]->id);

    }

    #[Test]
    public function clear(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type2 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type3 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);

        $found = Company::find(
            id: $company->id,
            fields: [
                'types.id',
            ],
        );

        $found->relation('types')->clear();

        $this->assertInstanceOf(EntityCollection::class, $found->types);
        $this->assertCount(0, $found->types);
    }

    #[Test]
    public function addEntity(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type2 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type3 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type4 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);

        $company->withLazyLoadAllowed(function (Company $company) use ($type1, $type2, $type4): void {
            $company->relation('types')->set(new EntityCollection([$type1, $type2]));

            $this->assertInstanceOf(EntityCollection::class, $company->types);
            $this->assertCount(2, $company->types);

            $company->relation('types')->add($type4);

            $this->assertInstanceOf(EntityCollection::class, $company->types);
            $this->assertCount(3, $company->types);

            $this->assertEquals($type1->id, $company->types[0]->id);
            $this->assertEquals($type2->id, $company->types[1]->id);
            $this->assertEquals($type4->id, $company->types[2]->id);
        });
    }

    #[Test]
    public function addEntityId(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type2 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type3 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type4 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);

        $company->withLazyLoadAllowed(function (Company $company) use ($type1, $type2, $type4): void {
            $company->relation('types')->set(new EntityCollection([$type1, $type2]));

            $this->assertInstanceOf(EntityCollection::class, $company->types);
            $this->assertCount(2, $company->types);

            $company->relation('types')->add($type4->id);

            $this->assertInstanceOf(EntityCollection::class, $company->types);
            $this->assertCount(3, $company->types);

            $this->assertEquals($type1->id, $company->types[0]->id);
            $this->assertEquals($type2->id, $company->types[1]->id);
            $this->assertEquals($type4->id, $company->types[2]->id);
        });
    }

    #[Test]
    public function removeEntity(): void
    {
        $company = Company::factory()->create();
        $type1 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type2 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type3 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);
        $type4 = CompanyType::factory()->create([
            'company_id' => $company->id,
        ]);

        $company->withLazyLoadAllowed(function (Company $company) use ($type1, $type2, $type4): void {
            $company->relation('types')->set(new EntityCollection([$type1, $type2, $type4]));

            $this->assertInstanceOf(EntityCollection::class, $company->types);
            $this->assertCount(3, $company->types);

            $company->relation('types')->remove($type2);

            $this->assertInstanceOf(EntityCollection::class, $company->types);
            $this->assertCount(2, $company->types);

            $this->assertEquals($type1->id, $company->types[0]->id);
            $this->assertEquals($type4->id, $company->types[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);

    }

}