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

use Contao\CoreBundle\Filesystem\FilesystemItem;
use Contao\CoreBundle\Filesystem\FilesystemItemIterator;
use Contao\CoreBundle\Filesystem\MountManager;
use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Ferienpass\AdminBundle\Controller\Page\FilesController;
use Ferienpass\CoreBundle\Entity\Dbafs;
use Ferienpass\CoreBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\LiveComponent\Metadata\UrlMapping;
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;

#[AsLiveComponent(route: 'live_component_admin')]
class Files extends AbstractController
{
    use DefaultActionTrait;

    #[LiveProp]
    public string $storage;

    #[LiveProp]
    public ?string $path = null;

    #[LiveProp(url: new UrlMapping(as: 'd'))]
    public ?string $selected = null;

    #[LiveProp]
    public ?string $offerUuid = null;

    #[LiveProp(writable: true)]
    public string $view = 'grid';

    public function __construct(
        #[Autowire(service: 'contao.filesystem.mount_manager')]
        private readonly MountManager $mountManager
    ) {
    }

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

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

        return $services;
    }

    #[ExposeInTemplate]
    public function contents(): FilesystemItemIterator
    {
        $listing = $this->getFilesystem()->listContents((string) $this->path);

        $user = $this->getUser();

        if (!$user instanceof User) {
            // No user -> no files
            return $listing->filter(static fn (FilesystemItem $item) => false);
        }

        return $listing->filter(function (FilesystemItem $item): bool {
            if (!$item->isFile()) {
                return true;
            }

            return $this->canViewItem($item);
        });
    }

    #[ExposeInTemplate]
    public function current(): ?FilesystemItem
    {
        if (null === $this->selected) {
            return null;
        }

        $item = $this->getFilesystem()->get((string) $this->selected);

        if (!$item instanceof FilesystemItem) {
            return null;
        }

        if (!$this->canViewItem($item)) {
            return null;
        }

        return $item;
    }

    #[LiveAction]
    public function toggleView(#[LiveArg] $view): void
    {
        if (!\in_array($view, ['grid', 'list'], true)) {
            throw new \InvalidArgumentException('Invalid view type');
        }

        $this->view = $view;
    }

    #[LiveListener('fileSelected')]
    public function onFileSelected(#[LiveArg] ?string $path): void
    {
        if (null === $path) {
            $this->selected = null;

            return;
        }

        $item = $this->getFilesystem()->get($path);
        if (!$item instanceof FilesystemItem || !$this->canViewItem($item)) {
            $this->selected = null;

            return;
        }

        $this->selected = $path;
    }

    #[LiveListener('fileDeleted')]
    public function onFileDeleted(): void
    {
        $this->selected = null;
    }

    private function canViewItem(FilesystemItem $item): bool
    {
        if (!$this->getUser() instanceof User) {
            return false;
        }

        // Directories carry no Dbafs model; selection only concerns files.
        if (!$item->isFile()) {
            return false;
        }

        $model = $item->getExtraMetadata()->get('model') ?? null;

        if (!$model instanceof Dbafs) {
            return false;
        }

        return $this->isGranted('view', $model);
    }

    private function getFilesystem(): VirtualFilesystemInterface
    {
        /** @noinspection MissingServiceXml */
        return $this->container->get("contao.filesystem.virtual.$this->storage");
    }
}
