<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/**
 * Service of copying customizations from one theme to another
 */
namespace Magento\Theme\Model;

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\View\Design\ThemeInterface;

class CopyService
{
    /**
     * @var \Magento\Framework\Filesystem\Directory\Write
     */
    protected $_directory;

    /**
     * @var \Magento\Framework\View\Design\Theme\FileFactory
     */
    protected $_fileFactory;

    /**
     * @var \Magento\Widget\Model\Layout\Link
     */
    protected $_link;

    /**
     * @var \Magento\Widget\Model\Layout\UpdateFactory
     */
    protected $_updateFactory;

    /**
     * @var \Magento\Framework\Event\ManagerInterface
     */
    protected $_eventManager;

    /**
     * @var \Magento\Framework\View\Design\Theme\Customization\Path
     */
    protected $_customizationPath;

    /**
     * @param \Magento\Framework\Filesystem $filesystem
     * @param \Magento\Framework\View\Design\Theme\FileFactory $fileFactory
     * @param \Magento\Widget\Model\Layout\Link $link
     * @param \Magento\Widget\Model\Layout\UpdateFactory $updateFactory
     * @param \Magento\Framework\Event\ManagerInterface $eventManager
     * @param \Magento\Framework\View\Design\Theme\Customization\Path $customization
     */
    public function __construct(
        \Magento\Framework\Filesystem $filesystem,
        \Magento\Framework\View\Design\Theme\FileFactory $fileFactory,
        \Magento\Widget\Model\Layout\Link $link,
        \Magento\Widget\Model\Layout\UpdateFactory $updateFactory,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Framework\View\Design\Theme\Customization\Path $customization
    ) {
        $this->_directory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
        $this->_fileFactory = $fileFactory;
        $this->_link = $link;
        $this->_updateFactory = $updateFactory;
        $this->_eventManager = $eventManager;
        $this->_customizationPath = $customization;
    }

    /**
     * Copy customizations from one theme to another
     *
     * @param ThemeInterface $source
     * @param ThemeInterface $target
     * @return void
     */
    public function copy(ThemeInterface $source, ThemeInterface $target)
    {
        $this->_copyDatabaseCustomization($source, $target);
        $this->_copyLayoutCustomization($source, $target);
        $this->_copyFilesystemCustomization($source, $target);
    }

    /**
     * Copy customizations stored in a database from one theme to another, overriding existing data
     *
     * @param ThemeInterface $source
     * @param ThemeInterface $target
     * @return void
     */
    protected function _copyDatabaseCustomization(ThemeInterface $source, ThemeInterface $target)
    {
        /** @var $themeFile \Magento\Theme\Model\Theme\File */
        foreach ($target->getCustomization()->getFiles() as $themeFile) {
            $themeFile->delete();
        }
        /** @var $newFile \Magento\Theme\Model\Theme\File */
        foreach ($source->getCustomization()->getFiles() as $themeFile) {
            /** @var $newThemeFile \Magento\Theme\Model\Theme\File */
            $newThemeFile = $this->_fileFactory->create();
            $newThemeFile->setData(
                [
                    'theme_id' => $target->getId(),
                    'file_path' => $themeFile->getFilePath(),
                    'file_type' => $themeFile->getFileType(),
                    'content' => $themeFile->getContent(),
                    'sort_order' => $themeFile->getData('sort_order'),
                ]
            );
            $newThemeFile->save();
        }
    }

    /**
     * Add layout links to general layout updates for themes
     *
     * @param ThemeInterface $source
     * @param ThemeInterface $target
     * @return void
     */
    protected function _copyLayoutCustomization(ThemeInterface $source, ThemeInterface $target)
    {
        $update = $this->_updateFactory->create();
        /** @var $targetUpdates \Magento\Widget\Model\ResourceModel\Layout\Update\Collection */
        $targetUpdates = $update->getCollection();
        $targetUpdates->addThemeFilter($target->getId());
        $targetUpdates->delete();

        /** @var $sourceCollection \Magento\Widget\Model\ResourceModel\Layout\Link\Collection */
        $sourceCollection = $this->_link->getCollection();
        $sourceCollection->addThemeFilter($source->getId());
        /** @var $layoutLink \Magento\Widget\Model\Layout\Link */
        foreach ($sourceCollection as $layoutLink) {
            /** @var $update \Magento\Widget\Model\Layout\Update */
            $update = $this->_updateFactory->create();
            $update->load($layoutLink->getLayoutUpdateId());
            if ($update->getId()) {
                $update->setId(null);
                $update->save();
                $layoutLink->setThemeId($target->getId());
                $layoutLink->setLayoutUpdateId($update->getId());
                $layoutLink->setId(null);
                $layoutLink->save();
            }
        }
    }

    /**
     * Copy customizations stored in a file system from one theme to another, overriding existing data
     *
     * @param ThemeInterface $source
     * @param ThemeInterface $target
     * @return void
     */
    protected function _copyFilesystemCustomization(ThemeInterface $source, ThemeInterface $target)
    {
        $sourcePath = $this->_customizationPath->getCustomizationPath($source);
        $targetPath = $this->_customizationPath->getCustomizationPath($target);

        if (!$sourcePath || !$targetPath) {
            return;
        }

        $this->_deleteFilesRecursively($targetPath);

        if ($this->_directory->isDirectory($sourcePath)) {
            $this->_copyFilesRecursively($sourcePath, $sourcePath, $targetPath);
        }
    }

    /**
     * Copies all files in a directory recursively
     *
     * @param string $baseDir
     * @param string $sourceDir
     * @param string $targetDir
     * @return void
     */
    protected function _copyFilesRecursively($baseDir, $sourceDir, $targetDir)
    {
        foreach ($this->_directory->read($sourceDir) as $path) {
            if ($this->_directory->isDirectory($path)) {
                $this->_copyFilesRecursively($baseDir, $path, $targetDir);
            } else {
                $filePath = substr($path, strlen($baseDir) + 1);
                $this->_directory->copyFile($path, $targetDir . '/' . $filePath);
            }
        }
    }

    /**
     * Delete all files in a directory recursively
     *
     * @param string $targetDir
     * @return void
     */
    protected function _deleteFilesRecursively($targetDir)
    {
        if ($this->_directory->isExist($targetDir)) {
            foreach ($this->_directory->read($targetDir) as $path) {
                $this->_directory->delete($path);
            }
        }
    }
}
