<?php

declare(strict_types=1);

/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

namespace TYPO3\CMS\Backend\Controller;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Attribute\AsController;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\LinkHandling\LinkService;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
use TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException;
use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;

/**
 * @internal
 */
#[AsController]
final readonly class LinkController
{
    public function __construct(
        private LinkService $linkService,
        private ResourceFactory $resourceFactory
    ) {}

    public function resourceAction(ServerRequestInterface $request): ResponseInterface
    {
        $identifier = $request->getParsedBody()['identifier'] ?? null;
        $resource = null;

        try {
            if ($identifier) {
                $resource = $this->resourceFactory->retrieveFileOrFolderObject($identifier);
            }
            if (!$resource instanceof File && !$resource instanceof Folder) {
                throw new \InvalidArgumentException('Resource must be a file or a folder', 1679039649);
            }
            if ($resource->getStorage()->isFallbackStorage()) {
                throw new InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1679039650);
            }
            if ($resource instanceof File) {
                if (!$resource->checkActionPermission('read')) {
                    throw new InsufficientFileAccessPermissionsException('You are not allowed to access this file', 1779001351);
                }
                $parameters = [
                    'type' => LinkService::TYPE_FILE,
                    'file' => $resource,
                ];
            }
            if ($resource instanceof Folder) {
                // Note: No explicit `$resource->checkActionPermission('read')` check here, as that would
                // be a no-op since `ResourceStorage::getFolder()` calls `assureFolderReadPermission()`
                // and throws `InsufficientFolderAccessPermissionsException`
                $parameters = [
                    'type' => LinkService::TYPE_FOLDER,
                    'folder' => $resource,
                ];
            }
            $link = $this->linkService->asString($parameters);
        } catch (InsufficientFileAccessPermissionsException|InsufficientFolderAccessPermissionsException $exception) {
            $message = match ($exception->getCode()) {
                1679039650 => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_resource.xlf:ajax.error.message.resourceOutsideOfStorages'),
                default => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_resource.xlf:ajax.error.message.resourceNoPermissionRead'),
            };

            return new JsonResponse($this->getResponseData(false, $message));
        } catch (\Exception $exception) {
            $message = match ($exception->getCode()) {
                1679039649 => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_resource.xlf:ajax.error.message.resourceNotFileOrFolder'),
                default => $exception->getMessage(),
            };

            return new JsonResponse($this->getResponseData(false, $message));
        }

        return new JsonResponse($this->getResponseData(true, null, $link));
    }

    /**
     * Prepare response data for a JSON response
     */
    private function getResponseData(bool $success, ?string $message = null, ?string $link = null): array
    {
        $flashMessageQueue = new FlashMessageQueue('backend');
        if ($message) {
            $flashMessageQueue->enqueue(
                new FlashMessage(
                    $message,
                    $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_resource.xlf:ajax.' . ($success ? 'success' : 'error')),
                    $success ? ContextualFeedbackSeverity::OK : ContextualFeedbackSeverity::ERROR
                )
            );
        }
        return [
            'success' => $success,
            'status' => $flashMessageQueue,
            'link' => $link,
        ];
    }

    private function getLanguageService(): LanguageService
    {
        return $GLOBALS['LANG'];
    }
}
