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

use ChampsLibres\WopiLib\Contract\Service\Discovery\DiscoveryInterface;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Guard\JWTDavTokenProviderInterface;
use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectNormalizer;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Twig\Environment;
use Twig\Extension\RuntimeExtensionInterface;

final readonly class WopiEditTwigExtensionRuntime implements RuntimeExtensionInterface
{
    public const SUPPORTED_MIMES = [
        'image/svg+xml',
        'application/vnd.ms-powerpoint',
        'application/vnd.ms-excel',
        'application/vnd.sun.xml.writer',
        'application/vnd.oasis.opendocument.text',
        'application/vnd.oasis.opendocument.text-flat-xml',
        'application/vnd.sun.xml.calc',
        'application/vnd.oasis.opendocument.spreadsheet',
        'application/vnd.oasis.opendocument.spreadsheet-flat-xml',
        'application/vnd.sun.xml.impress',
        'application/vnd.oasis.opendocument.presentation',
        'application/vnd.oasis.opendocument.presentation-flat-xml',
        'application/vnd.sun.xml.draw',
        'application/vnd.oasis.opendocument.graphics',
        'application/vnd.oasis.opendocument.graphics-flat-xml',
        'application/vnd.oasis.opendocument.chart',
        'application/vnd.sun.xml.writer.global',
        'application/vnd.oasis.opendocument.text-master',
        'application/vnd.sun.xml.writer.template',
        'application/vnd.oasis.opendocument.text-template',
        'application/vnd.oasis.opendocument.text-master-template',
        'application/vnd.sun.xml.calc.template',
        'application/vnd.oasis.opendocument.spreadsheet-template',
        'application/vnd.sun.xml.impress.template',
        'application/vnd.oasis.opendocument.presentation-template',
        'application/vnd.sun.xml.draw.template',
        'application/vnd.oasis.opendocument.graphics-template',
        'application/msword',
        'application/msword',
        'application/vnd.ms-excel',
        'application/vnd.ms-powerpoint',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'application/vnd.ms-word.document.macroEnabled.12',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
        'application/vnd.ms-word.template.macroEnabled.12',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
        'application/vnd.ms-excel.template.macroEnabled.12',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
        'application/vnd.ms-excel.sheet.macroEnabled.12',
        'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
        'application/vnd.openxmlformats-officedocument.presentationml.template',
        'application/vnd.ms-powerpoint.template.macroEnabled.12',
        'application/vnd.wordperfect',
        'application/x-aportisdoc',
        'application/x-hwp',
        'application/vnd.ms-works',
        'application/x-mswrite',
        'application/x-dif-document',
        'text/spreadsheet',
        'text/csv',
        'application/x-dbase',
        'application/vnd.lotus-1-2-3',
        'image/cgm',
        'image/vnd.dxf',
        'image/x-emf',
        'image/x-wmf',
        'application/coreldraw',
        'application/vnd.visio2013',
        'application/vnd.visio',
        'application/vnd.ms-visio.drawing',
        'application/x-mspublisher',
        'application/x-sony-bbeb',
        'application/x-gnumeric',
        'application/macwriteii',
        'application/x-iwork-numbers-sffnumbers',
        'application/vnd.oasis.opendocument.text-web',
        'application/x-pagemaker',
        'text/rtf',
        'text/plain',
        'application/x-fictionbook+xml',
        'application/clarisworks',
        'image/x-wpg',
        'application/x-iwork-pages-sffpages',
        'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
        'application/x-iwork-keynote-sffkey',

        'application/x-abiword',
        'image/x-freehand',
        'application/vnd.sun.xml.chart',
        'application/x-t602',
        'image/bmp',
        'image/png',
        'image/gif',
        'image/tiff',
        'image/jpg',
        'image/jpeg',
        'application/pdf',
    ];

    private const DEFAULT_OPTIONS_TEMPLATE_BUTTON_GROUP = [
        'small' => false,
    ];

    private const TEMPLATE = '@ChillDocStore/Button/wopi_edit_document.html.twig';

    private const TEMPLATE_BUTTON_GROUP = '@ChillDocStore/Button/button_group.html.twig';

    public function __construct(
        private DiscoveryInterface $discovery,
        private NormalizerInterface $normalizer,
        private JWTDavTokenProviderInterface $davTokenProvider,
        private UrlGeneratorInterface $urlGenerator,
        private Security $security,
    ) {}

    /**
     * return true if the document is editable.
     *
     * **NOTE**: as the Vue button does have similar test, this is not required if in use with
     * the dedicated Vue component (GroupDownloadButton.vue, WopiEditButton.vue)
     */
    public function isEditable(StoredObject $document): bool
    {
        return in_array($document->getType(), self::SUPPORTED_MIMES, true);
    }

    /**
     * @param array{small: bool} $options
     *
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function renderButtonGroup(Environment $environment, StoredObject $document, ?string $title = null, bool $showEditButtons = true, array $options = []): string
    {
        $canEdit = $this->security->isGranted(StoredObjectRoleEnum::EDIT->value, $document) && $showEditButtons;

        $accessToken = $this->davTokenProvider->createToken(
            $document,
            $canEdit ? StoredObjectRoleEnum::EDIT : StoredObjectRoleEnum::SEE
        );

        return $environment->render(self::TEMPLATE_BUTTON_GROUP, [
            'document' => $document,
            'document_json' => $this->normalizer->normalize($document, 'json', [AbstractNormalizer::GROUPS => ['read']]),
            'title' => $title,
            'can_edit' => $canEdit,
            'options' => [...self::DEFAULT_OPTIONS_TEMPLATE_BUTTON_GROUP, ...$options],
            'dav_link' => $this->urlGenerator->generate(
                'chill_docstore_dav_document_get',
                [
                    'uuid' => $document->getUuid(),
                    'access_token' => $accessToken,
                ],
                UrlGeneratorInterface::ABSOLUTE_URL,
            ),
            'dav_link_expiration' => $this->davTokenProvider->getTokenExpiration($accessToken)->format('U'),
        ]);
    }

    public function renderDownloadButton(Environment $environment, StoredObject $storedObject, string $title = ''): string
    {
        return $environment->render(
            '@ChillDocStore/Button/button_download.html.twig',
            [
                'document_json' => $this->normalizer->normalize($storedObject, 'json', [AbstractNormalizer::GROUPS => ['read', StoredObjectNormalizer::DOWNLOAD_LINK_ONLY]]),
                'title' => $title,
            ]
        );
    }

    public function renderEditButton(Environment $environment, StoredObject $document, ?array $options = null): string
    {
        return $environment->render(self::TEMPLATE, [
            'document' => $document,
            'options' => $options,
        ]);
    }
}
