<?php

namespace goodboyalex\php_components_pack\tests\classes;

use goodboyalex\php_components_pack\classes\ObjectArray;
use goodboyalex\php_components_pack\tests\data\A;
use PHPUnit\Framework\TestCase;

class ObjectArrayTest extends TestCase
{
    public function testSort ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);
        $b_Array = new ObjectArray($array);

        $a_Array->Sort('a');
        $b_Array->Sort('b');

        $this->assertEquals('a', $a_Array[0]->a);
        $this->assertEquals('b', $a_Array[1]->a);
        $this->assertEquals('c', $a_Array[2]->a);

        $this->assertEquals('1', $b_Array[0]->b);
        $this->assertEquals('2', $b_Array[1]->b);
        $this->assertEquals('3', $b_Array[2]->b);

    }

    private function PrepareForTest (): void
    {
        require_once __DIR__ . '/../data/A.php';
        require_once __DIR__ . '/../data/B.php';
        require_once __DIR__ . '/../../sources/interfaces/ISerializable.php';
        require_once __DIR__ . '/../../sources/traits/ObjectArray/ObjectArrayBasicTrait.php';
        require_once __DIR__ . '/../../sources/traits/ObjectArray/ObjectArrayConstantsTrait.php';
        require_once __DIR__ . '/../../sources/traits/ObjectArray/ObjectArrayLINQTrait.php';
        require_once __DIR__ . '/../../sources/traits/ObjectArray/ObjectArraySearchAndSortTrait.php';
        require_once __DIR__ . '/../../sources/classes/ObjectArray.php';
    }

    public function testSearch ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $sr = $a_Array->Search('a', 'c');

        $this->assertEquals(2, $sr->b);
    }

    public function testGetRows ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $sr = $a_Array->GetRows(fn (A $a): bool => $a->b < 3);

        $sr->Sort("b");

        $this->assertCount(2, $sr);
        $this->assertEquals('1', $sr[0]->b);
        $this->assertEquals('2', $sr[1]->b);
    }

    public function testGetValue ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);
        $sr = $a_Array->GetValue("b", fn (A $a): bool => $a->b < 2);

        $this->assertNotNull($sr);
        $this->assertEquals(1, $sr);
    }

    public function testCount ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $cnt = $a_Array->Count();
        $this->assertEquals(3, $cnt);

        $cnt = $a_Array->Count(fn (A $a): bool => $a->b < 3);
        $this->assertEquals(2, $cnt);
    }

    public function testDelete ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $a_Array->Delete(fn (A $a): bool => $a->b == 3);

        $cnt = $a_Array->Count();
        $this->assertEquals(2, $cnt);
    }

    public function testClear ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $a_Array->Clear();

        $cnt = $a_Array->Count();
        $this->assertEquals(0, $cnt);
    }

    public function testMerge ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $array2 = [
            new A("d", 4, true),
            new A("e", 5, false),
            new A("f", 6, true),
        ];

        $a_Array = new ObjectArray($array);

        $a_Array->Merge($array2);

        $cnt = $a_Array->Count();
        $this->assertEquals(6, $cnt);

        $sr = $a_Array->Search('a', 'f');

        $this->assertEquals(6, $sr->b);
    }

    public function testMaxBy ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $sr = $a_Array->MaxBy(fn (A $a) => $a->b);

        $this->assertEquals("a", $sr->a);
    }

    public function testGetColumn ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $sr = $a_Array->GetColumn('a', fn (A $a): bool => $a->b < 3);

        $this->assertIsArray($sr);
        $this->assertSame(['c', 'b'], $sr);
    }

    public function testToArray ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $sr = $a_Array->ToArray();

        $this->assertIsArray($sr);
        $this->assertSame($array, $sr);

    }

    public function testMinBy ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $sr = $a_Array->MinBy(fn (A $a) => $a->b);

        $this->assertEquals("b", $sr->a);
    }

    public function testIsExist ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $srTrue = $a_Array->IsExist(fn (A $a) => $a->b == 1);
        $srFalse = $a_Array->IsExist(fn (A $a) => $a->b == 5);

        $this->assertTrue($srTrue);
        $this->assertFalse($srFalse);
    }

    public function testUpdate ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $a_Array->Update([new A("d", 3, true)], fn (A $a) => $a->a == "a");

        $sr = $a_Array->Search("a", "d");

        $this->assertEquals("3", $sr->b);
        $this->assertTrue($sr->c);
    }

    public function testGetRow ()
    {
        $this->PrepareForTest();

        $array = [
            new A("a", 3, true),
            new A("c", 2, false),
            new A("b", 1, true),
        ];

        $a_Array = new ObjectArray($array);

        $sr = $a_Array->GetRow(fn (A $a): bool => $a->b == 3);

        $this->assertInstanceOf(A::class, $sr);
        $this->assertEquals('a', $sr->a);
        $this->assertTrue($sr->c);
    }
}