<?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\DocStoreBundle\Tests\AsyncUpload\Driver\LocalStorage;

use Chill\DocStoreBundle\AsyncUpload\Driver\LocalStorage\StoredObjectManager;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
use Chill\DocStoreBundle\Service\Cryptography\KeyGenerator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;

/**
 * @internal
 *
 * @coversNothing
 */
class StoredObjectManagerTest extends TestCase
{
    private const CONTENT = 'abcde';

    public function testWrite(): StoredObjectVersion
    {
        $storedObject = new StoredObject();

        $manager = $this->buildStoredObjectManager();
        $version = $manager->write($storedObject, self::CONTENT);

        self::assertSame($storedObject, $version->getStoredObject());

        return $version;
    }

    /**
     * @depends testWrite
     */
    public function testRead(StoredObjectVersion $version): StoredObjectVersion
    {
        $manager = $this->buildStoredObjectManager();
        $content = $manager->read($version);

        self::assertEquals(self::CONTENT, $content);

        return $version;
    }

    /**
     * @depends testRead
     */
    public function testExists(StoredObjectVersion $version): StoredObjectVersion
    {
        $manager = $this->buildStoredObjectManager();
        $notExisting = new StoredObject();
        $versionNotPersisted = $notExisting->registerVersion();

        self::assertTrue($manager->exists($version));
        self::assertFalse($manager->exists($versionNotPersisted));
        self::assertFalse($manager->exists(new StoredObject()));

        return $version;
    }

    /**
     * @throws \Chill\DocStoreBundle\Exception\StoredObjectManagerException
     *
     * @depends testExists
     */
    public function testEtag(StoredObjectVersion $version): StoredObjectVersion
    {
        $manager = $this->buildStoredObjectManager();
        $actual = $manager->etag($version);

        self::assertEquals(md5(self::CONTENT), $actual);

        return $version;
    }

    /**
     * @depends testEtag
     */
    public function testGetContentLength(StoredObjectVersion $version): StoredObjectVersion
    {
        $manager = $this->buildStoredObjectManager();

        $actual = $manager->getContentLength($version);

        self::assertSame(5, $actual);

        return $version;
    }

    /**
     * @throws \Chill\DocStoreBundle\Exception\StoredObjectManagerException
     *
     * @depends testGetContentLength
     */
    public function testGetLastModified(StoredObjectVersion $version): StoredObjectVersion
    {
        $manager = $this->buildStoredObjectManager();
        $actual = $manager->getLastModified($version);

        self::assertInstanceOf(\DateTimeImmutable::class, $actual);
        self::assertGreaterThan((new \DateTimeImmutable('now'))->getTimestamp() - 10, $actual->getTimestamp());

        return $version;
    }

    /**
     * @depends testGetLastModified
     */
    public function testDelete(StoredObjectVersion $version): void
    {
        $manager = $this->buildStoredObjectManager();
        $manager->delete($version);

        self::assertFalse($manager->exists($version));
    }

    public function testDeleteDoesNotRemoveOlderVersion(): void
    {
        $storedObject = new StoredObject();
        $manager = $this->buildStoredObjectManager();
        $version1 = $manager->write($storedObject, 'version1');
        $version2 = $manager->write($storedObject, 'version2');
        $version3 = $manager->write($storedObject, 'version3');

        self::assertTrue($manager->exists($version1));
        self::assertEquals('version1', $manager->read($version1));
        self::assertTrue($manager->exists($version2));
        self::assertEquals('version2', $manager->read($version2));
        self::assertTrue($manager->exists($version3));
        self::assertEquals('version3', $manager->read($version3));

        // we delete the intermediate version
        $manager->delete($version2);

        self::assertFalse($manager->exists($version2));
        // we check that we are still able to download the other versions
        self::assertTrue($manager->exists($version1));
        self::assertEquals('version1', $manager->read($version1));
        self::assertTrue($manager->exists($version3));
        self::assertEquals('version3', $manager->read($version3));
    }

    private function buildStoredObjectManager(): StoredObjectManager
    {
        return new StoredObjectManager(
            new ParameterBag(['chill_doc_store' => ['local_storage' => ['storage_path' => '/tmp/chill-local-storage-test']]]),
            new KeyGenerator(),
        );
    }
}
