<?php

declare(strict_types=1);

/**
 * @author Amasty Team
 * @copyright Copyright (c) Amasty (https://www.amasty.com)
 * @package Jet Theme Backend for Magento 2 (System)
 */

namespace Amasty\JetTheme\Model;

use Amasty\JetTheme\Model\TransferConfigProcessor\TransferConfigInterface;
use Magento\Framework\App\Filesystem\DirectoryList as AppDirectoryList;
use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Filesystem\DirectoryList;
use Magento\Framework\Filesystem\Driver\File as FileDriver;
use Magento\Framework\View\Design\ThemeInterface;
use Magento\Store\Model\App\Emulation;
use Psr\Log\LoggerInterface;

class AssetManager
{
    private const ASSET_DIR = 'assets';
    private const ASSET_EXT = '.less';

    /**
     * @var ConfigProvider
     */
    private $configProvider;

    /**
     * @var FileDriver
     */
    private $fileDriver;

    /**
     * @var DirectoryList
     */
    private $directoryList;

    /**
     * @var StoreThemeMapper
     */
    private $storeThemeMapper;

    /**
     * @var Emulation
     */
    private $emulation;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var TransferConfigInterface[]
     */
    private $transferConfigProcessors;

    /**
     * @var bool
     */
    private $hasGenerated = false;

    public function __construct(
        ConfigProvider $configProvider,
        FileDriver $fileDriver,
        DirectoryList $directoryList,
        StoreThemeMapper $storeThemeMapper,
        Emulation $emulation,
        LoggerInterface $logger,
        array $transferConfigProcessors
    ) {
        $this->configProvider = $configProvider;
        $this->fileDriver = $fileDriver;
        $this->directoryList = $directoryList;
        $this->storeThemeMapper = $storeThemeMapper;
        $this->emulation = $emulation;
        $this->logger = $logger;
        $this->transferConfigProcessors = $transferConfigProcessors;
    }

    /**
     * @return void
     */
    public function generate(): void
    {
        try {
            if ($this->hasGenerated) {
                return;
            }

            $assetPath = $this->getAssetsPath('');
            if ($this->fileDriver->isExists($assetPath)) {
                $this->fileDriver->deleteDirectory($assetPath);
            }

            /** @var ThemeInterface $theme */
            foreach ($this->getThemes() as $theme) {
                $storeId = $this->storeThemeMapper->getStoreIdByThemeFilePath($theme->getThemePath());
                if ($storeId === null) {
                    continue;
                }

                foreach ($this->transferConfigProcessors as $name => $transferConfigProcessor) {
                    if ($transferConfigProcessor->isValidToProcess($storeId)) {
                        $contents = $this->getFileContent($transferConfigProcessor, $storeId);
                        $path = $this->getAssetsPath($theme->getThemePath());
                        if (!$this->fileDriver->isExists($path)) {
                            $this->fileDriver->createDirectory($path);
                        }

                        $this->fileDriver->filePutContents(
                            $path . DIRECTORY_SEPARATOR . $name . self::ASSET_EXT,
                            $contents
                        );
                    }
                }
            }

            $this->hasGenerated = true;
        } catch (\Exception $e) {
            $this->logger->error($e->getMessage());
        }
    }

    /**
     * @param string $relativePath
     * @param string $transferConfigProcessorName
     * @param string $contents
     * @return string
     */
    public function getContent(string $relativePath, string $transferConfigProcessorName, string $contents): string
    {
        try {
            $themePath = $this->getThemeNameByPath($relativePath);
            $path = $this->getAssetsPath($themePath);
            $fullPath = $path . DIRECTORY_SEPARATOR . $transferConfigProcessorName . self::ASSET_EXT;
            if ($this->fileDriver->isExists($fullPath)) {
                return $this->fileDriver->fileGetContents($fullPath);
            }
        } catch (\Exception $e) {
            $this->logger->error($e->getMessage());
        }

        return $contents;
    }

    /**
     * @return array
     */
    private function getThemes(): array
    {
        $themeCollection = $this->storeThemeMapper->getThemeCollection();
        $themes = [];

        foreach ($themeCollection as $theme) {
            if (strpos($theme->getThemePath(), "JetTheme") !== false) {
                $themes[$theme->getId()] = $theme;
            }

            if ($theme->getParentId()) {
                if ($this->checkIsParentJetTheme($theme, $themeCollection->getItems())) {
                    $themes[$theme->getId()] = $theme;
                }
            }
        }

        return $themes;
    }

    /**
     * @param ThemeInterface $theme
     * @param array $themes
     * @return bool
     */
    private function checkIsParentJetTheme(ThemeInterface $theme, array $themes): bool
    {
        if (strpos($theme->getThemePath(), "JetTheme") !== false) {
            return true;
        }

        if ($theme->getParentId()) {
            return $this->checkIsParentJetTheme($themes[$theme->getParentId()], $themes);
        }

        return false;
    }

    /**
     * @param TransferConfigInterface $transferConfigProcessor
     * @param int|null $storeId
     * @return string
     */
    private function getFileContent(TransferConfigInterface $transferConfigProcessor, ?int $storeId): string
    {
        $this->configProvider->clean();
        $this->emulation->startEnvironmentEmulation($storeId);
        $content = $transferConfigProcessor->process();
        $this->emulation->stopEnvironmentEmulation();

        return $content;
    }

    /**
     * @param string $themePath
     * @return string
     * @throws FileSystemException
     */
    private function getAssetsPath(string $themePath): string
    {
        return $this->directoryList->getPath(AppDirectoryList::VAR_DIR) .
            DIRECTORY_SEPARATOR . self::ASSET_DIR . DIRECTORY_SEPARATOR . $themePath;
    }

    /**
     * @param string $path
     * @return string
     */
    private function getThemeNameByPath(string $path): string
    {
        [, $vendor, $themeName] = explode(DIRECTORY_SEPARATOR, $path);

        return $vendor . DIRECTORY_SEPARATOR . $themeName;
    }
}
