<?php

namespace SGalinski\SgJobs\Controller;

/***************************************************************
 *  Copyright notice
 *
 *  (c) sgalinski Internet Services (https://www.sgalinski.de)
 *
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

use SGalinski\ProjectBase\Domain\Repository\CountryRepository;
use SGalinski\SgJobs\Domain\Model\Job;
use SGalinski\SgJobs\Domain\Model\JobApplication;
use SGalinski\SgJobs\Domain\Repository\CompanyRepository;
use SGalinski\SgJobs\Domain\Repository\DepartmentRepository;
use SGalinski\SgJobs\Domain\Repository\ExperienceLevelRepository;
use SGalinski\SgJobs\Domain\Repository\JobApplicationRepository;
use SGalinski\SgJobs\Domain\Repository\JobRepository;
use SGalinski\SgJobs\Property\TypeConverter\UploadedFileReferenceConverter;
use SGalinski\SgMail\Service\MailTemplateService;
use SGalinski\SgSeo\Service\HeadTagService;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Frontend\Controller\ErrorController;
use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;

/**
 * The joblist plugin controller
 */
class JoblistController extends ActionController {
	// the array key for the error message in the post array
	public const ERROR_KEY_IN_POST = 'error';

	/**
	 * @var CompanyRepository
	 */
	protected $companyRepository;

	/**
	 * @var JobRepository
	 */
	protected $jobRepository;

	/**
	 * @var JobApplicationRepository
	 */
	protected $jobApplicationRepository;

	/**
	 * @var DepartmentRepository
	 */
	protected $departmentRepository;

	/**
	 * @var ExperienceLevelRepository
	 */
	protected $experienceLevelRepository;

	/**
	 * Inject the CompanyRepository
	 *
	 * @param CompanyRepository $companyRepository
	 */
	public function injectCompanyRepository(CompanyRepository $companyRepository): void {
		$this->companyRepository = $companyRepository;
	}

	/**
	 * Inject the DepartmentRepository
	 *
	 * @param DepartmentRepository $departmentRepository
	 */
	public function injectDepartmentRepository(DepartmentRepository $departmentRepository): void {
		$this->departmentRepository = $departmentRepository;
	}

	/**
	 * Inject the ExperienceLevelRepository
	 *
	 * @param ExperienceLevelRepository $experienceLevelRepository
	 */
	public function injectExperienceLevelRepository(ExperienceLevelRepository $experienceLevelRepository): void {
		$this->experienceLevelRepository = $experienceLevelRepository;
	}

	/**
	 * Inject the JobApplicationRepository
	 *
	 * @param JobApplicationRepository $jobApplicationRepository
	 */
	public function injectJobApplicationRepository(JobApplicationRepository $jobApplicationRepository): void {
		$this->jobApplicationRepository = $jobApplicationRepository;
	}

	/**
	 * Inject the JobRepository
	 *
	 * @param JobRepository $jobRepository
	 */
	public function injectJobRepository(JobRepository $jobRepository): void {
		$this->jobRepository = $jobRepository;
	}

	/**
	 * Initialize the indexAction to set the currentPageBrowserPage parameter
	 *
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException
	 */
	public function initializeIndexAction() {
		$currentPageBrowserPage = (int) GeneralUtility::_GP('tx_sgjobs_pagebrowser')['currentPage'];
		if ($currentPageBrowserPage > 0) {
			$this->request->setArgument('currentPageBrowserPage', $currentPageBrowserPage);
		}
	}

