<?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\WopiBundle\Controller;

use ChampsLibres\WopiLib\Contract\Service\Configuration\ConfigurationInterface;
use ChampsLibres\WopiLib\Contract\Service\Discovery\DiscoveryInterface;
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\WopiBundle\Service\Controller\ResponderInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use loophp\psr17\Psr17Interface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Twig\Environment;

/**
 * @internal
 *
 * @coversNothing
 */
final readonly class Editor
{
    public function __construct(private ConfigurationInterface $wopiConfiguration, private DiscoveryInterface $wopiDiscovery, private DocumentManagerInterface $documentManager, private Environment $engine, private JWTTokenManagerInterface $JWTTokenManager, private NormalizerInterface $normalizer, private ResponderInterface $responder, private Security $security, private Psr17Interface $psr17, private RouterInterface $router) {}

    public function __invoke(string $fileId, Request $request): Response
    {
        if (!($this->security->isGranted('ROLE_USER') || $this->security->isGranted('ROLE_ADMIN'))) {
            throw new AccessDeniedHttpException('Please authenticate to access this feature');
        }

        $user = $this->security->getUser();

        $configuration = $this->wopiConfiguration->jsonSerialize();
        /** @var StoredObject $storedObject */
        $storedObject = $this->documentManager->findByDocumentId($fileId);

        if (null === $storedObject) {
            throw new NotFoundHttpException(sprintf('Unable to find object %s', $fileId));
        }

        if (StoredObject::STATUS_FAILURE === $storedObject->getStatus()) {
            return new Response(
                $this->engine
                    ->render('@ChillWopi/Editor/stored_object_failure.html.twig')
            );
        }
        if (StoredObject::STATUS_PENDING === $storedObject->getStatus()) {
            return new Response(
                $this->engine
                    ->render('@ChillWopi/Editor/stored_object_pending.html.twig', [
                        'storedObject' => $this->normalizer->normalize($storedObject, 'json', [
                            AbstractNormalizer::GROUPS => ['read'],
                        ]),
                    ])
            );
        }

        if ([] === $discoverExtension = $this->wopiDiscovery->discoverMimeType($storedObject->getType())) {
            return new Response(
                $this->engine
                    ->render('@ChillWopi/Editor/unable_to_edit_such_document.html.twig', [
                        'document' => $storedObject,
                    ])
            );
        }

        $configuration['favIconUrl'] = '';
        $configuration['access_token'] = $this->JWTTokenManager->createFromPayload($user, [
            'UserCanWrite' => true,
            'UserCanAttend' => true,
            'UserCanPresent' => true,
            'fileId' => $fileId,
        ]);

        // we parse the token back to get the access_token_ttl
        // reminder: access_token_ttl is a javascript epoch, not a number of seconds; it is the
        // time when the token will expire, not the time to live:
        // https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/concepts#the-access_token_ttl-property
        $jwt = $this->JWTTokenManager->parse($configuration['access_token']);
        $configuration['access_token_ttl'] = $jwt['exp'] * 1000;

        $configuration['server'] = $this
            ->psr17
            ->createUri($discoverExtension[0]['urlsrc'])
            ->withQuery(
                http_build_query(
                    [
                        'WOPISrc' => $this
                            ->router
                            ->generate(
                                'checkFileInfo',
                                [
                                    'fileId' => $this->documentManager->getDocumentId($storedObject),
                                ],
                                UrlGeneratorInterface::ABSOLUTE_URL
                            ),
                        'closebutton' => 1,
                        'lang' => $request->getLocale(),
                    ]
                )
            );

        return $this
            ->responder
            ->render(
                '@ChillWopi/Editor/page.html.twig',
                $configuration
            );
    }
}
