<?php

namespace Sibs\Sibs\Model\Config\Backend;

use Exception;
use Magento\Config\Model\Config\Backend\File;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\LocalizedException;

class AppleFileType extends File
{
    /**
     * @var string
     */
    public const APPLE_FILENAME = 'apple-developer-merchantid-domain-association';

    /**
     * @var string
     */
    public const APPLE_FILE_EXTENSION = 'txt';

    /**
     * @var string
     */
    public const APPLE_DIRECTORY = '.well-known';

    /**
     * @var \Magento\Framework\Filesystem\Io\File
     */
    private $file;

    /**
     * @var \Magento\Framework\Filesystem\DirectoryList
     */
    private $dir;

    /**
     * @var string
     */
    protected $appleDir;

    /**
     * @var \Magento\Framework\Filesystem\Directory\WriteInterface
     */
    protected $_mediaDirectory;

    /**
     * @var string
     */
    protected $appleFileDirectory;

    /**
     * @var string
     */
    protected $appleFilePath;

    /**
     * @var \Magento\Framework\Filesystem\Directory\WriteInterface
     */
    protected $_pubDirectory;

    /**
     * @var string
     */
    protected $rootAppleFileDirectory;

    /**
     * @var string
     */
    protected $rootAppleFilePath;

    /**
     * @param \Magento\Framework\Model\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $config
     * @param \Magento\Framework\App\Cache\TypeListInterface $cacheTypeList
     * @param \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory
     * @param \Magento\Config\Model\Config\Backend\File\RequestData\RequestDataInterface $requestData
     * @param \Magento\Framework\Filesystem $filesystem
     * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
     * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\Model\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\App\Config\ScopeConfigInterface $config,
        \Magento\Framework\App\Cache\TypeListInterface $cacheTypeList,
        \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
        \Magento\Config\Model\Config\Backend\File\RequestData\RequestDataInterface $requestData,
        \Magento\Framework\Filesystem $filesystem,
        \Magento\Framework\Filesystem\Io\File $file,
        \Magento\Framework\Filesystem\DirectoryList $dir,
        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
        \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
        array $data = []
    ) {
        $this->file = $file;
        $this->dir = $dir;

        parent::__construct(
            $context, $registry, $config, $cacheTypeList, $uploaderFactory,
            $requestData, $filesystem, $resource, $resourceCollection, $data
        );

        // Root Directory
        $this->_mediaDirectory        = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
        $this->rootAppleFileDirectory = $this->dir->getRoot() . '/' . self::APPLE_DIRECTORY;
        $this->rootAppleFilePath      = $this->rootAppleFileDirectory . '/' . self::getAppleFileName();

        // Pub Directory
        $this->_pubDirectory      = $filesystem->getDirectoryWrite(DirectoryList::PUB);
        $this->appleFileDirectory = $this->dir->getPath('pub') . '/' . self::APPLE_DIRECTORY;
        $this->appleFilePath      = $this->appleFileDirectory . '/' . self::getAppleFileName();
    }

    /**
     * Save uploaded file before saving config value
     *
     * @return $this
     * @throws LocalizedException
     */
    public function beforeSave()
    {
        $value = $this->getValue();
        $file = $this->getFileData();

        if (!empty($file)) {
            $this->deleteAppleFiles();
            $file['name'] = self::getAppleFileName();

            // Root
            $this->appleDir = $this->getRootAppleDirectory();
            $resultRoot     = $this->fileUploader($file);

            // Pub
            $this->appleDir = $this->getAppleDirectory();
            
            if ($resultRoot !== false) {
                $this->copyAppleFileInRootToPub();
                $filename = self::APPLE_FILENAME;
                if ($this->_addWhetherScopeInfo()) {
                    $filename = $this->_prependScopeInfo($filename);
                }
                $this->setValue($filename);
            }
        } else {
            if (is_array($value) && !empty($value['delete'])) {
                $this->setValue('');
                $this->deleteAppleFiles();
            } elseif (is_array($value) && !empty($value['value'])) {
                $this->setValue($value['value']);
            } else {
                $this->unsValue();
                $this->deleteAppleFiles();
            }
        }

        return $this;
    }

