<?php

namespace Internetgalerie\IgDatapoolFe\Property\TypeConverter;

use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Extbase\Security\Cryptography\HashService;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
use TYPO3\CMS\Core\Utility\MathUtility;
use Internetgalerie\IgDatapoolFe\Services\FormService;
use TYPO3\CMS\Core\Resource\DuplicationBehavior;
use TYPO3\CMS\Extbase\Property\Exception;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Resource\File as FileResource;
use TYPO3\CMS\Core\Resource\Security\FileNameValidator;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Property\Exception\TypeConverterException;
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
use TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter;

/**
 * Class FileConverter
 */
class FileConverter extends AbstractTypeConverter
{
    protected $debug = false;
    protected $debugLog='/tmp/log2.txt';

    /**
     * @var array<string>
     */
    protected $sourceTypes = [ 'array','integer', 'string' ];

    /**
     * @var string
     */
    protected $targetType =  FileResource::class; //\TYPO3\CMS\Extbase\Domain\Model\File::class;
    //protected $targetType =  'int'; //\TYPO3\CMS\Extbase\Domain\Model\File::class;


    /**
     * Needs to take precedence over the available FileConverter?????
     *
     * @var integer
     */
    protected $priority = 22;

    /**
     * @var ResourceFactory
     */
    protected $resourceFactory;

    /**
     * @var HashService
     */
    protected $hashService;

    /**
     * @var PersistenceManager
     */
    protected $persistenceManager;

    /**
     * @var \TYPO3\CMS\Core\Resource\FileInterface[]
     */
    //protected $convertedResources = [];
    /**
     * @var FileRepository
     */
    protected $fileRepository = null;


    protected $fileFactory;

    public function injectFileFactory(ResourceFactory $fileFactory): void
    {
        $this->fileFactory = $fileFactory;
    }



    public function __construct()
    {
        $this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
        $this->hashService = GeneralUtility::makeInstance(HashService::class);
        $this->fileRepository = GeneralUtility::makeInstance(FileRepository::class);
        $this->persistenceManager = GeneralUtility::makeInstance(PersistenceManager::class);
    }


    public function log($text): void
    {
        $fp = fopen($this->debugLog, 'a');
        fputs($fp, $text ."\n");
        fclose($fp);
    }
    public function log_var_dump($var): void
    {
        $fp = fopen($this->debugLog, 'a');
        //ob_start();      var_dump($var);      $text = ob_get_clean();
        $text = var_export($var, true);
        fputs($fp, 'File-DUMP[FileReferenceConverter]=' .  $text ."\n");
        fclose($fp);
    }


    /*
     *  nicht bei Extension SYSEXT: from konvertieren
     * @todo verbessern - nur bei vorhanden sein von __dp.....
     */
    public function canConvertFrom($source, string $targetType): bool
    {
        if ($this->debug) {
            $this->log('canConvertFrom FileConverter');
        }
        foreach ($_POST as $name=>$val) {
            if ($name=='tx_form_formframework') {
                return false;
            }
        }
        if (isset($source['__dpObject']) &&$source['__dpObject']=='File') {
            return true;
        }
        return false;
    }

