<?php

namespace Drupal\Console\Extension;

use Drupal\Console\Utils\Site;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ThemeHandler;
use Drupal\Core\Extension\ModuleHandlerInterface;

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

/**
 * Class ExtensionManager
 *
 * @package Drupal\Console
 */
class Manager
{

    /**
     * @var Site
     */
    protected $site;

    /**
     * @var Client
     */
    protected $httpClient;

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

    /**
     * @var array
     */
    private $extensions = [];

    /**
     * @var array
     */
    private $filters = [];

    /**
     * @var string
     */
    private $extension = null;

    /**
     * ExtensionManager constructor.
     *
     * @param  Site    $site
     * @param  Client  $httpClient
     * @param  string  $appRoot
     */
    public function __construct(
        Site $site,
        Client $httpClient,
        $appRoot
    ) {
        $this->site       = $site;
        $this->httpClient = $httpClient;
        $this->appRoot    = $appRoot;
        $this->initialize();
    }

    /**
     * @return $this
     */
    public function showInstalled()
    {
        $this->filters['showInstalled'] = true;

        return $this;
    }

    /**
     * @return $this
     */
    public function showUninstalled()
    {
        $this->filters['showUninstalled'] = true;

        return $this;
    }

    /**
     * @return $this
     */
    public function showCore()
    {
        $this->filters['showCore'] = true;

        return $this;
    }

    /**
     * @return $this
     */
    public function showNoCore()
    {
        $this->filters['showNoCore'] = true;

        return $this;
    }

    /**
     * @param  boolean  $nameOnly
     *
     * @return array
     */
    public function getList($nameOnly = false)
    {
        return $this->getExtensions($this->extension, $nameOnly);
    }

    /**
     * @return $this
     */
    public function discoverModules()
    {
        $this->initialize();
        $this->discoverExtension('module');

        return $this;
    }

    /**
     * @return $this
     */
    public function discoverThemes()
    {
        $this->initialize();
        $this->discoverExtension('theme');

        return $this;
    }

    /**
     * @return $this
     */
    public function discoverProfiles()
    {
        $this->initialize();
        $this->discoverExtension('profile');

        return $this;
    }

    /**
     * @param  string  $extension
     */
    private function discoverExtension($extension)
    {
        $this->extension              = $extension;
        $this->extensions[$extension] = $this->discoverExtensions($extension);

        return $this;
    }

    /**
     * initializeFilters
     */
    private function initialize()
    {
        $this->extension  = 'module';
        $this->extensions = [
            'module'  => [],
            'theme'   => [],
            'profile' => [],
        ];
        $this->filters    = [
            'showInstalled'   => false,
            'showUninstalled' => false,
            'showCore'        => false,
            'showNoCore'      => false,
        ];
    }

    /**
     * @param  string      $type
     * @param  bool|false  $nameOnly
     *
     * @return array
     */
    private function getExtensions(
        $type = 'module',
        $nameOnly = false
    ) {
        $showInstalled   = $this->filters['showInstalled'];
        $showUninstalled = $this->filters['showUninstalled'];
        $showCore        = $this->filters['showCore'];
        $showNoCore      = $this->filters['showNoCore'];

        $extensions = [];
        if (!array_key_exists($type, $this->extensions)) {
            return $extensions;
        }

        $module_handler = \Drupal::service('module_handler');
        foreach ($this->extensions[$type] as $extension) {
            $name = $extension->getName();

            $isInstalled = $type == 'module'
                && $module_handler->moduleExists($name);
            if (!$isInstalled && property_exists($extension, 'status')) {
                $isInstalled = ($extension->status) ? true : false;
            }
            if (!$showInstalled && $isInstalled) {
                continue;
            }
            if (!$showUninstalled && !$isInstalled) {
                continue;
            }
            if (!$showCore && $extension->origin == 'core') {
                continue;
            }
            if (!$showNoCore && $extension->origin != 'core') {
                continue;
            }

            $extensions[$name] = $extension;
        }


        return $nameOnly ? array_keys($extensions) : $extensions;
    }

