<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\AdminBundle\Controller\Page;

use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\AdminBundle\Breadcrumb\Breadcrumb;
use Ferienpass\AdminBundle\Routing\Base58;
use Ferienpass\CoreBundle\Entity\Dbafs;
use Ferienpass\CoreBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Uid\Uuid;

#[IsGranted('ROLE_USER')]
final class FilesController extends AbstractController
{
    public const array STORAGES = [
        'media' => 'media',
        'logos' => 'logos',
        'attachments' => 'attachments',
    ];

    public static function getSubscribedServices(): array
    {
        $services = parent::getSubscribedServices();

        foreach (self::STORAGES as $storage) {
            $services["contao.filesystem.virtual.$storage"] = VirtualFilesystemInterface::class;
        }

        return $services;
    }

    #[Route('/dateien/{!storage}/{path?}', name: 'admin_files', requirements: ['path' => '.+'], defaults: ['storage' => 'media'])]
    public function index(string $storage, ?string $path, Breadcrumb $breadcrumb, EntityManagerInterface $em, Request $request): Response
    {
        /** @noinspection MissingService */
        if (!$this->container->has("contao.filesystem.virtual.$storage")) {
            throw $this->createNotFoundException();
        }

        return $this->render('@FerienpassAdmin/page/files/index.html.twig', [
            'storage' => $storage,
            'path' => $path ?? '',
            'offer' => $request->query->get('offer'),
            'breadcrumb' => $breadcrumb->generate(['files.title', ['route' => 'admin_files']], 'files.title'),
        ]);
    }

    // {uuid} is a DBAFS filesystem UUID. Uuid::fromString() (below) accepts
    // both base58 and RFC4122, so the two routes share one action.
    #[Route('/dateien/{!storage}/{uuid}', name: 'file_show', requirements: ['uuid' => Base58::UUID], priority: 2)]
    #[Route('/dateien/{!storage}/{uuid}', name: 'file_show_legacy', requirements: ['uuid' => '[a-fA-F0-9-]{36}'], priority: 2)]
    public function show(string $storage, string $uuid): Response
    {
        /** @noinspection MissingService */
        if (!$this->container->has("contao.filesystem.virtual.$storage")) {
            throw $this->createNotFoundException();
        }

        /** @var VirtualFilesystemInterface $storageService */
        $storageService = $this->container->get("contao.filesystem.virtual.$storage");
        $item = $storageService->get(Uuid::fromString($uuid));

        if (!$item) {
            throw $this->createNotFoundException();
        }

        // Enforce access for non-admins: only uploaded-by user, attached to accessible offers, or belonging to user's hosts
        if (!$this->isGranted('ROLE_ADMIN')) {
            $user = $this->getUser();
            $allowed = false;

            if ($item->isFile() && $user instanceof User) {
                $meta = $item->getExtraMetadata();
                $model = $meta->get('model') ?? null;

                if ($model instanceof Dbafs) {
                    $allowed = $this->isGranted('VIEW', $model);
                }
            }

            if (!$allowed) {
                throw $this->createNotFoundException();
            }
        }

        return $this->render('@FerienpassAdmin/page/files/show.html.twig', [
            'item' => $item,
            'storage' => $storage,
        ]);
    }
}