	/**
	 * Show all job offers and options to manage them
	 *
	 * @param array $filters
	 * @param int $jobId
	 * @param int $currentPageBrowserPage
	 * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
	 * @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
	 * @throws ImmediateResponseException
	 * @throws \TYPO3\CMS\Core\Package\Exception
	 */
	public function indexAction(array $filters = [], int $jobId = NULL, int $currentPageBrowserPage = 0): ?\Psr\Http\Message\ResponseInterface {
		if ($filters) {
			$this->view->assign('selectedCountry', $filters['filterCountry']);
			$this->view->assign('selectedCompany', $filters['filterCompany']);
			$this->view->assign('selectedLocation', $filters['filterLocation']);
			$this->view->assign('selectedDepartment', $filters['filterDepartment']);
			$this->view->assign('selectedExperienceLevel', $filters['filterExperienceLevel']);
			$this->view->assign('selectedFunction', $filters['filterFunction']);
			$this->view->assign('selectedRemote', $filters['filterRemote']);
		}

		$storagePid = (int) $this->configurationManager->getConfiguration(
			ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
		)['persistence']['storagePid'];

		$this->assignFilterValues($storagePid);
		$this->view->assign('recordPageId', $storagePid);
		$jobLimit = (int) $this->settings['jobLimit'];

		if ($jobId) {
			/** @var Job $job */
			$job = $this->jobRepository->findByUid($jobId);
			if (!$job) {
				throw new \InvalidArgumentException('Given Job Id is invalid!');
			}

			if (version_compare(ExtensionManagementUtility::getExtensionVersion('sg_seo'), '5.0.0', '>=')) {
				$headTagService = GeneralUtility::makeInstance(
					HeadTagService::class,
					TRUE,
					$job->getTitle(),
					$job->getDescription(),
					'&tx_sgjobs_jobapplication[jobId]=' . $jobId
				);
				$headTagService->execute();
			} else {
				$GLOBALS['TSFE']->page['titlebyextension'] = $job->getTitle();
				$GLOBALS['TSFE']->page['description'] = \strip_tags(\substr($job->getDescription(), 0, 200));
				$GLOBALS['TSFE']->page['extensionArgumentsForCanonicalAndHrefLang'] =
					'&tx_sgjobs_jobapplication[jobId]=' . $jobId;
			}

			$jobs = [$job];
			$numberOfPages = 1;
		} else {
			// pagination logic
			$offset = 0;
			if ($currentPageBrowserPage && $jobLimit) {
				$offset = $currentPageBrowserPage * $jobLimit;
			}

			$frontendPluginSettings = $this->configurationManager->getConfiguration(
				ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
			);
			$frontendPluginSettings = $frontendPluginSettings['settings'];

			$isManualSortingAllowed = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sg_jobs']['allowManualSorting'];

			// get all jobs for the current page
			$ordering = (int) $frontendPluginSettings['orderBy'];
			$experienceLevel = (int) $frontendPluginSettings['filterByExperienceLevel'];

			if ($experienceLevel !== 0) {
				$filters['filterExperienceLevel'] = $experienceLevel;
			}

			$this->jobRepository->setAllowManualSorting((bool) $isManualSortingAllowed);
			$jobs = $this->jobRepository->findJobsByFilter($filters, $jobLimit, $offset, $ordering)->toArray();

			// get all jobs for the current page
			$allJobs = $this->jobRepository->findJobsByFilter($filters)->toArray();
			$numberOfPages = (int) ($jobLimit <= 0 ? 0 : \ceil(\count($allJobs) / $jobLimit));
			if ($numberOfPages !== 0 && $currentPageBrowserPage >= $numberOfPages) {
				/** @var ErrorController $errorController */
				$errorController = GeneralUtility::makeInstance(ErrorController::class);
				$response = $errorController->pageNotFoundAction(
					$GLOBALS['TYPO3_REQUEST'],
					'The requested page does not exist',
					['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
				);
				throw new ImmediateResponseException($response);
			}
		}

		$this->view->assign('jobs', $jobs);
		$this->view->assign('limit', $jobLimit);
		$this->view->assign('numberOfPages', $numberOfPages);

		if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
			return NULL;
		}
		else {
			return $this->htmlResponse();
		}
	}

