<?php


namespace Olegnax\Core\Helper;


use Exception;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Component\ComponentRegistrar;
use Magento\Framework\Component\ComponentRegistrarInterface;
use Magento\Framework\Filesystem\Directory\ReadFactory;
use Magento\Framework\HTTP\Adapter\CurlFactory;
use Magento\Framework\HTTP\AsyncClient\Request;
use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress;
use Magento\Framework\Module\ModuleListInterface;
use Psr\Log\LoggerInterface;

class ModuleInfo extends Helper
{
    const MODULE_URL = 'https://olegnax.com/extras/products-status/magento2_modules.json';
    const CACHE_PERIOD = 24 * 60 * 60;
    const XML_PATH_CACHE = 'olegnax_core/module/cache';
    const XML_PATH_CACHE_DATE = 'olegnax_core/module/datacache';
    /**
     * @var ProductMetadataInterface
     */
    protected $productMetadata;
    /**
     * @var CurlFactory
     */
    protected $curlFactory;
    /**
     * @var RemoteAddress
     */
    protected $remoteAddress;
    /**
     * @var RequestInterface
     */
    protected $request;
    /**
     * @var array
     */
    protected $data;
    /**
     * @var string
     */
    protected $_moduleUrl;
    /**
     * @var ComponentRegistrarInterface
     */
    protected $componentRegistrar;
    /**
     * @var ModuleListInterface
     */
    protected $moduleList;
    /**
     * @var ReadFactory
     */
    protected $readFactory;

    public function __construct(
        Context $context,
        LoggerInterface $logger,
        CurlFactory $curlFactory,
        ProductMetadataInterface $productMetadata,
        RemoteAddress $remoteAddress,
        ComponentRegistrarInterface $componentRegistrar,
        ModuleListInterface $moduleList,
        ReadFactory $readFactory,
        RequestInterface $request
    ) {
        $this->curlFactory = $curlFactory;
        $this->moduleList = $moduleList;
        $this->productMetadata = $productMetadata;
        $this->remoteAddress = $remoteAddress;
        $this->componentRegistrar = $componentRegistrar;
        $this->readFactory = $readFactory;
        $this->request = $request;
        parent::__construct($context, $logger);
    }

    public function getServer(
        $name
    ) {
        return $this->request->getServer($name);
    }

    public function getModuleInfo($moduleName, $onlyInstalled = true)
    {
        $data = $this->getModule($moduleName);
        if ('' == $data['setup_version'] && $onlyInstalled) {
            return [];
        }
        $apiData = $this->getData();
        if (array_key_exists($moduleName, $apiData)) {
            $data = array_replace($data, $apiData[$moduleName]);
        }
        if (!isset($data['theme_module'])) {
            $data['theme_module'] = false;
        }
        $data['server_version'] = isset($data['version']) ? $data['version'] : $data['setup_version'];
        $data['update_status'] = version_compare(
            $data['server_version'],
            $data['setup_version'],
            '>'
        );

        return $data;
    }

    /**
     * @param string $moduleName
     * @return bool
     */
    public function isActive($moduleName){
        return $this->_moduleManager->isEnabled($moduleName);
    }

    protected function getModule($name)
    {
        $module = $this->moduleList->getOne($name);
        if (empty($module)) {
            $module = [
                'name' => $name,
                'setup_version' => $this->getComposerVersion($name),
            ];
        }
        if (empty($module['setup_version'])) {
            $module['setup_version'] = $this->getComposerVersion($name);
        }
        $module['title'] = str_replace('_', ' ', $module['name']);
        return (array)$module;
    }

    protected function getComposerVersion($moduleName, $type = ComponentRegistrar::MODULE)
    {
        $path = $this->componentRegistrar->getPath($type, $moduleName);

        if ($path) {
            $dirReader = $this->readFactory->create($path);

            if ($dirReader->isExist('composer.json')) {
                $data = $dirReader->readFile('composer.json');
                $data = json_decode($data, true);
                if (isset($data['version'])) {
                    return $data['version'];
                }
            }
            if ($dirReader->isExist('etc/module.xml')) {
                $data = $dirReader->readFile('etc/module.xml');
                if (preg_match('/setup_version="([^\"]+)"/i', $data, $matches)) {
                    return $matches[1];
                }
            }
        }

        return '';
    }

