<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Tests\Export;

use Chill\MainBundle\Export\ExportDataNormalizerTrait;
use Doctrine\Persistence\ObjectRepository;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * @internal
 *
 * @coversNothing
 */
class ExportDataNormalizerTraitTest extends TestCase
{
    use ProphecyTrait;

    private function buildTrait(): object
    {
        return new class () {
            use ExportDataNormalizerTrait;

            public function normalizeEntity(object|iterable $entity): array|int|string
            {
                return $this->normalizeDoctrineEntity($entity);
            }

            public function denormalizeEntity(mixed $entity, ObjectRepository $repository)
            {
                return $this->denormalizeDoctrineEntity($entity, $repository);
            }

            public function normalizeD(\DateTimeImmutable|\DateTime $date): string
            {
                return $this->normalizeDate($date);
            }

            public function denormalizeD(string $date): \DateTimeImmutable|\DateTime
            {
                return $this->denormalizeDate($date);
            }
        };
    }

    public function testNormalizationDoctrineEntitySingle(): void
    {
        $entity = new class () {
            public function getId(): int
            {
                return 1;
            }
        };

        $repository = $this->prophesize(ObjectRepository::class);
        $repository->find(1)->willReturn($entity);

        $normalized = $this->buildTrait()->normalizeEntity($entity);
        $actual = $this->buildTrait()->denormalizeEntity($normalized, $repository->reveal());

        self::assertSame($entity, $actual);
    }

    public function testNormalizationDoctrineEntityMulti(): void
    {
        $entityA = new class () {
            public function getId(): int
            {
                return 1;
            }
        };

        $entityB = new class () {
            public function getId(): int
            {
                return 2;
            }
        };

        $repository = $this->prophesize(ObjectRepository::class);
        $repository->findBy(
            Argument::that(static fn ($arg): bool => in_array(1, $arg['id'] ?? []) && in_array(2, $arg['id'] ?? []))
        )->willReturn([$entityA, $entityB]);

        $normalized = $this->buildTrait()->normalizeEntity([$entityA, $entityB]);
        $actual = $this->buildTrait()->denormalizeEntity($normalized, $repository->reveal());

        self::assertContains(1, array_map(static fn (object $item) => $item->getId(), $actual));
        self::assertContains(2, array_map(static fn (object $item) => $item->getId(), $actual));
        self::assertCount(2, $actual);
    }

    /**
     * @dataProvider provideDate
     */
    public function testNormalizationDate(\DateTimeImmutable|\DateTime $date): void
    {
        $normalized = $this->buildTrait()->normalizeD($date);
        $actual = $this->buildTrait()->denormalizeD($normalized);

        self::assertEquals($date, $actual);
    }

    public static function provideDate(): iterable
    {
        yield [new \DateTimeImmutable('2024-01-15T18:57:20', new \DateTimeZone('Europe/Athens'))];
        yield [new \DateTimeImmutable('2024-01-15T18:57:30', new \DateTimeZone('America/Havana'))];
        yield [new \DateTime('2024-01-15T18:57:40', new \DateTimeZone('Europe/Madrid'))];
        yield [new \DateTime('2024-01-15T18:57:50', new \DateTimeZone('Africa/Kinshasa'))];
    }
}
