<?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 Tests\Service\StoredObjectCleaner;

use Chill\DocStoreBundle\Repository\StoredObjectVersionRepository;
use Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveOldVersionCronJob;
use Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveOldVersionMessage;
use Chill\MainBundle\Entity\CronJobExecution;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;

/**
 * @internal
 *
 * @coversNothing
 */
class RemoveOldVersionCronJobTest extends KernelTestCase
{
    /**
     * @dataProvider buildTestCanRunData
     */
    public function testCanRun(?CronJobExecution $cronJobExecution, bool $expected): void
    {
        $repository = $this->createMock(StoredObjectVersionRepository::class);
        $clock = new MockClock(new \DateTimeImmutable('2024-01-01 00:00:00', new \DateTimeZone('+00:00')));

        $cronJob = new RemoveOldVersionCronJob($clock, $this->buildMessageBus(), $repository);

        self::assertEquals($expected, $cronJob->canRun($cronJobExecution));
    }

    public static function buildTestCanRunData(): iterable
    {
        yield [
            (new CronJobExecution('last-deleted-stored-object-version-id'))->setLastEnd(new \DateTimeImmutable('2023-12-31 00:00:00', new \DateTimeZone('+00:00'))),
            true,
        ];

        yield [
            (new CronJobExecution('last-deleted-stored-object-version-id'))->setLastEnd(new \DateTimeImmutable('2023-12-30 23:59:59', new \DateTimeZone('+00:00'))),
            true,
        ];

        yield [
            (new CronJobExecution('last-deleted-stored-object-version-id'))->setLastEnd(new \DateTimeImmutable('2023-12-31 00:00:01', new \DateTimeZone('+00:00'))),
            false,
        ];

        yield [
            null,
            true,
        ];
    }

    public function testRun(): void
    {
        // we create a clock in the future. This led us a chance to having stored object to delete
        $clock = new MockClock(new \DateTimeImmutable('2024-01-01 00:00:00', new \DateTimeZone('+00:00')));
        $repository = $this->createMock(StoredObjectVersionRepository::class);
        $repository->expects($this->once())
            ->method('findIdsByVersionsOlderThanDateAndNotLastVersionAndNotPointInTime')
            ->with(new \DateTime('2023-10-03 00:00:00', new \DateTimeZone('+00:00')))
            ->willReturnCallback(function ($arg) {
                yield 1;
                yield 3;
                yield 2;
            })
        ;

        $cronJob = new RemoveOldVersionCronJob($clock, $this->buildMessageBus(true), $repository);

        $results = $cronJob->run([]);

        self::assertArrayHasKey('last-deleted-stored-object-version-id', $results);
        self::assertIsInt($results['last-deleted-stored-object-version-id']);
    }

    private function buildMessageBus(bool $expectDistpatchAtLeastOnce = false): MessageBusInterface
    {
        $messageBus = $this->createMock(MessageBusInterface::class);

        $methodDispatch = match ($expectDistpatchAtLeastOnce) {
            true => $messageBus->expects($this->atLeastOnce())->method('dispatch')->with($this->isInstanceOf(RemoveOldVersionMessage::class)),
            false => $messageBus->method('dispatch'),
        };

        $methodDispatch->willReturnCallback(fn (RemoveOldVersionMessage $message) => new Envelope($message));

        return $messageBus;
    }
}