    public function getData($forsed = false)
    {
        $date = (int)$this->getSystemDefaultValue(static::XML_PATH_CACHE_DATE);
        try {
            if (empty($this->data)) {
                $this->data = $this->decodeData($this->getSystemDefaultValue(static::XML_PATH_CACHE));
            }
            if ($date + static::CACHE_PERIOD < time() || $forsed || empty($this->data)) {
                $this->data = $this->decodeData($this->loadData());
                if (!empty($this->data)) {
                    $this->setSystemValue(
                        static::XML_PATH_CACHE,
                        json_encode($this->data),
                        ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
                        0
                    );
                    $this->setSystemValue(
                        static::XML_PATH_CACHE_DATE,
                        time(),
                        ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
                        0
                    );
                }
            }
        } catch (Exception $e) {
            $this->data = [];
        }

        return $this->data;
    }

    private function decodeData($data = '')
    {
        if (empty($data)) {
            return [];
        }
        return (array)json_decode($data, true);
    }

    protected function loadData()
    {
        $curl = $this->curlFactory->create();
        $curl->setConfig(
            [
                'timeout' => 2,
                'maxredirects' => 5,
                'useragent' => sprintf(
                    'Olegnax: %s/%s (%s)',
                    $this->productMetadata->getName(),
                    $this->productMetadata->getVersion(),
                    $this->productMetadata->getEdition()
                ),
                'referer' => $this->getSystemDefaultValue('web/unsecure/base_url'),
                'verifypeer' => 0,
                'verifyhost' => 0,
            ]
        );
        $curl->write(Request::METHOD_GET, $this->getModuleUrl());
        $data = $curl->read();
        $curl->close();
        if(!empty($data)){
            $data = preg_split('/^\r?$/m', $data, 2);
            if(!empty($data) && isset($data[1])){
                $data = trim($data[1]);
                return $data;
            }
        }
        return '[]';
    }

    public function getModuleUrl()
    {
        if ($this->_moduleUrl === null) {
            $this->_moduleUrl = self::MODULE_URL . $this->getHash();
        }
        return $this->_moduleUrl;
    }

    public function getHash()
    {
        $args = ['hash' => base64_encode($this->getCurrentIp())];
        $args = array_filter($args);
        $query = '';
        if (!empty($args)) {
            $query = '?' . http_build_query($args);
        }

        return $query;
    }

    public function getCurrentIp()
    {
        $variable = [
            'HTTP_X_REAL_IP',
            'HTTP_CLIENT_IP',
            'HTTP_X_FORWARDED_FOR'
        ];
        $value = array_map([$this, 'getServer'], $variable);
        $value = array_filter($value);
        $value = array_map('trim', $value);
        $value[] = $this->getRemoteAddress();
        $value = array_filter($value);
        $value = implode(',', $value);

        return $value;
    }

    public function getRemoteAddress()
    {
        return $this->remoteAddress->getRemoteAddress();
    }

    public function getThemeInfo($themePath, $themeName = null, $onlyInstalled = true)
    {
        $data = $this->getTheme($themePath);
        if ('' == $data['setup_version'] && $onlyInstalled) {
            return [];
        }
        if (empty($themeName) && isset($data['name']) && !empty($data['name'])) {
            $themeName = $data['name'];
        }
        $apiData = $this->getData();
        if (!empty($themeName) && array_key_exists($themeName, $apiData)) {
            $data = array_replace($data, $apiData[$themeName]);
        }
        $data['server_version'] = isset($data['version']) ? $data['version'] : $data['setup_version'];
        $data['update_status'] = version_compare(
            $data['server_version'],
            $data['setup_version'],
            '>'
        );

        return $data;
    }

    private function getTheme($themePath)
    {
        $theme = [
            'path' => $themePath,
            'setup_version' => $this->getComposerVersion($themePath, ComponentRegistrar::THEME),
        ];
	    $theme['established'] = !empty($theme['setup_version']);
        $name = $this->getThemeName($themePath);
        if (!empty($name)) {
            $theme['name'] = $name;
        }

        return (array)$theme;
    }

    protected function getThemeName($path)
    {
        $path = $this->componentRegistrar->getPath(ComponentRegistrar::THEME, $path);
        if ($path) {
            $dirReader = $this->readFactory->create($path);
            if ($dirReader->isExist('theme.xml')) {
                $data = $dirReader->readFile('theme.xml');
                if (preg_match('/<title>([^<>]+)<\/title>/i', $data, $matches)) {
                    return $matches[1];
                }
            }
        }

        return null;
    }


}