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

use Chill\DocStoreBundle\AsyncUpload\Driver\LocalStorage\StoredObjectManager;
use Chill\DocStoreBundle\AsyncUpload\Driver\LocalStorage\TempUrlLocalStorageGenerator;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Controller to deal with local storage operation.
 */
final readonly class StoredObjectContentToLocalStorageController
{
    public function __construct(
        private StoredObjectManager $storedObjectManager,
        private TempUrlLocalStorageGenerator $tempUrlLocalStorageGenerator,
    ) {}

    #[Route('/public/stored-object/post', name: 'chill_docstore_storedobject_post', methods: ['POST'])]
    public function postContent(Request $request): Response
    {
        $prefix = $request->query->get('prefix', '');

        if ('' === $prefix) {
            throw new BadRequestHttpException('Prefix parameter is missing');
        }

        if (0 === $maxFileSize = $request->request->getInt('max_file_size', 0)) {
            throw new BadRequestHttpException('Max file size is not set or equal to zero');
        }

        if (1 !== $maxFileCount = $request->request->getInt('max_file_count', 0)) {
            throw new BadRequestHttpException('Max file count is not set or equal to zero');
        }

        if (0 === $expiration = $request->request->getInt('expires', 0)) {
            throw new BadRequestHttpException('Expiration is not set or equal to zero');
        }

        if ('' === $signature = $request->request->get('signature', '')) {
            throw new BadRequestHttpException('Signature is not set or is a blank string');
        }

        if (!$this->tempUrlLocalStorageGenerator->validateSignaturePost($signature, $prefix, $expiration, $maxFileSize, $maxFileCount)) {
            throw new AccessDeniedHttpException('Invalid signature');
        }

        $keyFiles = $request->files->keys();

        if ($maxFileCount < count($keyFiles)) {
            throw new AccessDeniedHttpException('More files than max file count');
        }

        if (0 === count($keyFiles)) {
            throw new BadRequestHttpException('Zero files given');
        }

        foreach ($keyFiles as $keyFile) {
            /** @var UploadedFile $file */
            $file = $request->files->get($keyFile);

            if ($maxFileSize < strlen($file->getContent())) {
                throw new AccessDeniedHttpException('File is too big');
            }

            if (!str_starts_with((string) $keyFile, $prefix)) {
                throw new AccessDeniedHttpException('Filename does not start with signed prefix');
            }

            $this->storedObjectManager->writeContent($keyFile, $file->getContent());
        }

        return new Response(status: Response::HTTP_NO_CONTENT);
    }

    #[Route('/public/stored-object/operate', name: 'chill_docstore_stored_object_operate', methods: ['GET', 'HEAD'])]
    public function contentOperate(Request $request): Response
    {
        if ('' === $objectName = $request->query->get('object_name', '')) {
            throw new BadRequestHttpException('Object name parameter is missing');
        }

        if (0 === $expiration = $request->query->getInt('exp', 0)) {
            throw new BadRequestHttpException('Expiration is not set or equal to zero');
        }

        if ('' === $signature = $request->query->get('sig', '')) {
            throw new BadRequestHttpException('Signature is not set or is a blank string');
        }

        if (!$this->tempUrlLocalStorageGenerator->validateSignature($signature, strtoupper($request->getMethod()), $objectName, $expiration)) {
            throw new AccessDeniedHttpException('Invalid signature');
        }

        if (!$this->storedObjectManager->existsContent($objectName)) {
            throw new NotFoundHttpException('Object does not exists on disk');
        }

        return match ($request->getMethod()) {
            'GET' => new Response($this->storedObjectManager->readContent($objectName)),
            'HEAD' => new Response(''),
            default => throw new BadRequestHttpException('method not supported'),
        };
    }
}