    /**
     * Actually convert from $source to $targetType, taking into account the fully
     * built $convertedChildProperties and $configuration.
     *
     * @param  string|integer                                                    $source
     * @param  string                                                            $targetType
     * @param  array                                                             $convertedChildProperties
     * @param PropertyMappingConfigurationInterface $configuration
     * @throws Exception
     * @return \TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder
     * @api
     */
    public function convertFrom($in_source, string $targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null): ?FileResource
    {
        /*
         * If we already converted it..
         */
        if ($this->debug) {
            $this->log("\n-----------------------------------------------------\nINIT: FileConverter.php");
        }
        //var_dump($in_source,$this->getSupportedSourceTypes(), $this->getSupportedTargetType());
        //        return new FileResource();
        //if (isset($this->convertedResources[md5(serialize($in_source))])) {
        //    return $this->convertedResources[md5(serialize($in_source))];
        //}

        // we have 2 kinds of references:
        // MODEL --> TYPO3\\CMS\\Extbase\\Domain\\Model\\File  --> TYPO3\CMS\Core\Resource\File


        if ($this->debug) {
            $this->log_var_dump($in_source);
        }

        $sub_val = null;
        foreach ($in_source as $key => $val) {
            if ($key=='__identity') {
                $fileUid = intval($val);
            }
            if (is_array($val)) {
                $sub_val = $key;
            }
        }
        //$this->log('sub_val='. $sub_val );
        if (is_null($sub_val)) {
            $source=$in_source;
        } else {
            $source=$in_source[$sub_val];
        } // ist noch nicht toll

        //$this->log_var_dump( $source );
        if ($this->debug) {
            $this->log('IN :'. $source['uid']);
        }
        if (isset($source['uid'])) {
            $fr_delete_uid = (int) $this->hashService->validateAndStripHmac($source['uid']);
            $fileUid = (int) $this->hashService->validateAndStripHmac($source['uid']);
            if ($this->debug) {
                $this->log('OUT :'. $this->hashService->validateAndStripHmac($source['uid']));
            }
        } else {
            $fr_delete_uid = null;
        }
        $fr_uid= $fileUid;
        if ($this->debug) {
            $this->log('uid=' . $fileUid . ', fr_delete_uid='.$fr_delete_uid);
        }


        // File schon vorhanden
        if (isset($source['langFile']) && $source['langFile']!='') {
            //$this->log('langFile='. $source['langFile'] . ', fr_uid=' . $fr_uid);
            $langFileId = intval($this->hashService->validateAndStripHmac($source['langFile']));
            if (MathUtility::canBeInterpretedAsInteger($langFileId)
                && $langFileId>0
            ) {
                //($in_source['description']!='' || $in_source['title']!='') ) {
                // Here we have a reference ID.. so do nothing, basically ^^
                // make sure no one has tempered with it.
                //$this->log('langFile: canBeInterpretedAsInteger');



                //store it for later use..
                //$this->convertedResources[md5(serialize($source))] = $fileReference;
            }
        } else {
            // Falls FileReference vorhanden und geloescht markiert->loeschen
            if ($this->debug) {
                $this->log('delete???? fr_delete_uid='. $fr_delete_uid .' und remove='.$source['remove']);
            }
            if ($fr_delete_uid>0 &&  (int)$source['remove']==1) {
                // Delete reference

                $reference = $this->fileRepository->findByUid($fr_delete_uid);
                //$this->log('fileReference remove uid='.  $fr_delete_uid);
                //$this->log_var_dump( $reference );
                if (is_object($reference)) {
                    if ($this->debug) {
                        $this->log('DELETE FROM sys_file_reference WHERE uid=' . $reference->getUid());
                    }
                    $this->fileRepository->remove($reference);
                }
            }

            // Neues File
            if (isset($source['newFile']) && $source['newFile']!='') {

                //$this->log('newFile='. $source['newFile'] . ', origFilename=' . $source['origFilename']);
                // it is atemporary string
                $base = Environment::getPublicPath() .'/';
                $filePath = PathUtility::getAbsolutePathOfRelativeReferencedFileOrPath($base, 'fileadmin/_temp_/' . $source['newFile']);
                //$this->log('filePath='.$filePath);
                if ($this->debug) {
                    $this->log('test ' . $filePath);
                }

                // if there was a problem and our source file doesn't exist, we just return NULL
                if (!is_file($filePath)) {
                    return null;
                }

                // check again for php and illegal stuff
                if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($source['origFilename'])) {
                    throw new TypeConverterException('Uploading files with PHP file extensions is not allowed!', 1399312430);
                }
                // get the uploaded file object
                //$this->resourceFactory=GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceFactory::class);
                //var_dump($this->resourceFactory);exit(0);
                $uploadedFile = $this->resourceFactory->retrieveFileOrFolderObject($filePath);
                if ($this->debug) {
                    $this->log('uploadedFile');
                }

                // do the checks here VALIDATION
                $options = $this->hashService->validateAndStripHmac($source['options']);
                $options = unserialize(base64_decode($options));
                if (isset($options['maxSize'])) {
                    $size = $uploadedFile->getSize();
                    if ($size > $options['maxSize'] * 1024) {
                        $uploadedFile->delete();
                        return 'validator.file.tooBig';
                    }
                }

                if (isset($options['allowedExtensions']) && $options['allowedExtensions'] != '*') {
                    $ext = $uploadedFile->getExtension();
                    if (!in_array($ext, $options['allowedExtensions'])) {
                        $uploadedFile->delete();
                        return 'validator.file.invalidType';
                    }
                }

                if (isset($options['minImageSize'])) {
                    $minSize = $options['minImageSize'];
                    if (stripos((string) $minSize, 'mp')) {
                        $minSize = floatval($minSize) * (1024 * 1024);
                    } else {
                        $minSize = floatval($minSize);
                    }

                    $size = getimagesize($uploadedFile->getForLocalProcessing(false));

                    $fullSize = $size[0] * $size[1];
                    if ($fullSize > 0 && $fullSize < $minSize) {
                        $uploadedFile->delete();
                        return 'validator.image.tooSmall';
                    }
                }

                // create upload folder
                $uploadFolder = $this->resourceFactory->retrieveFileOrFolderObject('1:/');

                $formService =GeneralUtility::makeInstance(FormService::class);
                $request = $formService->getController()->getRequest();

                // move to fileadmin/datapool_upload/extension/controller/origFilename.jpg
                if ($options['folder']) {
                    if (str_starts_with((string) $options['folder'], '/')) {
                        $folderName = substr((string) $options['folder'], 1);
                    } else {
                        $folderName = 'datapool_upload/' . $request->getControllerExtensionName() . '/' . $request->getControllerName()  . '/' . $options['folder'];
                    }
                } else {
                    $folderName = 'datapool_upload/' . $request->getControllerExtensionName() . '/' . $request->getControllerName();
                }

                if (!$uploadFolder->hasFolder($folderName)) {
                    if ($this->debug) {
                        $this->log('create Folder: ' . $folderName);
                    }
                    $uploadFolder = $uploadFolder->createFolder($folderName);
                } else {
                    $uploadFolder = $uploadFolder->getSubfolder($folderName);
                }
                // rename and move
                $uploadedFile = $uploadedFile->moveTo($uploadFolder, $source['origFilename'], DuplicationBehavior::RENAME);
                if ($this->debug) {
                    $this->log('call:'. $uploadedFile::class);
                }

                return $uploadedFile;
            }

            // File schon vorhanden
            /////////////////////////
            if (isset($source['curFile']) && $source['curFile']!='') { //  && isset($source['origFilename']) && $source['origFilename']!=''
                if ($this->debug) {
                    $this->log('curFile='. $source['curFile'] . ', origFilename=' . $source['origFilename']);
                }
                if (str_starts_with((string) $source['origFilename'], 'file:')) {
                    $origFilename=substr((string) $source['origFilename'], 5);
                } else {
                    $origFilename=$source['origFilename'];
                }
                if ($this->debug) {
                    $this->log('test canBeInterpretedAsInteger with='. $origFilename);
                }
                if (MathUtility::canBeInterpretedAsInteger($origFilename)) {
                    // Here we have a reference ID.. so do nothing, basically ^^
                    // make sure no one has tempered with it.
                    if ($this->debug) {
                        $this->log('canBeInterpretedAsInteger');
                    }
                    $fileId = intval($this->hashService->validateAndStripHmac($source['curFile']));

                    if ($this->debug) {
                        $this->log('fileId='.$fileId .', uid='.$source['uid'] .'fr_uid='.$fr_uid);
                    }
                    // DADADADADADADA

                    // @todo - sieht nicht gut aus DA
                    if ($this->debug) {
                        $this->log('search file['.$fileId.']');
                    }
                    $file = $this->fileRepository->findByUid($fileId); // $fr_uid
                    if ($this->debug) {
                        if ($file===null) {
                            // File Reference existiert in DB nicht mehr -> wird geloescht da null
                            $this->log('found file['.$fileId.'] NOT FOUND');
                        } else {
                            $this->log('found file['.$fileId.'] uid='.$file->getUid());
                        }
                    }
                }
            }
            if ($this->debug) {
                $this->log('return FileReference');
            }

            return $file;//$fileReference->_getProperty('originalFileIdentifier'); //count($fileReference);
        }

        if ($this->debug) {
            $this->log('return FileReference=Null');
        }
        return null;
    }
}