    /**
     * Return path to directory for upload file
     *
     * @return string
     * @throws LocalizedException
     */
    protected function _getUploadDir()
    {
        $fieldConfig = $this->getFieldConfig();

        if (!array_key_exists('upload_dir', $fieldConfig)) {
            throw new LocalizedException(
                __('The base directory to upload file is not specified.')
            );
        }

        if (is_array($fieldConfig['upload_dir'])) {
            $uploadDir = $this->appleDir;
            if (array_key_exists('scope_info', $fieldConfig['upload_dir'])
                && $fieldConfig['upload_dir']['scope_info']
            ) {
                $uploadDir = $this->_appendScopeInfo($uploadDir);
            }

            if (array_key_exists('config', $fieldConfig['upload_dir'])) {
                $uploadDir = $this->getUploadDirPath($uploadDir);
            }
        } else {
            $uploadDir = $this->getUploadDirPath($this->appleDir);
        }

        return $uploadDir;
    }

     /**
     * Function to upload a file
     * 
     * @param array $file
     * @return bool
     */
    protected function fileUploader(array $file)
    {
        $uploadDir = $this->_getUploadDir();
        try {
            /** @var Uploader $uploader */
            $uploader = $this->_uploaderFactory->create(['fileId' => $file]);
            $uploader->setAllowedExtensions($this->_getAllowedExtensions());
            $uploader->setAllowRenameFiles(true);
            $uploader->addValidateCallback('size', $this, 'validateMaxSize');
            $result = $uploader->save($uploadDir);

            return $result;
        } catch (Exception $e) {
            throw new LocalizedException(__('%1', $e->getMessage()));
        }

        return false;
    }

    /**
     * Getter for allowed file name of uploaded files
     *
     * @return array
     */
    protected function _getAllowedExtensions()
    {
        return [self::APPLE_FILE_EXTENSION];
    }

    /**
     * Getter for Apple Root Directory. If directory don't exists will be created
     *
     * @param bool $fullPath
     * @return string
     */
    protected function getRootAppleDirectory($fullPath = false)
    {
        if (!file_exists($this->rootAppleFileDirectory)) {
            $this->file->mkdir($this->rootAppleFileDirectory);
        }
        return ($fullPath ? $this->rootAppleFileDirectory : self::APPLE_DIRECTORY);
    }

    /**
     * Getter for Apple Directory. If directory don't exists will be created
     *
     * @param bool $fullPath
     * @return string
     */
    protected function getAppleDirectory($fullPath = false)
    {
        if (!file_exists($this->appleFileDirectory)) {
            $this->file->mkdir($this->appleFileDirectory);
        }
        return ($fullPath ? $this->appleFileDirectory : self::APPLE_DIRECTORY);
    }

    /**
     * Return Apple File Name and Extension
     *
     * @return string
     */
    public static function getAppleFileName()
    {
        return AppleFileType::APPLE_FILENAME . '.' .
            AppleFileType::APPLE_FILE_EXTENSION;
    }

    /**
     * Return Apple File Endpoint
     *
     * @return string
     */
    public static function getAppleFileEndpoint()
    {
        return AppleFileType::APPLE_DIRECTORY . '/' .
            self::getAppleFileName();
    }

    /**
     * Return Root Apple File Path
     *
     * @return string
     */
    public function getRootAppleFilePath()
    {
        return $this->rootAppleFilePath;
    }

    /**
     * Return Apple File Path
     *
     * @return string
     */
    public function getAppleFilePath()
    {
        return $this->appleFilePath;
    }

    /**
     * Return if file exists in root
     *
     * @return bool
     */
    public function validateIfFileExistsInRoot()
    {
        return $this->file->fileExists($this->getRootAppleFilePath(), true);
    }

    /**
     * Return if file exists in pub
     *
     * @return bool
     */
    public function validateIfFileExists()
    {
        return $this->file->fileExists($this->getAppleFilePath(), true);
    }

    /**
     * Copy Apple File in Root to Pub
     */
    protected function copyAppleFileInRootToPub()
    {
        $this->file->cp($this->getRootAppleFilePath(), $this->getAppleFilePath());
    }

    /**
     * Copy Apple File in Root to Pub
     */
    protected function copyAppleFileInPubToRoot()
    {
        $this->file->cp($this->getAppleFilePath(), $this->getRootAppleFilePath());
    }

    /**
     * Delete Apple Files
     */
    protected function deleteAppleFiles()
    {
        $this->deleteRootAppleFile();
        $this->deleteAppleFile();
    }

    /**
     * Delete Apple Files
     */
    protected function deleteRootAppleFile()
    {
        if ($this->validateIfFileExistsInRoot()) {
            $this->file->rm($this->getRootAppleFilePath());
        }
    }

    /**
     * Delete Apple Files
     */
    protected function deleteAppleFile()
    {
        if ($this->validateIfFileExists()) {
            $this->file->rm($this->getAppleFilePath());
        }
    }
}
