<?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\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
use Chill\DocStoreBundle\Repository\StoredObjectVersionRepository;
use Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveOldVersionMessage;
use Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveOldVersionMessageHandler;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Component\Clock\MockClock;

/**
 * @internal
 *
 * @coversNothing
 */
class RemoveOldVersionMessageHandlerTest extends TestCase
{
    public function testInvoke(): void
    {
        $object = new StoredObject();
        $version = $object->registerVersion();
        $storedObjectVersionRepository = $this->createMock(StoredObjectVersionRepository::class);
        $storedObjectVersionRepository->expects($this->once())->method('find')
            ->with($this->identicalTo(1))
            ->willReturn($version);

        $entityManager = $this->createMock(EntityManagerInterface::class);
        $entityManager->expects($this->once())->method('remove')->with($this->identicalTo($version));
        $entityManager->expects($this->once())->method('flush');
        $entityManager->expects($this->once())->method('clear');

        $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
        $storedObjectManager->expects($this->once())->method('exists')->willReturn(true);
        $storedObjectManager->expects($this->once())->method('delete')->with($this->identicalTo($version));

        $handler = new RemoveOldVersionMessageHandler($storedObjectVersionRepository, new NullLogger(), $entityManager, $storedObjectManager, new MockClock());

        $handler(new RemoveOldVersionMessage(1));
    }

    public function testInvokeForVersionNotExisting(): void
    {
        $object = new StoredObject();
        $version = $object->registerVersion();
        $storedObjectVersionRepository = $this->createMock(StoredObjectVersionRepository::class);
        $storedObjectVersionRepository->expects($this->once())->method('find')
            ->with($this->identicalTo(1))
            ->willReturn($version);

        $entityManager = $this->createMock(EntityManagerInterface::class);
        $entityManager->expects($this->once())->method('remove')->with($this->identicalTo($version));
        $entityManager->expects($this->once())->method('flush');
        $entityManager->expects($this->once())->method('clear');

        $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
        $storedObjectManager->expects($this->once())->method('exists')->willReturn(false);
        $storedObjectManager->expects($this->never())->method('delete')->with($this->identicalTo($version));

        $handler = new RemoveOldVersionMessageHandler($storedObjectVersionRepository, new NullLogger(), $entityManager, $storedObjectManager, new MockClock());

        $handler(new RemoveOldVersionMessage(1));
    }

    public function testInvokeWithStoredObjectToDelete(): void
    {
        $object = new StoredObject();
        $object->setDeleteAt(new \DateTimeImmutable('2023-12-01'));
        $version = $object->registerVersion();

        $storedObjectVersionRepository = $this->createMock(StoredObjectVersionRepository::class);
        $storedObjectVersionRepository->expects($this->once())->method('find')
            ->with($this->identicalTo(1))
            ->willReturn($version);

        $entityManager = $this->createMock(EntityManagerInterface::class);
        $entityManager->expects($this->exactly(2))->method('remove')->with(
            $this->logicalOr($this->identicalTo($version), $this->identicalTo($object))
        );
        $entityManager->expects($this->once())->method('flush');
        $entityManager->expects($this->once())->method('clear');

        $handler = new RemoveOldVersionMessageHandler(
            $storedObjectVersionRepository,
            new NullLogger(),
            $entityManager,
            new DummyStoredObjectManager(),
            new MockClock(new \DateTimeImmutable('2024-01-01'))
        );

        $handler(new RemoveOldVersionMessage(1));

        self::assertCount(0, $object->getVersions());
    }
}

class DummyStoredObjectManager implements StoredObjectManagerInterface
{
    public function getLastModified(StoredObject|StoredObjectVersion $document): \DateTimeInterface
    {
        throw new \RuntimeException();
    }

    public function getContentLength(StoredObject|StoredObjectVersion $document): int
    {
        throw new \RuntimeException();
    }

    public function read(StoredObject|StoredObjectVersion $document): string
    {
        throw new \RuntimeException();
    }

    public function write(StoredObject $document, string $clearContent, ?string $contentType = null): StoredObjectVersion
    {
        throw new \RuntimeException();
    }

    public function delete(StoredObjectVersion $storedObjectVersion): void
    {
        $object = $storedObjectVersion->getStoredObject();
        $object->removeVersion($storedObjectVersion);
    }

    public function etag(StoredObject|StoredObjectVersion $document): string
    {
        throw new \RuntimeException();
    }

    public function clearCache(): void
    {
        throw new \RuntimeException();
    }

    public function exists(StoredObject|StoredObjectVersion $document): bool
    {
        return true;
    }
}