    /**
     * @param  string  $type
     *
     * @return \Drupal\Core\Extension\Extension[]
     */
    private function discoverExtensions($type)
    {
        if ($type === 'module') {
            $this->site->loadLegacyFile('/core/modules/system/system.module');
            $extensionList = \Drupal::service('extension.list.module');
            $extensionList->reset()->getList();
        }

        if ($type === 'theme') {
            $theme_handler = \Drupal::service('theme_handler');
            $theme_handler->rebuildThemeData();
        }

        /*
         * @see Remove DrupalExtensionDiscovery subclass once
         * https://www.drupal.org/node/2503927 is fixed.
         */
        $discovery = new Discovery($this->appRoot);
        $discovery->reset();

        return $discovery->scan($type);
    }

    /**
     * @param  string  $name
     *
     * @return \Drupal\Console\Extension\Extension
     */
    public function getModule($name)
    {
        if ($extension = $this->getExtension('module', $name)) {
            return $this->createExtension($extension);
        }

        return null;
    }

    /**
     * @param  string  $name
     *
     * @return \Drupal\Console\Extension\Extension
     */
    public function getProfile($name)
    {
        if ($extension = $this->getExtension('profile', $name)) {
            return $this->createExtension($extension);
        }

        return null;
    }

    /**
     * @param  string  $name
     *
     * @return \Drupal\Console\Extension\Extension
     */
    public function getTheme($name)
    {
        if ($extension = $this->getExtension('theme', $name)) {
            return $this->createExtension($extension);
        }

        return null;
    }

    /**
     * @param  string  $type
     * @param  string  $name
     *
     * @return \Drupal\Core\Extension\Extension
     */
    private function getExtension($type, $name)
    {
        if (!$this->extensions[$type]) {
            $this->discoverExtension($type);
        }

        if (array_key_exists($name, $this->extensions[$type])) {
            return $this->extensions[$type][$name];
        }

        return null;
    }

    /**
     * @param  \Drupal\Core\Extension\Extension  $extension
     *
     * @return \Drupal\Console\Extension\Extension
     */
    private function createExtension($extension)
    {
        $consoleExtension = new Extension(
            $this->appRoot,
            $extension->getType(),
            $extension->getPathname(),
            $extension->getExtensionFilename()
        );

        $consoleExtension->loadFromParentObj($extension);
        $consoleExtension->setRoot($this->appRoot);

        return $consoleExtension;
    }

    /**
     * @param  string  $testType
     * @param          $fullPath
     *
     * @return string
     */
    public function getTestPath($testType, $fullPath = false)
    {
        return $this->getPath($fullPath).'/Tests/'.$testType;
    }

    public function validateModuleFunctionExist(
        $moduleName,
        $function,
        $moduleFile = null
    ) {
        //Load module file to prevent issue of missing functions used in update
        $module     = $this->getModule($moduleName);
        $modulePath = $module->getPath();
        if ($moduleFile) {
            $this->site->loadLegacyFile($modulePath.'/'.$moduleFile);
        } else {
            $this->site->loadLegacyFile(
                $modulePath.'/'.$module->getName().'.module'
            );
        }

        if (function_exists($function)) {
            return true;
        }

        return false;
    }

    /**
     * @param  string  $moduleName
     * @param  string  $pluginType
     *
     * @return string
     */
    public function getPluginPath($moduleName, $pluginType)
    {
        $module = $this->getModule($moduleName);

        return $module->getPath().'/src/Plugin/'.$pluginType;
    }

    public function getDrupalExtension($type, $name)
    {
        $extension = $this->getExtension($type, $name);

        return $this->createExtension($extension);
    }

    /**
     * @param  array   $extensions
     * @param  string  $type
     *
     * @return array
     */
    public function checkExtensions(array $extensions, $type = 'module')
    {
        $checkextensions = [
            'local_extensions'  => [],
            'drupal_extensions' => [],
            'no_extensions'     => [],
        ];

        $local_extensions = $this->discoverExtension($type)
            ->showInstalled()
            ->showUninstalled()
            ->showCore()
            ->showNoCore()
            ->getList(true);

        foreach ($extensions as $extension) {
            if (in_array($extension, $local_extensions)) {
                $checkextensions['local_extensions'][] = $extension;
            } else {
                try {
                    $response    = $this->httpClient->head(
                        'https://www.drupal.org/project/'.$extension
                    );
                    $header_link = $response->getHeader('link');
                    if (empty($header_link[0])) {
                        $checkextensions['no_extensions'][] = $extension;
                    } else {
                        $checkextensions['drupal_extensions'][] = $extension;
                    }
                } catch (ClientException $e) {
                    $checkextensions['no_extensions'][] = $extension;
                }
            }
        }

        return $checkextensions;
    }

}