	/**
	 * Renders the application form with an optional job
	 *
	 * @param JobApplication|null $applyData
	 * @param string $error
	 * @param int $jobId
	 * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 */
	public function applyFormAction(JobApplication $applyData = NULL, string $error = '', int $jobId = NULL): ?\Psr\Http\Message\ResponseInterface {
		if ($error !== '') {
			$this->view->assign('internalError', $error);
			$this->request->setArgument('error', NULL);
		}

		if ($jobId === NULL && $this->settings['disallowUnsolicitedApplication']) {
			$uriBuilder = $this->uriBuilder;
			$uri = $uriBuilder
				->setTargetPageUid($this->settings['offersPage'])
				->build();
			$this->redirectToUri($uri, 0, 301);
		}

		$folderName = NULL;
		try {
			$folderName = $this->request->getArgument('folderName');
		} catch (\Exception $exception) {
			// this happens for the initial call, but works for any follow-up call as the form validation
			// throws you back to this one if something has failed
		}

		if ($folderName === NULL) {
			$folderName = \md5(\uniqid('sgjobs-', TRUE));
			$this->request->setArgument('folderName', $folderName);
		}
		$this->view->assign('folderName', $folderName);

		$job = NULL;
		if ($jobId !== NULL) {
			/** @var Job $job */
			$job = $this->jobRepository->findByUid($jobId);
			if ($job) {
				if (version_compare(ExtensionManagementUtility::getExtensionVersion('sg_seo'), '5.0.0', '>=')) {
					$headTagService = GeneralUtility::makeInstance(
						HeadTagService::class,
						FALSE,
						$job->getTitle(),
						$job->getDescription(),
						'&tx_sgjobs_jobapplication[jobId]=' . $jobId
					);
					$headTagService->execute();
				} else {
					$GLOBALS['TSFE']->page['titlebyextension'] = $job->getTitle();
					$GLOBALS['TSFE']->page['description'] = \strip_tags(\substr($job->getDescription(), 0, 200));
					$GLOBALS['TSFE']->page['extensionArgumentsForCanonicalAndHrefLang'] =
						'&tx_sgjobs_jobapplication[jobId]=' . $jobId;
				}
			}
			$this->view->assign('job', $job);
		} else {
			$storagePid = (int) $this->configurationManager->getConfiguration(
				ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
			)['persistence']['storagePid'];
			$this->view->assign('companies', $this->companyRepository->getAllCompanies($storagePid));
		}

		// display country options
		$context = GeneralUtility::makeInstance(Context::class);
		$sysLanguageUid = $context->getPropertyFromAspect('language', 'id');
		$site = GeneralUtility::makeInstance(SiteFinder::class)
			->getSiteByPageId($GLOBALS['TSFE']->id)
			->getLanguageById($sysLanguageUid);
		$countries = [];
		if (ExtensionManagementUtility::isLoaded('project_base')) {
			$countryRepository = $this->objectManager->get(CountryRepository::class);
			$countries = $countryRepository->findAllOrderedByLanguage($site->getTwoLetterIsoCode());
		}
		$this->view->assign('countries', $countries);
		$this->view->assign('sysLanguageUid', $sysLanguageUid);

		$allowedMimeTypes = $this->settings['allowedMimeTypes'];
		$this->view->assign('allowedMimeTypes', $allowedMimeTypes);
		$allowedFileExtensions = $this->settings['allowedFileExtensions'];
		$this->view->assign('allowedFileExtensions', $allowedFileExtensions);

		if ($applyData === NULL) {
			/** @noinspection CallableParameterUseCaseInTypeContextInspection */
			$applyData = $this->objectManager->get(JobApplication::class);
			if ($job) {
				$applyData->setJobId($job->getJobId());
			}
		}

		$this->view->assign('applyData', $applyData);
		$this->view->assign('maxFileSize', $this->settings['allowedMaxFileSize']);
		$this->view->assign('maxFileSizeMb', ((int) $this->settings['allowedMaxFileSize'] / 1000) . ' MByte');
		$this->view->assign(
			'maxFileSizeMessage', LocalizationUtility::translate('error.maxFileSizeMessage', 'sg_jobs')
		);

		// This fixes a bug in the form ViewHelper that wants to serialize a Model with closures in it
		$arguments = $this->request->getArguments();
		if ($arguments['applyData']) {
			$arguments['applyData'] = (string) $arguments['applyData'];
			$this->request->setArguments($arguments);
		}

		if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
			return NULL;
		}
		else {
			return $this->htmlResponse();
		}
	}

	/**
	 * Pre-apply action setup, configures model-property mapping and handles file upload
	 *
	 * @throws NoSuchArgumentException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 */
	protected function initializeApplyAction(): ?\Psr\Http\Message\ResponseInterface {
		try {
			$uniqueFolderName = $this->request->getArgument('folderName');
		} catch (NoSuchArgumentException $exception) {
			$exceptionMessage = 'Some file could not be uploaded. Is it too large?';

			if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
				$this->redirect('applyForm', NULL, NULL, ['error' => $exceptionMessage]);
				return NULL;
			}
			else {
				return $this->redirect('applyForm', NULL, NULL, ['error' => $exceptionMessage]);
			}
		}
		$propertyMappingConfiguration = $this->arguments->getArgument('applyData')->getPropertyMappingConfiguration();
		$propertyMappingConfiguration->forProperty('job')->allowAllProperties();

		foreach (['coverLetter', 'cv', 'certificate'] as $property) {
			$typeConverter = $this->objectManager->get(UploadedFileReferenceConverter::class);
			$typeConverter->setAllowedFileExtensions(
				$this->settings['allowedFileExtensions'] ?? $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
			);
			$typeConverter->setUploadFolder('1:/JobApplication/temp/' . $uniqueFolderName);
			$typeConverter->setTargetUploadFileName($property);
			$propertyMappingConfiguration->forProperty($property)->setTypeConverter($typeConverter);
		}
		if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
			return NULL;
		}
		else {
			return $this->htmlResponse();
		}
	}

	/**
	 * Moves the application files from temporary to permanent storage
	 *
	 * @param JobApplication $applicationData
	 * @param string $folderName
	 * @return void
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 */
	protected function submitApplicationFiles(JobApplication $applicationData, $folderName): void {
		$resourceFactory = $this->objectManager->get(ResourceFactory::class);
		$newName = \date('Ymd-His') . '_' . $applicationData->getJobId() . '-' . $applicationData->getFirstName()
			. '-' . $applicationData->getLastName();
		$storage = $resourceFactory->getStorageObject(1);
		$applicationFilePath = Environment::getPublicPath() . '/' . $storage->getConfiguration(
			)['basePath'] . 'JobApplication/' . $folderName .
			'/' . $newName . '.csv';
		$this->writeApplicationFile($applicationData, $applicationFilePath);
	}

	/**
	 * Saves the application send by the applyFormAction
	 *
	 * @param JobApplication $applyData
	 * @throws NoSuchArgumentException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 */
	public function applyAction(JobApplication $applyData): ?\Psr\Http\Message\ResponseInterface {
		$folderName = $this->request->getArgument('folderName');

		try {
			$applyData->setPid($GLOBALS['TSFE']->id);
			$job = $applyData->getJob();
			// look for a configured default job, in case of unsolicited application
			if ((!$job || $applyData->getJobId() === NULL) && $applyData->getCompany() !== NULL) {
				$applyData->setJobId($applyData->getCompany()->getJobId());
			}

			if ($applyData->_isNew()) {
				$this->jobApplicationRepository->add($applyData);
			} else {
				$this->jobApplicationRepository->update($applyData);
			}

			$this->moveTmpFolder($folderName);
			$this->submitApplicationFiles($applyData, $folderName);

			$mailService = $this->objectManager->get(
				MailTemplateService::class, 'application_mail', 'sg_jobs',
				$this->getApplicationMailMarkers($applyData)
			);

			// get email from the job contact, fallback is TS settings
			$contact = NULL;
			if ($job !== NULL) {
				$contact = $job->getContact();
			}

			if ($contact !== NULL) {
				$mailService->setToAddresses($contact->getEmail());
			} else {
				$company = $applyData->getCompany();
				if (($company !== NULL) && $company->getContact() !== NULL) {
					$mailService->setToAddresses($company->getContact()->getEmail());
				}
			}

			$mailService->setMarkers(
				[
					'application' => $applyData,
				]
			);
			$mailService->setIgnoreMailQueue(TRUE);

			// add attachments for each file
			$coverLetter = $applyData->getCoverLetter();
			if ($coverLetter) {
				$mailService->addFileResourceAttachment($coverLetter);
			}

			$cv = $applyData->getCv();
			if ($cv) {
				$mailService->addFileResourceAttachment($cv);
			}

			$certificate = $applyData->getCertificate();
			if ($certificate) {
				$mailService->addFileResourceAttachment($certificate);
			}

			$mailService->sendEmail();

			$redirectPageUid = (int) $this->settings['redirectPage'];
			if ($redirectPageUid) {
				$contentObject = $this->configurationManager->getContentObject();
				if ($contentObject) {
					$url = $contentObject->getTypoLink_URL($redirectPageUid);
					$this->redirectToUri($url);
				}
			}

			$this->redirect('applyForm');

		} catch (\Exception $exception) {
			$this->deleteTmpFolder($folderName);
			$job = $applyData->getJob();
			$jobId = $job !== NULL ? $job->getUid() : NULL;
			$this->request->setArgument('folderName', $folderName);
			$this->forward(
				'applyForm', NULL, NULL,
				['applyData' => $applyData, 'error' => $exception->getMessage(), 'jobId' => $jobId]
			);
		}

		if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
			return NULL;
		}
		else {
			return $this->htmlResponse();
		}
	}

	/**
	 * Assign filter values
	 *
	 * @param int $rootPageId
	 * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
	 */
	protected function assignFilterValues($rootPageId): ?\Psr\Http\Message\ResponseInterface {
		$countries = $this->companyRepository->getAllCountries($rootPageId);
		$this->view->assign('countries', $countries);

		$cities = $this->companyRepository->getAllCities($rootPageId);
		$this->view->assign('cities', $cities);

		$companies = $this->companyRepository->getAllCompanyNames($rootPageId);
		$this->view->assign('companies', $companies);

		$departments = $this->departmentRepository->findAll();
		$this->view->assign('departments', $departments);

		$experienceLevels = $this->experienceLevelRepository->findAll();
		$this->view->assign('experienceLevels', $experienceLevels);

		if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
			return NULL;
		}
		else {
			return $this->htmlResponse();
		}
	}

	/**
	 * Returns the application mail markers
	 *
	 * @param JobApplication $applyData
	 * @return array
	 */
	protected function getApplicationMailMarkers(JobApplication $applyData): array {
		$location = '';
		if ($applyData->getCompany() !== NULL) {
			$location = $applyData->getCompany()->getCity();
		}

		return [
			'salutation' => $applyData->getGender(),
			'location' => $location,
			'firstname' => $applyData->getFirstName(),
			'lastname' => $applyData->getLastName(),
			'street' => $applyData->getStreet(),
			'city' => $applyData->getCity(),
			'country' => $applyData->getCountry(),
			'phone' => $applyData->getPhone(),
			'mobile' => $applyData->getMobile(),
			'email' => $applyData->getEmail(),
			'message' => $applyData->getFirstName()
		];
	}

	/**
	 * Writes the application files
	 *
	 * @param JobApplication $data
	 * @param string $filePath
	 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
	 */
	protected function writeApplicationFile(JobApplication $data, $filePath): void {
		$coverLetter = '';
		$coverLetterObject = $data->getCoverLetter();
		if ($coverLetterObject) {
			$coverLetterObject = $coverLetterObject->getOriginalResource();
			if ($coverLetterObject) {
				$coverLetter = $coverLetterObject->getPublicUrl();
			}
		}

		$cv = '';
		$cvObject = $data->getCv();
		if ($cvObject) {
			$cvObject = $cvObject->getOriginalResource();
			if ($cvObject) {
				$cv = $cvObject->getPublicUrl();
			}
		}

		$certificate = '';
		$certificateObject = $data->getCertificate();
		if ($certificateObject) {
			$certificateObject = $certificateObject->getOriginalResource();
			if ($certificateObject) {
				$certificate = $certificateObject->getPublicUrl();
			}
		}

		$dataToInsertArr = [
			$data->getJobId(),
			$data->getFirstName(),
			$data->getLastName(),
			$data->getGender(),
			$data->getCountry(),
			$data->getBirthDate(),
			$data->getEducation(),
			$data->getStreet(),
			$data->getZip(),
			$data->getCity(),
			$data->getNationality(),
			$data->getPhone(),
			$data->getEmail(),
			$coverLetter,
			$cv,
			$certificate,
			$data->getMessage()
		];

		try {
			GeneralUtility::mkdir_deep(\dirname($filePath));
			$file = \fopen($filePath, 'wb+');
			\fputcsv($file, $dataToInsertArr);
			\fclose($file);
		} catch (\RuntimeException $exception) {
			$this->redirect('applyForm', NULL, NULL, ['error' => $exception->getMessage()]);
		}
	}

	/**
	 * Move the temp folder to its proper location
	 *
	 * @param string $folderName
	 * @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
	 * @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException
	 * @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
	 * @throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException
	 */
	protected function moveTmpFolder(string $folderName): void {
		// Move uploaded files & csv fo real folder and delete the tmp folder
		/** @var ResourceFactory $resourceFactory */
		$resourceFactory = $this->objectManager->get(ResourceFactory::class);
		$storage = $resourceFactory->getStorageObject(1);
		$folder = $storage->getFolder('/JobApplication/');
		if (!$storage->hasFolderInFolder($folderName, $folder)) {
			$newFolder = $storage->createFolder($folderName, $folder);
		} else {
			$newFolder = $storage->getFolder('/JobApplication/' . $folderName);
		}
		$tempFolder = $storage->getFolder('/JobApplication/temp/' . $folderName);
		$filesToMove = $storage->getFilesInFolder($tempFolder);
		foreach ($filesToMove as $fileToMove) {
			$storage->moveFile($fileToMove, $newFolder);
		}
	}

	/**
	 * Delete uploaded files in tmp folder
	 *
	 * @param string $folderName
	 */
	protected function deleteTmpFolder($folderName): void {
		/** @var ResourceFactory $resourceFactory */
		$resourceFactory = $this->objectManager->get(ResourceFactory::class);
		$storage = $resourceFactory->getStorageObject(1);
		try {
			$tempFolder = $storage->getFolder('/JobApplication/temp/' . $folderName);
			$storage->deleteFolder($tempFolder, TRUE);
		} catch (\Exception $exception) {
			// folder is already deleted for some reason
		}
	}

	/**
	 * If for any reason something goes wrong, delete the tmp upload folder
	 *
	 * @return void
	 * @throws NoSuchArgumentException
	 */
	public function errorAction() {
		if ($this->request->hasArgument('folderName')) {
			$folderName = $this->request->getArgument('folderName');
			$this->deleteTmpFolder($folderName);
		}

		parent::errorAction();
	}

	/**
	 * Build Typo3 11 Response
	 * @param string|NULL $html
	 * @return \Psr\Http\Message\ResponseInterface
	 */
	protected function htmlResponse(string $html = null): \Psr\Http\Message\ResponseInterface
	{
		return $this->responseFactory->createResponse()
			->withHeader('Content-Type', 'text/html; charset=utf-8')
			->withBody($this->streamFactory->createStream($html ?? $this->view->render()));
	}
}