<?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\Form;

use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Form\DataMapper\StoredObjectDataMapper;
use Chill\DocStoreBundle\Form\DataTransformer\StoredObjectDataTransformer;
use Chill\DocStoreBundle\Form\StoredObjectType;
use Chill\DocStoreBundle\Repository\StoredObjectRepositoryInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Guard\JWTDavTokenProviderInterface;
use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectDenormalizer;
use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectNormalizer;
use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectVersionNormalizer;
use Chill\MainBundle\Serializer\Normalizer\UserNormalizer;
use Chill\MainBundle\Templating\Entity\UserRender;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Form\PreloadedExtension;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Serializer;

/**
 * @internal
 *
 * @coversNothing
 */
class StoredObjectTypeTest extends TypeTestCase
{
    use ProphecyTrait;

    private StoredObject $model;

    protected function setUp(): void
    {
        $this->model = new StoredObject();
        $this->model->registerVersion();
        parent::setUp();
    }

    public function testChangeTitleValue(): void
    {
        $formData = ['title' => $newTitle = 'new title', 'stored_object' => <<<'JSON'
            {"uuid":"9855d676-690b-11ef-88d3-9f5a4129a7b7"}
            JSON];
        $form = $this->factory->create(StoredObjectType::class, $this->model, ['has_title' => true]);

        $form->submit($formData);

        $this->assertTrue($form->isSynchronized());

        $this->assertEquals($newTitle, $this->model->getTitle());
    }

    public function testReplaceByAnotherObject(): void
    {
        $formData = ['title' => $newTitle = 'new title', 'stored_object' => <<<'JSON'
            {"uuid":"9855d676-690b-11ef-88d3-9f5a4129a7b7","currentVersion":{"filename":"abcdef","iv":[10, 15, 20, 30],"keyInfos":[],"type":"text/html","persisted": false}}
            JSON];
        $originalObjectId = spl_object_hash($this->model);
        $form = $this->factory->create(StoredObjectType::class, $this->model, ['has_title' => true]);

        $form->submit($formData);

        $this->assertTrue($form->isSynchronized());

        $model = $form->getData();
        $this->assertEquals($originalObjectId, spl_object_hash($model));
        $this->assertEquals('abcdef', $model->getFilename());
        $this->assertEquals([10, 15, 20, 30], $model->getIv());
        $this->assertEquals('text/html', $model->getType());
        $this->assertEquals($newTitle, $model->getTitle());
    }

    public function testNothingIsChanged(): void
    {
        $formData = ['title' => $newTitle = 'new title', 'stored_object' => <<<'JSON'
            {"uuid":"9855d676-690b-11ef-88d3-9f5a4129a7b7","currentVersion":{"filename":"abcdef","iv":[10, 15, 20, 30],"keyInfos":[],"type":"text/html"}}
            JSON];
        $originalObjectId = spl_object_hash($this->model);
        $originalVersion = $this->model->getCurrentVersion();
        $originalFilename = $originalVersion->getFilename();
        $originalKeyInfos = $originalVersion->getKeyInfos();

        $form = $this->factory->create(StoredObjectType::class, $this->model, ['has_title' => true]);

        $form->submit($formData);

        $this->assertTrue($form->isSynchronized());

        $model = $form->getData();
        $this->assertEquals($originalObjectId, spl_object_hash($model));
        $this->assertSame($originalVersion, $model->getCurrentVersion());
        $this->assertEquals($originalFilename, $model->getCurrentVersion()->getFilename());
        $this->assertEquals($originalKeyInfos, $model->getCurrentVersion()->getKeyInfos());
    }

    protected function getExtensions()
    {
        $jwtTokenProvider = $this->prophesize(JWTDavTokenProviderInterface::class);
        $jwtTokenProvider->createToken(Argument::type(StoredObject::class), Argument::type(StoredObjectRoleEnum::class))
            ->willReturn('token');
        $jwtTokenProvider->getTokenExpiration('token')->willReturn(new \DateTimeImmutable());
        $urlGenerator = $this->prophesize(UrlGeneratorInterface::class);
        $urlGenerator->generate('chill_docstore_dav_document_get', Argument::type('array'), UrlGeneratorInterface::ABSOLUTE_URL)
            ->willReturn('http://url/fake');

        $security = $this->prophesize(Security::class);
        $security->isGranted(Argument::cetera())->willReturn(true);

        $storedObjectRepository = $this->prophesize(StoredObjectRepositoryInterface::class);
        $storedObjectRepository->findOneByUUID(Argument::type('string'))
            ->willReturn($this->model);

        $userRender = $this->prophesize(UserRender::class);

        $serializer = new Serializer(
            [
                new StoredObjectNormalizer(
                    $jwtTokenProvider->reveal(),
                    $urlGenerator->reveal(),
                    $security->reveal(),
                    $this->createMock(TempUrlGeneratorInterface::class)
                ),
                new StoredObjectDenormalizer($storedObjectRepository->reveal()),
                new StoredObjectVersionNormalizer(),
                new UserNormalizer($userRender->reveal(), new MockClock()),
            ],
            [
                new JsonEncoder(),
            ]
        );
        $dataTransformer = new StoredObjectDataTransformer($serializer);
        $dataMapper = new StoredObjectDataMapper();
        $type = new StoredObjectType(
            $dataTransformer,
            $dataMapper,
        );

        return [
            new PreloadedExtension([$type], []),
        ];
    }
}
